Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ jobs:
ci:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6

- uses: actions/setup-node@v4
- uses: actions/setup-node@v6
with:
node-version: 20
node-version: 22
cache: npm

- run: npm ci
Expand Down
34 changes: 31 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -236,8 +236,14 @@ Supported attributes:
| `theme-selected` | Background color of the selected thumb. |
| `theme-selected-color` | Icon color of the selected thumb. |
| `inject-styles` | Set to `"false"` to skip automatic style injection. |

> **Note:** `colorScheme`, `showLabel`, `modalTitle`, `modalPlaceholder`, `showTitleField`, `showEmailField`, and `source` are not available as web component attributes. Use `InputBufferIO.createBar(config)` for those options.
| `color-scheme` | `"light"`, `"dark"`, or `"auto"`. |
| `show-label` | `"true"` to show the label, `"false"` to hide it. |
| `modal-title` | Title shown above the feedback textarea. |
| `modal-placeholder` | Placeholder text for the feedback textarea. |
| `show-title-field` | `"true"` to show an optional title input. |
| `show-email-field` | `"true"` to show an optional email input. |
| `source` | Identifier for the feedback source. |
| `user-id` | Stable user identifier for reaction deduplication. When set, only one reaction per user is recorded per target (all-time). When omitted, deduplication falls back to IP address with a 24-hour window. |

### `InputBufferIO.createBar(config)`

Expand Down Expand Up @@ -275,6 +281,7 @@ document.getElementById('my-slot').appendChild(bar.element);
| `showEmailField` | boolean | `false` | Show/hide the email field in the follow-up popover. |
| `showTitleField` | boolean | `false` | Show/hide the title field in the follow-up popover. |
| `source` | string | — | Tag identifying which of your surfaces this widget is embedded on (e.g. `"ios-app"`, `"docs-site"`). Stored on every submission for filtering in the dashboard. |
| `userId` | string | — | Stable user identifier for reaction deduplication. When set, only one reaction per user is recorded per target (all-time). When omitted, deduplication falls back to IP address with a 24-hour window. |
| `injectStyles` | boolean | `true` | Set to `false` to skip automatic style injection. |

### `bar.on(event, handler)`
Expand All @@ -291,7 +298,7 @@ bar.on('error', (err) => console.error('Submission failed:', err));

| Event | Handler signature | When it fires |
|---|---|---|
| `vote` | `({ sentiment: 'positive' \| 'negative' }) => void` | User clicks a thumb before submitting. |
| `vote` | `({ sentiment: 'positive' \| 'negative' }) => void` | User clicks a thumb. The reaction is recorded immediately via the reactions API (if a `target` is configured), and the selection is persisted in `localStorage` for 24 hours so it survives page reloads. |
| `open` | `({ sentiment: 'positive' \| 'negative' }) => void` | The follow-up popover opens. |
| `submit` | `({ id: string }) => void` | Feedback was submitted successfully. |
| `close` | `() => void` | The follow-up popover closes. |
Expand Down Expand Up @@ -631,6 +638,27 @@ document.getElementById('my-slot').appendChild(bar.element);
</script>
```

## `source` vs `target`

These are two separate concepts:

- **`source`** — *where* your widget is deployed. Identifies the platform or product surface,
e.g. `"website"`, `"ios-app"`, `"chrome-extension"`. Use this to filter feedback by deployment
environment in your dashboard.

- **`target`** — *what* the feedback is about. A structured object describing the specific content
or feature, e.g. a REST endpoint, a docs page, or a CLI command. Use this to group feedback by
the thing being reviewed, regardless of where the widget is embedded.

You can use both together:
```js
InputBufferIO.createBar({
apiKey: 'YOUR_WIDGET_TOKEN',
source: 'website',
target: { type: 'documentation', metadata: { page_slug: 'getting-started' } },
});


---

## License
Expand Down
1 change: 1 addition & 0 deletions TODO.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
1. investigate thumbsup/down single icon, mouseover expand to both clickable options.
82 changes: 53 additions & 29 deletions coverage/api.ts.html
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,16 @@ <h1><a href="index.html">All files</a> api.ts</h1>
<div class='clearfix'>

<div class='fl pad1y space-right2'>
<span class="strong">90% </span>
<span class="strong">88% </span>
<span class="quiet">Statements</span>
<span class='fraction'>18/20</span>
<span class='fraction'>22/25</span>
</div>


<div class='fl pad1y space-right2'>
<span class="strong">91.66% </span>
<span class="strong">77.27% </span>
<span class="quiet">Branches</span>
<span class='fraction'>11/12</span>
<span class='fraction'>17/22</span>
</div>


Expand All @@ -44,9 +44,9 @@ <h1><a href="index.html">All files</a> api.ts</h1>


<div class='fl pad1y space-right2'>
<span class="strong">94.44% </span>
<span class="strong">90.9% </span>
<span class="quiet">Lines</span>
<span class='fraction'>17/18</span>
<span class='fraction'>20/22</span>
</div>


Expand Down Expand Up @@ -123,48 +123,61 @@ <h1><a href="index.html">All files</a> api.ts</h1>
<a name='L58'></a><a href='#L58'>58</a>
<a name='L59'></a><a href='#L59'>59</a>
<a name='L60'></a><a href='#L60'>60</a>
<a name='L61'></a><a href='#L61'>61</a></td><td class="line-coverage quiet"><span class="cline-any cline-neutral">&nbsp;</span>
<a name='L61'></a><a href='#L61'>61</a>
<a name='L62'></a><a href='#L62'>62</a>
<a name='L63'></a><a href='#L63'>63</a>
<a name='L64'></a><a href='#L64'>64</a>
<a name='L65'></a><a href='#L65'>65</a>
<a name='L66'></a><a href='#L66'>66</a>
<a name='L67'></a><a href='#L67'>67</a>
<a name='L68'></a><a href='#L68'>68</a>
<a name='L69'></a><a href='#L69'>69</a></td><td class="line-coverage quiet"><span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">2x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">2x</span>
<span class="cline-any cline-yes">3x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">3x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">12x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">13x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">13x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">12x</span>
<span class="cline-any cline-yes">13x</span>
<span class="cline-any cline-yes">1x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">12x</span>
<span class="cline-any cline-yes">13x</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">12x</span>
<span class="cline-any cline-yes">13x</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">13x</span>
<span class="cline-any cline-yes">1x</span>
<span class="cline-any cline-yes">1x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">12x</span>
<span class="cline-any cline-yes">12x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">12x</span>
<span class="cline-any cline-yes">12x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">13x</span>
<span class="cline-any cline-yes">13x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">13x</span>
<span class="cline-any cline-yes">13x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
Expand All @@ -173,34 +186,37 @@ <h1><a href="index.html">All files</a> api.ts</h1>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">12x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">12x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">13x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">13x</span>
<span class="cline-any cline-yes">2x</span>
<span class="cline-any cline-yes">2x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">10x</span>
<span class="cline-any cline-yes">11x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span></td><td class="text"><pre class="prettyprint lang-js">import type { OpenOptions } from './types.js';
&nbsp;
declare const __WIDGET_VERSION__: string;
export const WIDGET_VERSION = __WIDGET_VERSION__;
&nbsp;
const DEFAULT_API_URL = 'https://inputbuffer.io/api/widget/inputs';
const DEFAULT_API_URL = 'https://inputbuffer.io/api/v0/inputs';
&nbsp;
export async function submitFeedback(
apiKey: string,
description: string,
email: string | null,
title: string | null,
options?: OpenOptions,
apiUrl?: string
): Promise&lt;{ id: string }&gt; {
const body: Record&lt;string, unknown&gt; = {
title: 'Widget feedback',
description,
};
const body: Record&lt;string, unknown&gt; = { description };
&nbsp;
if (title) body.title = title;
&nbsp;
if (email) {
body.contactEmail = email;
Expand All @@ -209,11 +225,19 @@ <h1><a href="index.html">All files</a> api.ts</h1>
<span class="missing-if-branch" title="if path not taken" >I</span>if (options?.sentiment) {
<span class="cstat-no" title="statement not covered" > body.sentiment = options.sentiment;</span>
}
&nbsp;
<span class="missing-if-branch" title="if path not taken" >I</span>if (options?.source) {
<span class="cstat-no" title="statement not covered" > body.source = options.source;</span>
}
&nbsp;
if (options?.target) {
const t = options.target;
body.targets = [{
target_type: options.target.type,
metadata: options.target.metadata,
target_type: t.type,
...(t.targetId &amp;&amp; <span class="branch-1 cbranch-no" title="branch not covered" >{ target_id: t.targetId })</span>,
...(t.displayName &amp;&amp; <span class="branch-1 cbranch-no" title="branch not covered" >{ display_name: t.displayName })</span>,
...(t.dedupKey &amp;&amp; <span class="branch-1 cbranch-no" title="branch not covered" >{ dedup_key: t.dedupKey })</span>,
metadata: t.metadata,
}];
}
&nbsp;
Expand Down Expand Up @@ -250,7 +274,7 @@ <h1><a href="index.html">All files</a> api.ts</h1>
<div class='footer quiet pad2 space-top1 center small'>
Code coverage generated by
<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
at 2026-04-28T02:43:08.549Z
at 2026-05-12T12:50:46.852Z
</div>
<script src="prettify.js"></script>
<script>
Expand Down
Loading