@ganeshdole/brainbox-widget 0.0.0-stage → 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/LICENSE +28 -0
- package/README.md +87 -3
- package/dist/App.d.ts +10 -0
- package/dist/components/BarVisualizer.d.ts +7 -0
- package/dist/components/Chooser.d.ts +8 -0
- package/dist/components/Composer.d.ts +13 -0
- package/dist/components/Launcher.d.ts +5 -0
- package/dist/components/MarkShape.d.ts +20 -0
- package/dist/components/MarkupOverlay.d.ts +17 -0
- package/dist/components/MarkupToolbar.d.ts +13 -0
- package/dist/components/RecordOverlay.d.ts +8 -0
- package/dist/components/Result.d.ts +8 -0
- package/dist/components/VoiceNote.d.ts +6 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +1315 -0
- package/dist/lib/config.d.ts +14 -0
- package/dist/lib/multiband.d.ts +31 -0
- package/dist/lib/position.d.ts +3 -0
- package/dist/lib/scroll-lock.d.ts +19 -0
- package/dist/lib/shadow-css.d.ts +27 -0
- package/dist/lib/shadow-host.d.ts +12 -0
- package/dist/lib/time.d.ts +2 -0
- package/dist/lib/use-drawing.d.ts +25 -0
- package/dist/lib/use-multiband.d.ts +20 -0
- package/dist/lib/widget-store.d.ts +15 -0
- package/dist/mount.d.ts +18 -0
- package/dist/use-brainbox-widget.d.ts +6 -0
- package/dist/widget-capture.js +141 -0
- package/dist/widget.d.ts +16 -0
- package/dist/widget.js +11 -0
- package/package.json +71 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
O'Saasy License
|
|
2
|
+
|
|
3
|
+
Copyright © 2026, Ganesh Dole.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
6
|
+
a copy of this software and associated documentation files (the
|
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
11
|
+
the following conditions:
|
|
12
|
+
|
|
13
|
+
1. The above copyright notice and this permission notice shall be
|
|
14
|
+
included in all copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
2. No licensee or downstream recipient may use the Software (including
|
|
17
|
+
any modified or derivative versions) to directly compete with the
|
|
18
|
+
original Licensor by offering it to third parties as a hosted, managed,
|
|
19
|
+
or Software-as-a-Service (SaaS) product or cloud service where the
|
|
20
|
+
primary value of the service is the functionality of the Software itself.
|
|
21
|
+
|
|
22
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
23
|
+
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
24
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
25
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
|
26
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
|
27
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
|
28
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,87 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
# @ganeshdole/brainbox-widget
|
|
2
|
+
|
|
3
|
+
Brainbox's own capture UI: the floating button, the markup step, the
|
|
4
|
+
recorder, the composer. Built on `@ganeshdole/brainbox-core` and `@ganeshdole/brainbox-react`.
|
|
5
|
+
Three ways in, one UI.
|
|
6
|
+
|
|
7
|
+
## Script tag
|
|
8
|
+
|
|
9
|
+
For a page with no build step. Paste before `</body>`:
|
|
10
|
+
|
|
11
|
+
```html
|
|
12
|
+
<script src="https://app.brainbox.sh/widget.js"
|
|
13
|
+
data-project="pk_..."
|
|
14
|
+
data-endpoint="https://app.brainbox.sh/ingest"></script>
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
| Attribute | Values | Default |
|
|
18
|
+
|---|---|---|
|
|
19
|
+
| `data-project` | your project key | required |
|
|
20
|
+
| `data-endpoint` | where reports are POSTed | required |
|
|
21
|
+
| `data-mode` | `float` (our button) or `mount` (your own trigger) | `float` |
|
|
22
|
+
| `data-mount` | CSS selector of your trigger, with `data-mode="mount"` | |
|
|
23
|
+
| `data-position` | `bottom-right`, `bottom-left`, `top-right`, `top-left` | `bottom-right` |
|
|
24
|
+
|
|
25
|
+
The tag publishes `window.Brainbox` with `open()`, `close()` and
|
|
26
|
+
`identify({ id, email })`, and fires a `brainbox:submitted` event on `window`
|
|
27
|
+
(`event.detail.id`) after each report.
|
|
28
|
+
|
|
29
|
+
## In a React app
|
|
30
|
+
|
|
31
|
+
```sh
|
|
32
|
+
npm install @ganeshdole/brainbox-widget @ganeshdole/brainbox-react @ganeshdole/brainbox-core
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
```tsx
|
|
36
|
+
import { BrainboxProvider } from "@ganeshdole/brainbox-react";
|
|
37
|
+
import { BrainboxWidget, useBrainboxWidget } from "@ganeshdole/brainbox-widget";
|
|
38
|
+
|
|
39
|
+
<BrainboxProvider transport={transport} identity={user}>
|
|
40
|
+
<App />
|
|
41
|
+
<BrainboxWidget /> {/* trigger="floating" | "manual", position */}
|
|
42
|
+
</BrainboxProvider>
|
|
43
|
+
|
|
44
|
+
function ReportProblemButton() {
|
|
45
|
+
const { open } = useBrainboxWidget(); // anywhere under the provider
|
|
46
|
+
return <button onClick={open}>Report a problem</button>;
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
The widget renders into a shadow root appended to `<body>`, wherever it sits
|
|
51
|
+
in your tree, so your styles cannot reach it and no ancestor's `overflow` or
|
|
52
|
+
`transform` can trap its panels. Your React is used; the package does not
|
|
53
|
+
bundle one.
|
|
54
|
+
|
|
55
|
+
## Without React, from a bundler
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
import { mount } from "@ganeshdole/brainbox-widget";
|
|
59
|
+
import { brainboxTransport } from "@ganeshdole/brainbox-core";
|
|
60
|
+
|
|
61
|
+
const widget = mount({ transport: brainboxTransport({ endpoint, projectKey }), trigger: "manual" });
|
|
62
|
+
widget.open();
|
|
63
|
+
widget.identify({ id, email });
|
|
64
|
+
widget.destroy();
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Where things live
|
|
68
|
+
|
|
69
|
+
| Concern | Package |
|
|
70
|
+
|---|---|
|
|
71
|
+
| What gets captured and sent | `@ganeshdole/brainbox-core` |
|
|
72
|
+
| Holding an instance in React | `@ganeshdole/brainbox-react` |
|
|
73
|
+
| What the user sees | this package |
|
|
74
|
+
|
|
75
|
+
Reports go wherever the `Transport` sends them. `brainboxTransport()` is
|
|
76
|
+
Brainbox Cloud; see `@ganeshdole/brainbox-core` for pointing one at your own backend.
|
|
77
|
+
|
|
78
|
+
## Build
|
|
79
|
+
|
|
80
|
+
`pnpm build` emits `dist/widget.js` (the script-tag IIFE, everything bundled
|
|
81
|
+
except the capture libraries), `dist/widget-capture.js` (rrweb and
|
|
82
|
+
modern-screenshot, which `widget.js` loads from beside itself the first time the
|
|
83
|
+
widget opens - serve the two files from the same folder), `dist/index.js` (an ES module of the library entry with React, `lucide-react`
|
|
84
|
+
and the other `@ganeshdole/brainbox-*` packages external) and the `.d.ts` files. Inside
|
|
85
|
+
the monorepo the package is consumed from source; the published package's
|
|
86
|
+
`exports` point at `dist/` (`publishConfig`). `react` and `react-dom` are peer
|
|
87
|
+
dependencies of the library; the IIFE still bundles them.
|
package/dist/App.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type Brainbox } from "@ganeshdole/brainbox-core";
|
|
2
|
+
import type { TriggerMode } from "@ganeshdole/brainbox-shared";
|
|
3
|
+
import type { Position } from "./lib/position.ts";
|
|
4
|
+
export declare function App({ brainbox, hostEl, trigger, position, }: {
|
|
5
|
+
brainbox: Brainbox;
|
|
6
|
+
/** The shadow host: left out of screenshots. */
|
|
7
|
+
hostEl: HTMLElement;
|
|
8
|
+
trigger: TriggerMode;
|
|
9
|
+
position: Position;
|
|
10
|
+
}): import("react").JSX.Element;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/** A row of level bars, themed with brainbox tokens rather than the shadcn ones
|
|
2
|
+
* ElevenLabs UI's `bar-visualizer` ships with. `dimFrom` greys everything at or
|
|
3
|
+
* past that index, which is how a recorded clip doubles as a playback scrubber. */
|
|
4
|
+
export declare const BarRow: import("react").MemoExoticComponent<({ levels, dimFrom, }: {
|
|
5
|
+
levels: readonly number[];
|
|
6
|
+
dimFrom?: number;
|
|
7
|
+
}) => import("react").JSX.Element>;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type Position } from "../lib/position.ts";
|
|
2
|
+
export declare function Chooser({ position, canRecord, onScreenshot, onRecord, onCancel, }: {
|
|
3
|
+
position: Position;
|
|
4
|
+
canRecord: boolean;
|
|
5
|
+
onScreenshot: () => void;
|
|
6
|
+
onRecord: () => void;
|
|
7
|
+
onCancel: () => void;
|
|
8
|
+
}): import("react").JSX.Element;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type Position } from "../lib/position.ts";
|
|
2
|
+
export declare function Composer({ screenshotUrl, videoUrl, sessionReady, voiceCaptured, capturePending, captureFailed, position, onCancel, onSubmit, }: {
|
|
3
|
+
screenshotUrl?: string;
|
|
4
|
+
videoUrl?: string;
|
|
5
|
+
sessionReady?: boolean;
|
|
6
|
+
voiceCaptured?: boolean;
|
|
7
|
+
/** Screenshot still rasterising - the composer opens without waiting for it. */
|
|
8
|
+
capturePending?: boolean;
|
|
9
|
+
captureFailed?: boolean;
|
|
10
|
+
position: Position;
|
|
11
|
+
onCancel: () => void;
|
|
12
|
+
onSubmit: (text: string, audio: Blob | null) => void;
|
|
13
|
+
}): import("react").JSX.Element;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { type Mark } from "@ganeshdole/brainbox-core";
|
|
2
|
+
/** One mark as SVG. Shared by the markup step and the in-recording overlay so
|
|
3
|
+
* a box drawn in one looks identical in the other - and matches the bake,
|
|
4
|
+
* which draws the same geometry onto a canvas. */
|
|
5
|
+
export declare const MarkShape: import("react").MemoExoticComponent<({ mark }: {
|
|
6
|
+
mark: Mark;
|
|
7
|
+
}) => import("react").JSX.Element>;
|
|
8
|
+
/** The floating input a text mark is typed into, positioned so the text sits
|
|
9
|
+
* where the baked glyphs will land. */
|
|
10
|
+
export declare function MarkTextInput({ at, color, onChange, onCommit, onCancel, }: {
|
|
11
|
+
at: {
|
|
12
|
+
x: number;
|
|
13
|
+
y: number;
|
|
14
|
+
value: string;
|
|
15
|
+
};
|
|
16
|
+
color: string;
|
|
17
|
+
onChange: (value: string) => void;
|
|
18
|
+
onCommit: () => void;
|
|
19
|
+
onCancel: () => void;
|
|
20
|
+
}): import("react").JSX.Element;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type Mark } from "@ganeshdole/brainbox-core";
|
|
2
|
+
/**
|
|
3
|
+
* The markup step: the page is frozen and the end-user draws on it - boxes,
|
|
4
|
+
* arrows, freehand, text - before saying what's wrong.
|
|
5
|
+
*
|
|
6
|
+
* The screenshot is already rasterising when this mounts, so the marks are held
|
|
7
|
+
* as vectors here and baked onto the shot on the way out. Drawing never waits
|
|
8
|
+
* on the capture, and everything stays undoable right up to Done.
|
|
9
|
+
*/
|
|
10
|
+
export declare function MarkupOverlay({ frozenUrl, pending, failed, onDone, onCancel, }: {
|
|
11
|
+
/** The frozen page, once rasterised. Until then the live page shows through. */
|
|
12
|
+
frozenUrl?: string;
|
|
13
|
+
pending: boolean;
|
|
14
|
+
failed: boolean;
|
|
15
|
+
onDone: (marks: Mark[]) => void;
|
|
16
|
+
onCancel: () => void;
|
|
17
|
+
}): import("react").JSX.Element;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type Tool } from "@ganeshdole/brainbox-core";
|
|
2
|
+
export declare function MarkupToolbar({ tool, color, canUndo, canDelete, onTool, onColor, onUndo, onDelete, onCancel, onDone, }: {
|
|
3
|
+
tool: Tool;
|
|
4
|
+
color: string;
|
|
5
|
+
canUndo: boolean;
|
|
6
|
+
canDelete: boolean;
|
|
7
|
+
onTool: (t: Tool) => void;
|
|
8
|
+
onColor: (c: string) => void;
|
|
9
|
+
onUndo: () => void;
|
|
10
|
+
onDelete: () => void;
|
|
11
|
+
onCancel: () => void;
|
|
12
|
+
onDone: () => void;
|
|
13
|
+
}): import("react").JSX.Element;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type Annotations } from "@ganeshdole/brainbox-core";
|
|
2
|
+
export declare function RecordOverlay({ annotations, onStop, micActive, onMuteChange, }: {
|
|
3
|
+
/** The recording's marks on the host page - drawn here, cleared by core. */
|
|
4
|
+
annotations: Annotations;
|
|
5
|
+
onStop: () => void;
|
|
6
|
+
micActive: () => boolean;
|
|
7
|
+
onMuteChange: (muted: boolean) => void;
|
|
8
|
+
}): import("react").JSX.Element;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type Position } from "../lib/position.ts";
|
|
2
|
+
export declare function Result({ kind, id, message, position, onClose, }: {
|
|
3
|
+
kind: "loading" | "success" | "error";
|
|
4
|
+
id?: string;
|
|
5
|
+
message?: string;
|
|
6
|
+
position: Position;
|
|
7
|
+
onClose?: () => void;
|
|
8
|
+
}): import("react").JSX.Element;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/** Voice capture as the primary way to file a report: one big mic target, bars
|
|
2
|
+
* that track the speaker's voice per frequency band, and playback before
|
|
3
|
+
* sending - people won't submit audio they can't hear back. */
|
|
4
|
+
export declare function VoiceNote({ onChange }: {
|
|
5
|
+
onChange: (b: Blob | null) => void;
|
|
6
|
+
}): import("react").JSX.Element;
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { BrainboxWidget, type BrainboxWidgetProps } from "./widget.tsx";
|
|
2
|
+
export { useBrainboxWidget } from "./use-brainbox-widget.ts";
|
|
3
|
+
export { mount, type MountOptions, type WidgetHandle } from "./mount.tsx";
|
|
4
|
+
export type { Position } from "./lib/position.ts";
|
|
5
|
+
export type { TriggerMode } from "@ganeshdole/brainbox-shared";
|