@heedkit/sdk-react 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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 HeedKit
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,181 @@
1
+ # @heedkit/sdk-react
2
+
3
+ React SDK for [HeedKit](https://heedkit.com). Drop-in feature requests, voting,
4
+ and comments — themed from your console.
5
+
6
+ - **Drop-in widget** — `<FeedbackButton/>` renders a floating launcher + panel.
7
+ - **Headless** — `useHeedKit()` gives you the client; build your own UI.
8
+ - **Themed remotely** — colors, radius, light/dark, tabs-vs-list, and per-kind
9
+ interactions are configured in the HeedKit console and applied at runtime.
10
+
11
+ ## Install
12
+
13
+ ```bash
14
+ npm i @heedkit/sdk-react react react-dom
15
+ ```
16
+
17
+ `react` and `react-dom` (both `>=17`) are **peer dependencies** — install them
18
+ alongside the SDK (most React apps already have them).
19
+
20
+ ## Get your project key
21
+
22
+ 1. Sign in to the HeedKit console at <https://heedkit.com>.
23
+ 2. Open your project and copy its **project key** (looks like `fh_xxx`).
24
+
25
+ The key is sent client-side as the `X-Project-Key` header on every request, so
26
+ it's safe to embed in your front-end bundle.
27
+
28
+ ## Mount the widget
29
+
30
+ ```tsx
31
+ import { HeedKitProvider, FeedbackButton } from "@heedkit/sdk-react";
32
+
33
+ export default function App() {
34
+ return (
35
+ <HeedKitProvider
36
+ projectKey="fh_xxx"
37
+ user={{ externalId: "user-123", email: "alice@example.com", name: "Alice" }}
38
+ >
39
+ <YourApp />
40
+ <FeedbackButton label="Feedback" />
41
+ </HeedKitProvider>
42
+ );
43
+ }
44
+ ```
45
+
46
+ `FeedbackButton` mounts the floating launcher and panel — the same UI as the bare
47
+ JS SDK (it delegates to it). When an admin changes theme, tabs vs list, per-kind
48
+ interactions, or `show_counts` in the console, the widget picks it up on the next
49
+ mount.
50
+
51
+ ## Imperative open / close
52
+
53
+ `FeedbackButton` is **self-contained**: it runs its own `init` and does **not**
54
+ read config from `<HeedKitProvider>`, so pass `projectKey` on every instance.
55
+ Forward a ref to drive it imperatively:
56
+
57
+ ```tsx
58
+ import React from "react";
59
+ import { FeedbackButton, type Widget } from "@heedkit/sdk-react";
60
+
61
+ function CustomTrigger() {
62
+ const ref = React.useRef<Widget>(null);
63
+
64
+ return (
65
+ <>
66
+ <FeedbackButton ref={ref} projectKey="fh_xxx" hideLauncher />
67
+ <button onClick={() => ref.current?.open()}>Open feedback</button>
68
+ <button onClick={() => ref.current?.close()}>Close</button>
69
+ </>
70
+ );
71
+ }
72
+ ```
73
+
74
+ The ref resolves to a `Widget` exposing `open()`, `close()`, `destroy()`, and the
75
+ underlying `client`. If `init` fails (bad key / network), it's logged via
76
+ `console.warn` and `open()` becomes a no-op rather than throwing.
77
+
78
+ ## Headless (no built-in UI)
79
+
80
+ `useHeedKit()` returns the ready client so you can build your own roadmap. It
81
+ must be called inside `<HeedKitProvider>` (it throws otherwise).
82
+
83
+ ```tsx
84
+ import { useHeedKit, type Feature } from "@heedkit/sdk-react";
85
+
86
+ function MyRoadmap() {
87
+ const { client, ready, theme } = useHeedKit();
88
+ const [features, setFeatures] = React.useState<Feature[]>([]);
89
+
90
+ React.useEffect(() => {
91
+ if (ready) client.list({ sort: "top" }).then(setFeatures);
92
+ }, [client, ready]);
93
+
94
+ if (!ready) return null;
95
+
96
+ // Data: client.list / submit / vote / listComments / comment
97
+ // Config: client.getEnabledKinds() / getInteractionsFor(kind) /
98
+ // getKindInteractions() / getKindVisibility() / getProjectName() /
99
+ // getTheme() / getEndUserId()
100
+ return <ul>{features.map((f) => <li key={f.id}>{f.title}</li>)}</ul>;
101
+ }
102
+ ```
103
+
104
+ ## SSR / Next.js (App Router)
105
+
106
+ `HeedKitProvider`, `FeedbackButton`, and `useHeedKit` use React hooks and Context,
107
+ so they must run in a **Client Component**. Add `"use client"` at the top of any
108
+ file that renders them (or wrap them in one):
109
+
110
+ ```tsx
111
+ "use client";
112
+ import { HeedKitProvider, FeedbackButton } from "@heedkit/sdk-react";
113
+ ```
114
+
115
+ They're safe under server rendering and streaming: the widget DOM work and
116
+ `client.init()` run inside `useEffect` (browser only), and the device-id helper
117
+ returns `null` on the server — importing the package or its types in a server
118
+ file won't crash. The only requirement is the Client Component boundary.
119
+
120
+ ## Theming
121
+
122
+ Theme is configured in the HeedKit console (not via props) and delivered on
123
+ `init`. Themeable: primary color, corner radius, light / dark / `system` mode,
124
+ font family & size, `tabs` vs `list` grouping, and per-kind `show_counts`. Read
125
+ the resolved theme in headless UIs via `useHeedKit().theme` (or
126
+ `client.getTheme()`).
127
+
128
+ ## API reference
129
+
130
+ ### `<HeedKitProvider>`
131
+
132
+ | Prop | Type | Required | Notes |
133
+ | ------------ | ----------- | -------- | --------------------------------------- |
134
+ | `projectKey` | `string` | yes | Your `fh_…` key. |
135
+ | `apiUrl` | `string` | no | Override the API base URL. |
136
+ | `user` | `EndUser` | no | `{ externalId?, email?, name?, avatarUrl?, platform? }`. Omit `externalId` to use a persisted per-browser device id. |
137
+
138
+ ### `useHeedKit()`
139
+
140
+ Returns `{ client: HeedKitClient, ready: boolean, theme: Theme }`. Throws if used
141
+ outside `<HeedKitProvider>`.
142
+
143
+ ### `<FeedbackButton>`
144
+
145
+ Accepts the same config as the provider (`projectKey`, `apiUrl`, `user`) plus:
146
+
147
+ | Prop | Type | Default | Notes |
148
+ | -------------- | --------- | ------------ | ---------------------------------- |
149
+ | `label` | `string` | `"Feedback"` | Floating launcher label. |
150
+ | `hideLauncher` | `boolean` | `false` | Hide the launcher; drive via ref. |
151
+
152
+ Forwards a ref to a `Widget` (`open()`, `close()`, `destroy()`, `client`).
153
+
154
+ ### `HeedKitClient`
155
+
156
+ | Method | Returns |
157
+ | --------------------------------- | ---------------------------------------- |
158
+ | `list(opts?)` | `Promise<Feature[]>` — `opts: { status?, kind?, sort?: "top" \| "new" }` |
159
+ | `submit(input)` | `Promise<Feature>` — `input: { title, description?, tag?, kind? }` |
160
+ | `vote(featureId)` | `Promise<{ voted: boolean; vote_count: number }>` (toggles) |
161
+ | `listComments(featureId)` | `Promise<Comment[]>` |
162
+ | `comment(featureId, body)` | `Promise<Comment>` |
163
+ | `getTheme()` | `Theme` |
164
+ | `getEnabledKinds()` | `FeatureKind[]` |
165
+ | `getInteractionsFor(kind)` | `Interaction[]` |
166
+ | `getKindInteractions()` | per-kind interaction map |
167
+ | `getKindVisibility()` | per-kind default visibility |
168
+ | `getProjectName()` | `string` |
169
+ | `getEndUserId()` | `string \| null` |
170
+
171
+ Methods other than `init`/getters throw if called before `init()` resolves; the
172
+ provider and widget call `init()` for you.
173
+
174
+ ## Example
175
+
176
+ A complete Vite + React app lives in [`Example/`](./Example) — build the SDK
177
+ once (`npm run build`), then `cd Example && npm install && npm run dev`.
178
+
179
+ ## License
180
+
181
+ MIT © HeedKit