@harkly/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.
@@ -0,0 +1,16 @@
1
+ import { type ComponentPropsWithoutRef, type ReactNode } from "react";
2
+ import { HarklyClient, type FeedbackOptions, type HarklyUser, type IdentifyOptions, type InitOptions, type MessengerOptions, type UpdatesOptions } from "@harkly/js";
3
+ export declare function HarklyProvider({ children, user, identifyOptions, ...options }: InitOptions & {
4
+ children: ReactNode;
5
+ user?: HarklyUser | null;
6
+ identifyOptions?: IdentifyOptions;
7
+ }): import("react").JSX.Element;
8
+ export declare function useHarkly(): HarklyClient;
9
+ export declare function FeedbackButton({ board, title, theme, onPosted, onClick, children, ...rest }: ComponentPropsWithoutRef<"button"> & FeedbackOptions & {
10
+ onPosted?: (postId: string) => void;
11
+ }): import("react").JSX.Element;
12
+ /** Polls the unread count on mount and whenever the user changes; renders nothing when it's zero. */
13
+ export declare function useUnreadUpdates(): number;
14
+ export declare function ChangelogBadge({ theme, onClick, children, ...rest }: ComponentPropsWithoutRef<"button"> & UpdatesOptions): import("react").JSX.Element;
15
+ /** Mounts the messenger launcher while rendered; re-mounts when the user changes so the thread follows them. */
16
+ export declare function Messenger(options: MessengerOptions): null;
package/dist/index.js ADDED
@@ -0,0 +1,4 @@
1
+ import{createContext as Y,useContext as Z,useEffect as R,useMemo as _,useState as T}from"react";import{HarklyClient as $}from"@harkly/js";import{jsxDEV as L}from"react/jsx-dev-runtime";var W=Y(null);function M({children:A,user:q,identifyOptions:F,...z}){let B=_(()=>new $(z),[z.publicKey,z.apiUrl,z.portalUrl,z.anonymous]);return R(()=>{if(q?.id)B.identify(q,F).catch(()=>{});else B.reset()},[B,q?.id,q?.email,q?.name,q?.plan,q?.mrr,F?.jwt]),L(W.Provider,{value:B,children:A},void 0,!1,void 0,this)}function Q(){let A=Z(W);if(!A)throw Error("Harkly: wrap your app in <HarklyProvider publicKey=…>");return A}function P({board:A,title:q,theme:F,onPosted:z,onClick:B,children:I,...J}){let K=Q();return L("button",{type:"button",...J,onClick:(G)=>{B?.(G),K.feedback({board:A,title:q,theme:F,onPosted:z})},children:I??"Send feedback"},void 0,!1,void 0,this)}function N(){let A=Q(),[q,F]=T(0);return R(()=>{let z=!0;return A.unread().then((B)=>{if(z)F(B)}).catch(()=>{}),()=>{z=!1}},[A,A.user?.id]),q}function U({theme:A,onClick:q,children:F,...z}){let B=Q(),I=N(),[J,K]=T(0),G=Math.max(0,I-J);return L("button",{type:"button",...z,onClick:(X)=>{q?.(X),K(I),B.updates({theme:A})},"aria-label":G?`What's new, ${G} unread`:"What's new",children:[F??"What's new",G>0&&L("span",{"data-harkly-badge":!0,children:G},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function w(A){let q=Q(),{position:F,launcher:z,theme:B,color:I}=A;return R(()=>{let J,K=!0;return q.messenger({position:F,launcher:z,theme:B,color:I}).then((G)=>{if(K)J=G;else G.destroy()}).catch(()=>{}),()=>{K=!1,J?.destroy()}},[q,q.user?.id,F,z,B,I]),null}export{N as useUnreadUpdates,Q as useHarkly,w as Messenger,M as HarklyProvider,P as FeedbackButton,U as ChangelogBadge};
2
+
3
+ //# debugId=865BFF16C834754464756E2164756E21
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,10 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/index.tsx"],
4
+ "sourcesContent": [
5
+ "// Thin React bindings over @harkly/js. The SDK keeps all the state; these\n// components only call it.\nimport { createContext, useContext, useEffect, useMemo, useState, type ComponentPropsWithoutRef, type ReactNode } from \"react\";\nimport { HarklyClient, type FeedbackOptions, type HarklyUser, type IdentifyOptions, type InitOptions, type MessengerOptions, type UpdatesOptions } from \"@harkly/js\";\n\nconst HarklyContext = createContext<HarklyClient | null>(null);\n\nexport function HarklyProvider({ children, user, identifyOptions, ...options }: InitOptions & { children: ReactNode; user?: HarklyUser | null; identifyOptions?: IdentifyOptions }) {\n const client = useMemo(() => new HarklyClient(options), [options.publicKey, options.apiUrl, options.portalUrl, options.anonymous]);\n useEffect(() => {\n if (user?.id) void client.identify(user, identifyOptions).catch(() => {});\n else client.reset();\n }, [client, user?.id, user?.email, user?.name, user?.plan, user?.mrr, identifyOptions?.jwt]);\n return <HarklyContext.Provider value={client}>{children}</HarklyContext.Provider>;\n}\n\nexport function useHarkly(): HarklyClient {\n const c = useContext(HarklyContext);\n if (!c) throw new Error(\"Harkly: wrap your app in <HarklyProvider publicKey=…>\");\n return c;\n}\n\nexport function FeedbackButton({ board, title, theme, onPosted, onClick, children, ...rest }: ComponentPropsWithoutRef<\"button\"> & FeedbackOptions & { onPosted?: (postId: string) => void }) {\n const harkly = useHarkly();\n return (\n <button type=\"button\" {...rest} onClick={(e) => { onClick?.(e); void harkly.feedback({ board, title, theme, onPosted }); }}>\n {children ?? \"Send feedback\"}\n </button>\n );\n}\n\n/** Polls the unread count on mount and whenever the user changes; renders nothing when it's zero. */\nexport function useUnreadUpdates(): number {\n const harkly = useHarkly();\n const [n, setN] = useState(0);\n useEffect(() => {\n let live = true;\n harkly.unread().then((v) => { if (live) setN(v); }).catch(() => {});\n return () => { live = false; };\n }, [harkly, harkly.user?.id]);\n return n;\n}\n\nexport function ChangelogBadge({ theme, onClick, children, ...rest }: ComponentPropsWithoutRef<\"button\"> & UpdatesOptions) {\n const harkly = useHarkly();\n const unread = useUnreadUpdates();\n const [seen, setSeen] = useState(0);\n const count = Math.max(0, unread - seen);\n return (\n <button type=\"button\" {...rest} onClick={(e) => { onClick?.(e); setSeen(unread); void harkly.updates({ theme }); }} aria-label={count ? `What's new, ${count} unread` : \"What's new\"}>\n {children ?? \"What's new\"}\n {count > 0 && <span data-harkly-badge>{count}</span>}\n </button>\n );\n}\n\n/** Mounts the messenger launcher while rendered; re-mounts when the user changes so the thread follows them. */\nexport function Messenger(options: MessengerOptions) {\n const harkly = useHarkly();\n const { position, launcher, theme, color } = options;\n useEffect(() => {\n let handle: { destroy: () => void } | undefined;\n let live = true;\n harkly.messenger({ position, launcher, theme, color }).then((h) => { if (live) handle = h; else h.destroy(); }).catch(() => {});\n return () => { live = false; handle?.destroy(); };\n }, [harkly, harkly.user?.id, position, launcher, theme, color]);\n return null;\n}\n"
6
+ ],
7
+ "mappings": "AAEA,wBAAS,gBAAe,eAAY,aAAW,cAAS,cACxD,uBAAS,kEAET,IAAM,EAAgB,EAAmC,IAAI,EAEtD,SAAS,CAAc,EAAG,WAAU,OAAM,qBAAoB,GAA+G,CAClL,IAAM,EAAS,EAAQ,IAAM,IAAI,EAAa,CAAO,EAAG,CAAC,EAAQ,UAAW,EAAQ,OAAQ,EAAQ,UAAW,EAAQ,SAAS,CAAC,EAKjI,OAJA,EAAU,IAAM,CACd,GAAI,GAAM,GAAS,EAAO,SAAS,EAAM,CAAe,EAAE,MAAM,IAAM,EAAE,EACnE,OAAO,MAAM,GACjB,CAAC,EAAQ,GAAM,GAAI,GAAM,MAAO,GAAM,KAAM,GAAM,KAAM,GAAM,IAAK,GAAiB,GAAG,CAAC,EACpF,EAAmD,EAAc,SAAjE,CAAwB,MAAO,EAA/B,SAAwC,GAAxC,qBAAmD,EAGrD,SAAS,CAAS,EAAiB,CACxC,IAAM,EAAI,EAAW,CAAa,EAClC,GAAI,CAAC,EAAG,MAAU,MAAM,uDAAsD,EAC9E,OAAO,EAGF,SAAS,CAAc,EAAG,QAAO,QAAO,QAAO,WAAU,UAAS,cAAa,GAAwG,CAC5L,IAAM,EAAS,EAAU,EACzB,OACE,EAEE,SAFF,CAAQ,KAAK,YAAa,EAAM,QAAS,CAAC,IAAM,CAAE,IAAU,CAAC,EAAQ,EAAO,SAAS,CAAE,QAAO,QAAO,QAAO,UAAS,CAAC,GAAtH,SACG,GAAY,iBADf,qBAEE,EAKC,SAAS,CAAgB,EAAW,CACzC,IAAM,EAAS,EAAU,GAClB,EAAG,GAAQ,EAAS,CAAC,EAM5B,OALA,EAAU,IAAM,CACd,IAAI,EAAO,GAEX,OADA,EAAO,OAAO,EAAE,KAAK,CAAC,IAAM,CAAE,GAAI,EAAM,EAAK,CAAC,EAAI,EAAE,MAAM,IAAM,EAAE,EAC3D,IAAM,CAAE,EAAO,KACrB,CAAC,EAAQ,EAAO,MAAM,EAAE,CAAC,EACrB,EAGF,SAAS,CAAc,EAAG,QAAO,UAAS,cAAa,GAA6D,CACzH,IAAM,EAAS,EAAU,EACnB,EAAS,EAAiB,GACzB,EAAM,GAAW,EAAS,CAAC,EAC5B,EAAQ,KAAK,IAAI,EAAG,EAAS,CAAI,EACvC,OACE,EAGE,SAHF,CAAQ,KAAK,YAAa,EAAM,QAAS,CAAC,IAAM,CAAE,IAAU,CAAC,EAAG,EAAQ,CAAM,EAAQ,EAAO,QAAQ,CAAE,OAAM,CAAC,GAAM,aAAY,EAAQ,eAAe,WAAiB,aAAxK,SAGE,CAFC,GAAY,aACZ,EAAQ,GAAK,EAAiC,OAAjC,CAAM,oBAAiB,GAAvB,SAAyB,GAAzB,qBAAiC,IAFjD,qBAGE,EAKC,SAAS,CAAS,CAAC,EAA2B,CACnD,IAAM,EAAS,EAAU,GACjB,WAAU,WAAU,QAAO,SAAU,EAO7C,OANA,EAAU,IAAM,CACd,IAAI,EACA,EAAO,GAEX,OADA,EAAO,UAAU,CAAE,WAAU,WAAU,QAAO,OAAM,CAAC,EAAE,KAAK,CAAC,IAAM,CAAE,GAAI,EAAM,EAAS,EAAQ,OAAE,QAAQ,EAAI,EAAE,MAAM,IAAM,EAAE,EACvH,IAAM,CAAE,EAAO,GAAO,GAAQ,QAAQ,IAC5C,CAAC,EAAQ,EAAO,MAAM,GAAI,EAAU,EAAU,EAAO,CAAK,CAAC,EACvD",
8
+ "debugId": "865BFF16C834754464756E2164756E21",
9
+ "names": []
10
+ }
package/package.json ADDED
@@ -0,0 +1,55 @@
1
+ {
2
+ "name": "@harkly/react",
3
+ "version": "0.1.0",
4
+ "description": "React bindings for the Harkly SDK: a provider, a feedback button and a what's-new badge.",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "main": "./dist/index.js",
8
+ "module": "./dist/index.js",
9
+ "types": "./dist/index.d.ts",
10
+ "exports": {
11
+ ".": {
12
+ "types": "./dist/index.d.ts",
13
+ "import": "./dist/index.js",
14
+ "default": "./dist/index.js"
15
+ }
16
+ },
17
+ "files": [
18
+ "dist"
19
+ ],
20
+ "sideEffects": false,
21
+ "scripts": {
22
+ "build": "bun run build.ts",
23
+ "typecheck": "tsc --noEmit",
24
+ "test": "bun test"
25
+ },
26
+ "dependencies": {
27
+ "@harkly/js": "0.1.0"
28
+ },
29
+ "peerDependencies": {
30
+ "react": ">=18"
31
+ },
32
+ "devDependencies": {
33
+ "@types/bun": "latest",
34
+ "@types/react": "^19",
35
+ "react": "19.2.8",
36
+ "react-dom": "19.2.8",
37
+ "@types/react-dom": "^19"
38
+ },
39
+ "homepage": "https://harkly.app",
40
+ "repository": {
41
+ "type": "git",
42
+ "url": "https://github.com/Rad-Soft/harkly.git",
43
+ "directory": "packages/sdk-react"
44
+ },
45
+ "publishConfig": {
46
+ "access": "public"
47
+ },
48
+ "keywords": [
49
+ "harkly",
50
+ "feedback",
51
+ "support",
52
+ "messenger",
53
+ "widget"
54
+ ]
55
+ }