@userz-ai/react 1.0.2 → 2.0.1
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/README.md +2 -2
- package/dist/index.d.mts +4 -0
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +6 -6
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -6,10 +6,10 @@ Idiomatic React bindings for the [Userz](https://userz.ai) feedback widget. A pr
|
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
8
|
pnpm add @userz-ai/react @userz-ai/browser
|
|
9
|
-
# Optional peer for richer screenshots
|
|
10
|
-
pnpm add @zumer/snapdom
|
|
11
9
|
```
|
|
12
10
|
|
|
11
|
+
Screenshot capture (`modern-screenshot`) ships as a transitive dependency of `@userz-ai/browser` — no extra install step required.
|
|
12
|
+
|
|
13
13
|
React 18 and 19 are both supported.
|
|
14
14
|
|
|
15
15
|
## Quick start
|
package/dist/index.d.mts
CHANGED
|
@@ -59,6 +59,10 @@ interface ScreenshotEditorProps {
|
|
|
59
59
|
brandColor?: string;
|
|
60
60
|
/** Save-button text color. Defaults to brand foreground. */
|
|
61
61
|
brandForeground?: string;
|
|
62
|
+
/** Cancel-button surface color. Defaults to white. */
|
|
63
|
+
backgroundColor?: string;
|
|
64
|
+
/** Cancel-button text color. Defaults to near-black. */
|
|
65
|
+
textColor?: string;
|
|
62
66
|
}
|
|
63
67
|
declare function ScreenshotEditor(props: ScreenshotEditorProps): ReactNode;
|
|
64
68
|
//#endregion
|
package/dist/index.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/provider.tsx","../src/ScreenshotEditor.tsx","../src/UserzTarget.tsx"],"mappings":";;;;;UAYiB,kBAAA,SAA2B,WAAA;EAC1C,QAAA,EAAU,SAAA;AAAA;AADZ;;;;;;;;;AAeA;;AAfA,iBAegB,aAAA,CAAA;EAAgB,QAAA;EAAA,GAAa;AAAA,GAAU,kBAAA,GAAkB,oBAAA,CAAA,GAAA,CAAA,OAAA;AAAA,iBA0CzD,QAAA,CAAA,GAAY,KAAA;;;;;;;AAzD5B;;;;;;;;;AAeA;;;;;;UCGiB,qBAAA;EDHwD;ECKvE,IAAA,EAAM,IAAA;EDLwB;ECO9B,KAAA;EACA,MAAA;EDRuE;ECUvE,MAAA,GAAS,SAAA,EAAW,IAAA,YAAgB,OAAA;EACpC,QAAA;EDXuE;ECavE,KAAA,GAAQ,aAAA;EACR,SAAA;;EAEA,UAAA;ED0B+B;ECxB/B,eAAA;AAAA;AAAA,iBAGc,gBAAA,CAAiB,KAAA,EAAO,qBAAA,GAAwB,SAAA;;;
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/provider.tsx","../src/ScreenshotEditor.tsx","../src/UserzTarget.tsx"],"mappings":";;;;;UAYiB,kBAAA,SAA2B,WAAA;EAC1C,QAAA,EAAU,SAAA;AAAA;AADZ;;;;;;;;;AAeA;;AAfA,iBAegB,aAAA,CAAA;EAAgB,QAAA;EAAA,GAAa;AAAA,GAAU,kBAAA,GAAkB,oBAAA,CAAA,GAAA,CAAA,OAAA;AAAA,iBA0CzD,QAAA,CAAA,GAAY,KAAA;;;;;;;AAzD5B;;;;;;;;;AAeA;;;;;;UCGiB,qBAAA;EDHwD;ECKvE,IAAA,EAAM,IAAA;EDLwB;ECO9B,KAAA;EACA,MAAA;EDRuE;ECUvE,MAAA,GAAS,SAAA,EAAW,IAAA,YAAgB,OAAA;EACpC,QAAA;EDXuE;ECavE,KAAA,GAAQ,aAAA;EACR,SAAA;;EAEA,UAAA;ED0B+B;ECxB/B,eAAA;;EAEA,eAAA;EAjBe;EAmBf,SAAA;AAAA;AAAA,iBAGc,gBAAA,CAAiB,KAAA,EAAO,qBAAA,GAAwB,SAAA;;;UCxC/C,gBAAA;;;EAGf,IAAA;EFHe;EEKf,IAAA,GAAO,MAAA;;;EAGP,QAAA,EAAU,SAAA;AAAA;;;;AFOZ;;;;;;;;iBEOgB,WAAA,CAAA;EAAc,IAAA;EAAM,IAAA;EAAM;AAAA,GAAY,gBAAA,GAAgB,oBAAA,CAAA,GAAA,CAAA,OAAA"}
|
package/dist/index.mjs
CHANGED
|
@@ -82,7 +82,7 @@ async function loadTldraw() {
|
|
|
82
82
|
return null;
|
|
83
83
|
}
|
|
84
84
|
}
|
|
85
|
-
function TldrawEditor({ tldraw, blob, width, height, onSave, onCancel, style, className, brandColor = UZ_DEFAULT_BRAND, brandForeground = UZ_DEFAULT_BRAND_FG }) {
|
|
85
|
+
function TldrawEditor({ tldraw, blob, width, height, onSave, onCancel, style, className, brandColor = UZ_DEFAULT_BRAND, brandForeground = UZ_DEFAULT_BRAND_FG, backgroundColor = "white", textColor = "#111" }) {
|
|
86
86
|
const [editor, setEditor] = useState(null);
|
|
87
87
|
const [saving, setSaving] = useState(false);
|
|
88
88
|
const onMount = useCallback(async (ed) => {
|
|
@@ -166,13 +166,13 @@ function TldrawEditor({ tldraw, blob, width, height, onSave, onCancel, style, cl
|
|
|
166
166
|
children: [onCancel && /* @__PURE__ */ jsx("button", {
|
|
167
167
|
type: "button",
|
|
168
168
|
onClick: onCancel,
|
|
169
|
-
style: editorButtonStyle("ghost", brandColor, brandForeground),
|
|
169
|
+
style: editorButtonStyle("ghost", brandColor, brandForeground, backgroundColor, textColor),
|
|
170
170
|
disabled: saving,
|
|
171
171
|
children: "Cancel"
|
|
172
172
|
}), /* @__PURE__ */ jsx("button", {
|
|
173
173
|
type: "button",
|
|
174
174
|
onClick: handleSave,
|
|
175
|
-
style: editorButtonStyle("primary", brandColor, brandForeground),
|
|
175
|
+
style: editorButtonStyle("primary", brandColor, brandForeground, backgroundColor, textColor),
|
|
176
176
|
disabled: saving || !editor,
|
|
177
177
|
children: saving ? "Saving…" : "Save annotation"
|
|
178
178
|
})]
|
|
@@ -231,7 +231,7 @@ const loadingStyle = {
|
|
|
231
231
|
fontSize: 14,
|
|
232
232
|
color: "#555"
|
|
233
233
|
};
|
|
234
|
-
function editorButtonStyle(kind, brandColor, brandForeground) {
|
|
234
|
+
function editorButtonStyle(kind, brandColor, brandForeground, backgroundColor, textColor) {
|
|
235
235
|
if (kind === "primary") return {
|
|
236
236
|
padding: "8px 14px",
|
|
237
237
|
borderRadius: 6,
|
|
@@ -246,8 +246,8 @@ function editorButtonStyle(kind, brandColor, brandForeground) {
|
|
|
246
246
|
padding: "8px 14px",
|
|
247
247
|
borderRadius: 6,
|
|
248
248
|
border: "1px solid rgba(0,0,0,0.18)",
|
|
249
|
-
background:
|
|
250
|
-
color:
|
|
249
|
+
background: backgroundColor,
|
|
250
|
+
color: textColor,
|
|
251
251
|
cursor: "pointer"
|
|
252
252
|
};
|
|
253
253
|
}
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../src/provider.tsx","../src/ScreenshotEditor.tsx","../src/UserzTarget.tsx"],"sourcesContent":["import { createUserz, type Userz, type UserzConfig } from '@userz-ai/browser';\nimport {\n createContext,\n type ReactNode,\n useContext,\n useEffect,\n useReducer,\n useRef,\n} from 'react';\n\nconst UserzCtx = createContext<Userz | null>(null);\n\nexport interface UserzProviderProps extends UserzConfig {\n children: ReactNode;\n}\n\n/**\n * React provider that owns one Userz instance for the lifetime of the\n * component tree. Re-renders DON'T tear down the widget — config changes\n * after the first render are ignored (same as Sentry/Datadog SDKs). Use\n * `useUserz()` to call methods (setUser, submit, …) imperatively.\n *\n * StrictMode dev lifecycle is `render → setup → cleanup → setup` (render\n * only fires ONCE per mount cycle). The cleanup destroys the widget and\n * nulls the ref; the second setup re-creates it and forces a re-render so\n * the context value swaps from the destroyed instance to the live one.\n */\nexport function UserzProvider({ children, ...config }: UserzProviderProps) {\n const instance = useRef<Userz | null>(null);\n // Freeze initial config — matches the documented \"ignored after mount\"\n // semantics and gives the StrictMode re-setup something to recreate from.\n const configRef = useRef(config);\n const [, rerender] = useReducer((x: number) => x + 1, 0);\n\n // Initialize once, lazily — and only in the browser. Next.js, Remix, and\n // any other RSC/SSR framework will render this provider on the server to\n // emit the initial HTML; `createUserz` touches `document` immediately so\n // running it server-side crashes the render. The widget has nothing to do\n // during SSR anyway (no bubble to paint, no console to capture), so we\n // defer construction to the client pass.\n if (instance.current === null && typeof document !== 'undefined') {\n instance.current = createUserz(configRef.current);\n }\n\n useEffect(() => {\n // StrictMode remount: the previous cleanup destroyed the widget and\n // nulled the ref, but render doesn't re-fire between cleanup and the\n // next setup. Recreate here, then force a re-render so the context\n // value (currently the destroyed instance) updates for children.\n if (instance.current === null && typeof document !== 'undefined') {\n instance.current = createUserz(configRef.current);\n rerender();\n }\n return () => {\n instance.current?.destroy();\n instance.current = null;\n };\n }, []);\n\n // Pass through identity updates if the parent passes them via prop\n // changes after mount.\n const initialUser = config.initialUser;\n useEffect(() => {\n if (initialUser !== undefined) instance.current?.setUser(initialUser);\n }, [initialUser]);\n\n return <UserzCtx.Provider value={instance.current}>{children}</UserzCtx.Provider>;\n}\n\nexport function useUserz(): Userz {\n const u = useContext(UserzCtx);\n if (!u) throw new Error('useUserz must be used inside <UserzProvider>');\n return u;\n}\n","import { UZ_DEFAULT_BRAND, UZ_DEFAULT_BRAND_FG } from '@userz-ai/browser';\nimport {\n type CSSProperties,\n type ReactNode,\n Suspense,\n useCallback,\n useEffect,\n useState,\n} from 'react';\n\n/**\n * Annotated screenshot editor.\n *\n * Wraps tldraw to give the end-user a quick \"circle the broken thing, type\n * an arrow, save\" workflow before submitting feedback. The tldraw bundle\n * (~600KB) is dynamic-imported on first mount so apps that never open the\n * editor don't pay for it; the component shows a loading state until ready.\n *\n * tldraw is declared as an OPTIONAL peer dep in package.json. Apps that\n * want this component MUST install `tldraw` themselves; without it the\n * component renders an instructive fallback instead of throwing.\n *\n * Usage:\n * <ScreenshotEditor\n * blob={blob}\n * onSave={(annotated) => userz.submit({ ..., attachments: [annotated] })}\n * onCancel={() => setOpen(false)}\n * />\n */\n\nexport interface ScreenshotEditorProps {\n /** PNG/JPEG blob to seed the canvas with. */\n blob: Blob;\n /** Optional dimensions override; default is the blob's natural size. */\n width?: number;\n height?: number;\n /** Receives the annotated PNG. */\n onSave: (annotated: Blob) => void | Promise<void>;\n onCancel?: () => void;\n /** Inline style override on the editor container. */\n style?: CSSProperties;\n className?: string;\n /** Save-button color. Defaults to the Userz brand mint. */\n brandColor?: string;\n /** Save-button text color. Defaults to brand foreground. */\n brandForeground?: string;\n}\n\nexport function ScreenshotEditor(props: ScreenshotEditorProps): ReactNode {\n return (\n <Suspense fallback={<EditorLoadingState />}>\n <LazyEditor {...props} />\n </Suspense>\n );\n}\n\nfunction LazyEditor(props: ScreenshotEditorProps) {\n const [tldraw, setTldraw] = useState<TldrawModule | null | 'missing'>(null);\n\n useEffect(() => {\n let cancelled = false;\n loadTldraw().then(\n (mod) => {\n if (!cancelled) setTldraw(mod ?? 'missing');\n },\n () => {\n if (!cancelled) setTldraw('missing');\n },\n );\n return () => {\n cancelled = true;\n };\n }, []);\n\n if (tldraw === null) return <EditorLoadingState />;\n if (tldraw === 'missing') return <MissingPeerFallback />;\n\n return <TldrawEditor tldraw={tldraw} {...props} />;\n}\n\ninterface TldrawModule {\n Tldraw: (props: Record<string, unknown>) => ReactNode;\n exportAs: (\n editor: unknown,\n shapeIds: unknown[],\n format: string,\n opts?: Record<string, unknown>,\n ) => Promise<Blob>;\n AssetRecordType: {\n createId(): string;\n };\n createShapeId(): string;\n}\n\nasync function loadTldraw(): Promise<TldrawModule | null> {\n try {\n // The string is split with a runtime expression so bundlers don't try\n // to follow the import; tldraw stays out of the build graph entirely\n // for apps that don't install it.\n const name = ['tld', 'raw'].join('');\n const mod = (await import(/* @vite-ignore */ name)) as Partial<TldrawModule>;\n if (!mod.Tldraw) return null;\n return mod as TldrawModule;\n } catch {\n return null;\n }\n}\n\ninterface TldrawEditorProps extends ScreenshotEditorProps {\n tldraw: TldrawModule;\n}\n\nfunction TldrawEditor({\n tldraw,\n blob,\n width,\n height,\n onSave,\n onCancel,\n style,\n className,\n brandColor = UZ_DEFAULT_BRAND,\n brandForeground = UZ_DEFAULT_BRAND_FG,\n}: TldrawEditorProps) {\n const [editor, setEditor] = useState<unknown>(null);\n const [saving, setSaving] = useState(false);\n\n const onMount = useCallback(\n async (ed: unknown) => {\n setEditor(ed);\n const url = URL.createObjectURL(blob);\n try {\n const dims = width && height ? { w: width, h: height } : await measureBlob(blob);\n const assetId = tldraw.AssetRecordType.createId();\n const shapeId = tldraw.createShapeId();\n const editorAny = ed as {\n createAssets: (assets: unknown[]) => void;\n createShape: (s: unknown) => void;\n };\n editorAny.createAssets([\n {\n id: assetId,\n type: 'image',\n typeName: 'asset',\n props: {\n name: 'screenshot.png',\n src: url,\n w: dims.w,\n h: dims.h,\n mimeType: blob.type || 'image/png',\n isAnimated: false,\n },\n meta: {},\n },\n ]);\n editorAny.createShape({\n id: shapeId,\n type: 'image',\n x: 0,\n y: 0,\n props: { assetId, w: dims.w, h: dims.h },\n });\n } catch {\n // best-effort seeding; user can still draw on a blank canvas\n }\n },\n [tldraw, blob, width, height],\n );\n\n const handleSave = useCallback(async () => {\n if (!editor || saving) return;\n setSaving(true);\n try {\n const editorAny = editor as { getCurrentPageShapeIds(): Set<unknown> };\n const ids = Array.from(editorAny.getCurrentPageShapeIds());\n const png = await tldraw.exportAs(editor, ids, 'png', { background: false });\n await onSave(png);\n } finally {\n setSaving(false);\n }\n }, [editor, saving, tldraw, onSave]);\n\n const TldrawCmp = tldraw.Tldraw as unknown as (p: {\n onMount: (ed: unknown) => void;\n persistenceKey?: string;\n }) => ReactNode;\n\n return (\n <div\n className={className}\n style={{\n position: 'relative',\n width: '100%',\n height: '60vh',\n minHeight: 360,\n ...style,\n }}\n >\n <TldrawCmp onMount={onMount} />\n <div\n style={{\n position: 'absolute',\n right: 12,\n bottom: 12,\n display: 'flex',\n gap: 8,\n zIndex: 1000,\n }}\n >\n {onCancel && (\n <button\n type=\"button\"\n onClick={onCancel}\n style={editorButtonStyle('ghost', brandColor, brandForeground)}\n disabled={saving}\n >\n Cancel\n </button>\n )}\n <button\n type=\"button\"\n onClick={handleSave}\n style={editorButtonStyle('primary', brandColor, brandForeground)}\n disabled={saving || !editor}\n >\n {saving ? 'Saving…' : 'Save annotation'}\n </button>\n </div>\n </div>\n );\n}\n\nfunction EditorLoadingState() {\n return (\n <div style={loadingStyle}>\n <span>Loading editor…</span>\n </div>\n );\n}\n\nfunction MissingPeerFallback() {\n return (\n <div style={loadingStyle}>\n <div style={{ maxWidth: 360, textAlign: 'center', lineHeight: 1.45 }}>\n <strong>Screenshot annotation unavailable.</strong>\n <div style={{ marginTop: 8, fontSize: 13, opacity: 0.8 }}>\n Install <code>tldraw</code> in your app to enable the in-widget editor:\n <pre\n style={{\n marginTop: 8,\n padding: 8,\n background: 'rgba(0,0,0,0.05)',\n borderRadius: 6,\n fontSize: 12,\n }}\n >\n pnpm add tldraw\n </pre>\n </div>\n </div>\n </div>\n );\n}\n\nconst loadingStyle: CSSProperties = {\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n width: '100%',\n height: '60vh',\n minHeight: 360,\n border: '1px dashed rgba(0,0,0,0.15)',\n borderRadius: 8,\n fontSize: 14,\n color: '#555',\n};\n\nfunction editorButtonStyle(\n kind: 'primary' | 'ghost',\n brandColor: string,\n brandForeground: string,\n): CSSProperties {\n if (kind === 'primary') {\n return {\n padding: '8px 14px',\n borderRadius: 6,\n border: 'none',\n background: brandColor,\n color: brandForeground,\n fontWeight: 600,\n cursor: 'pointer',\n boxShadow: '0 4px 12px rgba(0,0,0,0.18)',\n };\n }\n return {\n padding: '8px 14px',\n borderRadius: 6,\n border: '1px solid rgba(0,0,0,0.18)',\n background: 'white',\n color: '#111',\n cursor: 'pointer',\n };\n}\n\nasync function measureBlob(blob: Blob): Promise<{ w: number; h: number }> {\n return new Promise((resolve, reject) => {\n const img = new Image();\n const url = URL.createObjectURL(blob);\n img.onload = () => {\n URL.revokeObjectURL(url);\n resolve({ w: img.naturalWidth, h: img.naturalHeight });\n };\n img.onerror = (err) => {\n URL.revokeObjectURL(url);\n reject(err);\n };\n img.src = url;\n });\n}\n","import {\n Children,\n cloneElement,\n isValidElement,\n type ReactElement,\n type ReactNode,\n useEffect,\n useMemo,\n useRef,\n} from 'react';\nimport { useUserz } from './provider';\n\nexport interface UserzTargetProps {\n /** Stable display name for this target. Surfaces in feedback as the\n * \"component the user clicked on\" label. */\n name: string;\n /** Opt-in metadata you want forwarded with feedback. Keep small + non-sensitive. */\n meta?: Record<string, unknown>;\n /** A single child element (no fragments). We attach a ref to it without\n * a wrapper div so layout is unchanged. */\n children: ReactNode;\n}\n\n/**\n * Wraps a child element to mark it as a \"feedback target\" the end-user can\n * click on while the targeting overlay is active (Ctrl+Shift+U by default).\n *\n * Implementation: clones the single child, attaches a ref, and registers/\n * unregisters with the Userz instance over the child's lifecycle. We do NOT\n * inject a wrapper div — that would break flex/grid layouts. As a result,\n * the child must accept a `ref`; for most DOM elements and `forwardRef`\n * components this is fine. For function components without forwardRef, we\n * fall back to a `display: contents` wrapper.\n */\nexport function UserzTarget({ name, meta, children }: UserzTargetProps) {\n const u = useUserz();\n const ref = useRef<HTMLElement | null>(null);\n // Stable meta identity so the effect only re-runs when shape actually changes.\n // The dep is intentionally the JSON string, not `meta`, so callers passing a\n // fresh object literal each render don't thrash the registration.\n const metaJson = JSON.stringify(meta ?? null);\n // biome-ignore lint/correctness/useExhaustiveDependencies: see above\n const stableMeta = useMemo(() => meta, [metaJson]);\n\n useEffect(() => {\n const el = ref.current;\n if (!el) return;\n return u.registerTarget({ el, name, meta: stableMeta });\n }, [u, name, stableMeta]);\n\n const only = Children.only(children);\n if (isValidElement(only) && (typeof only.type === 'string' || hasForwardedRef(only))) {\n const original = (only.props as { ref?: unknown }).ref;\n return cloneElement(only as ReactElement<{ ref?: unknown }>, {\n ref: composeRefs(ref, original),\n });\n }\n // Function component without forwardRef — use a `display: contents` span\n // so we can attach a ref without affecting layout.\n return (\n <span\n ref={(el) => {\n ref.current = el;\n }}\n style={{ display: 'contents' }}\n >\n {only}\n </span>\n );\n}\n\nfunction hasForwardedRef(el: ReactElement): boolean {\n // Conservative heuristic — React.forwardRef components have a \"$$typeof\"\n // marker on their type. We can't import it portably across React versions,\n // so we check the symbol indirectly.\n const t = el.type as unknown as { $$typeof?: symbol };\n return typeof t === 'object' && typeof t.$$typeof?.toString === 'function';\n}\n\nfunction composeRefs<T>(...refs: unknown[]): (node: T) => void {\n return (node: T) => {\n for (const r of refs) {\n if (typeof r === 'function') (r as (n: T) => void)(node);\n else if (r && typeof r === 'object' && 'current' in r) {\n (r as { current: T }).current = node;\n }\n }\n };\n}\n"],"mappings":";;;;;AAUA,MAAM,WAAW,cAA4B,KAAK;;;;;;;;;;;;AAiBlD,SAAgB,cAAc,EAAE,UAAU,GAAG,UAA8B;CACzE,MAAM,WAAW,OAAqB,KAAK;CAG3C,MAAM,YAAY,OAAO,OAAO;CAChC,MAAM,GAAG,YAAY,YAAY,MAAc,IAAI,GAAG,EAAE;AAQxD,KAAI,SAAS,YAAY,QAAQ,OAAO,aAAa,YACnD,UAAS,UAAU,YAAY,UAAU,QAAQ;AAGnD,iBAAgB;AAKd,MAAI,SAAS,YAAY,QAAQ,OAAO,aAAa,aAAa;AAChE,YAAS,UAAU,YAAY,UAAU,QAAQ;AACjD,aAAU;;AAEZ,eAAa;AACX,YAAS,SAAS,SAAS;AAC3B,YAAS,UAAU;;IAEpB,EAAE,CAAC;CAIN,MAAM,cAAc,OAAO;AAC3B,iBAAgB;AACd,MAAI,gBAAgB,OAAW,UAAS,SAAS,QAAQ,YAAY;IACpE,CAAC,YAAY,CAAC;AAEjB,QAAO,oBAAC,SAAS,UAAV;EAAmB,OAAO,SAAS;EAAU;EAA6B;;AAGnF,SAAgB,WAAkB;CAChC,MAAM,IAAI,WAAW,SAAS;AAC9B,KAAI,CAAC,EAAG,OAAM,IAAI,MAAM,+CAA+C;AACvE,QAAO;;;;;ACxBT,SAAgB,iBAAiB,OAAyC;AACxE,QACE,oBAAC,UAAD;EAAU,UAAU,oBAAC,oBAAD,EAAsB;YACxC,oBAAC,YAAD,EAAY,GAAI,OAAS;EAChB;;AAIf,SAAS,WAAW,OAA8B;CAChD,MAAM,CAAC,QAAQ,aAAa,SAA0C,KAAK;AAE3E,iBAAgB;EACd,IAAI,YAAY;AAChB,cAAY,CAAC,MACV,QAAQ;AACP,OAAI,CAAC,UAAW,WAAU,OAAO,UAAU;WAEvC;AACJ,OAAI,CAAC,UAAW,WAAU,UAAU;IAEvC;AACD,eAAa;AACX,eAAY;;IAEb,EAAE,CAAC;AAEN,KAAI,WAAW,KAAM,QAAO,oBAAC,oBAAD,EAAsB;AAClD,KAAI,WAAW,UAAW,QAAO,oBAAC,qBAAD,EAAuB;AAExD,QAAO,oBAAC,cAAD;EAAsB;EAAQ,GAAI;EAAS;;AAiBpD,eAAe,aAA2C;AACxD,KAAI;EAKF,MAAM,MAAO,MAAM,OADN,CAAC,OAAO,MAAM,CAAC,KAAK,GAAG;AAEpC,MAAI,CAAC,IAAI,OAAQ,QAAO;AACxB,SAAO;SACD;AACN,SAAO;;;AAQX,SAAS,aAAa,EACpB,QACA,MACA,OACA,QACA,QACA,UACA,OACA,WACA,aAAa,kBACb,kBAAkB,uBACE;CACpB,MAAM,CAAC,QAAQ,aAAa,SAAkB,KAAK;CACnD,MAAM,CAAC,QAAQ,aAAa,SAAS,MAAM;CAE3C,MAAM,UAAU,YACd,OAAO,OAAgB;AACrB,YAAU,GAAG;EACb,MAAM,MAAM,IAAI,gBAAgB,KAAK;AACrC,MAAI;GACF,MAAM,OAAO,SAAS,SAAS;IAAE,GAAG;IAAO,GAAG;IAAQ,GAAG,MAAM,YAAY,KAAK;GAChF,MAAM,UAAU,OAAO,gBAAgB,UAAU;GACjD,MAAM,UAAU,OAAO,eAAe;GACtC,MAAM,YAAY;AAIlB,aAAU,aAAa,CACrB;IACE,IAAI;IACJ,MAAM;IACN,UAAU;IACV,OAAO;KACL,MAAM;KACN,KAAK;KACL,GAAG,KAAK;KACR,GAAG,KAAK;KACR,UAAU,KAAK,QAAQ;KACvB,YAAY;KACb;IACD,MAAM,EAAE;IACT,CACF,CAAC;AACF,aAAU,YAAY;IACpB,IAAI;IACJ,MAAM;IACN,GAAG;IACH,GAAG;IACH,OAAO;KAAE;KAAS,GAAG,KAAK;KAAG,GAAG,KAAK;KAAG;IACzC,CAAC;UACI;IAIV;EAAC;EAAQ;EAAM;EAAO;EAAO,CAC9B;CAED,MAAM,aAAa,YAAY,YAAY;AACzC,MAAI,CAAC,UAAU,OAAQ;AACvB,YAAU,KAAK;AACf,MAAI;GACF,MAAM,YAAY;GAClB,MAAM,MAAM,MAAM,KAAK,UAAU,wBAAwB,CAAC;AAE1D,SAAM,OADM,MAAM,OAAO,SAAS,QAAQ,KAAK,OAAO,EAAE,YAAY,OAAO,CAAC,CAC3D;YACT;AACR,aAAU,MAAM;;IAEjB;EAAC;EAAQ;EAAQ;EAAQ;EAAO,CAAC;CAEpC,MAAM,YAAY,OAAO;AAKzB,QACE,qBAAC,OAAD;EACa;EACX,OAAO;GACL,UAAU;GACV,OAAO;GACP,QAAQ;GACR,WAAW;GACX,GAAG;GACJ;YARH,CAUE,oBAAC,WAAD,EAAoB,SAAW,GAC/B,qBAAC,OAAD;GACE,OAAO;IACL,UAAU;IACV,OAAO;IACP,QAAQ;IACR,SAAS;IACT,KAAK;IACL,QAAQ;IACT;aARH,CAUG,YACC,oBAAC,UAAD;IACE,MAAK;IACL,SAAS;IACT,OAAO,kBAAkB,SAAS,YAAY,gBAAgB;IAC9D,UAAU;cACX;IAEQ,GAEX,oBAAC,UAAD;IACE,MAAK;IACL,SAAS;IACT,OAAO,kBAAkB,WAAW,YAAY,gBAAgB;IAChE,UAAU,UAAU,CAAC;cAEpB,SAAS,YAAY;IACf,EACL;KACF;;;AAIV,SAAS,qBAAqB;AAC5B,QACE,oBAAC,OAAD;EAAK,OAAO;YACV,oBAAC,QAAD,YAAM,mBAAsB;EACxB;;AAIV,SAAS,sBAAsB;AAC7B,QACE,oBAAC,OAAD;EAAK,OAAO;YACV,qBAAC,OAAD;GAAK,OAAO;IAAE,UAAU;IAAK,WAAW;IAAU,YAAY;IAAM;aAApE,CACE,oBAAC,UAAD,YAAQ,sCAA2C,GACnD,qBAAC,OAAD;IAAK,OAAO;KAAE,WAAW;KAAG,UAAU;KAAI,SAAS;KAAK;cAAxD;KAA0D;KAChD,oBAAC,QAAD,YAAM,UAAa;;KAC3B,oBAAC,OAAD;MACE,OAAO;OACL,WAAW;OACX,SAAS;OACT,YAAY;OACZ,cAAc;OACd,UAAU;OACX;gBACF;MAEK;KACF;MACF;;EACF;;AAIV,MAAM,eAA8B;CAClC,SAAS;CACT,YAAY;CACZ,gBAAgB;CAChB,OAAO;CACP,QAAQ;CACR,WAAW;CACX,QAAQ;CACR,cAAc;CACd,UAAU;CACV,OAAO;CACR;AAED,SAAS,kBACP,MACA,YACA,iBACe;AACf,KAAI,SAAS,UACX,QAAO;EACL,SAAS;EACT,cAAc;EACd,QAAQ;EACR,YAAY;EACZ,OAAO;EACP,YAAY;EACZ,QAAQ;EACR,WAAW;EACZ;AAEH,QAAO;EACL,SAAS;EACT,cAAc;EACd,QAAQ;EACR,YAAY;EACZ,OAAO;EACP,QAAQ;EACT;;AAGH,eAAe,YAAY,MAA+C;AACxE,QAAO,IAAI,SAAS,SAAS,WAAW;EACtC,MAAM,MAAM,IAAI,OAAO;EACvB,MAAM,MAAM,IAAI,gBAAgB,KAAK;AACrC,MAAI,eAAe;AACjB,OAAI,gBAAgB,IAAI;AACxB,WAAQ;IAAE,GAAG,IAAI;IAAc,GAAG,IAAI;IAAe,CAAC;;AAExD,MAAI,WAAW,QAAQ;AACrB,OAAI,gBAAgB,IAAI;AACxB,UAAO,IAAI;;AAEb,MAAI,MAAM;GACV;;;;;;;;;;;;;;;;AC3RJ,SAAgB,YAAY,EAAE,MAAM,MAAM,YAA8B;CACtE,MAAM,IAAI,UAAU;CACpB,MAAM,MAAM,OAA2B,KAAK;CAM5C,MAAM,aAAa,cAAc,MAAM,CAFtB,KAAK,UAAU,QAAQ,KAAK,CAEI,CAAC;AAElD,iBAAgB;EACd,MAAM,KAAK,IAAI;AACf,MAAI,CAAC,GAAI;AACT,SAAO,EAAE,eAAe;GAAE;GAAI;GAAM,MAAM;GAAY,CAAC;IACtD;EAAC;EAAG;EAAM;EAAW,CAAC;CAEzB,MAAM,OAAO,SAAS,KAAK,SAAS;AACpC,KAAI,eAAe,KAAK,KAAK,OAAO,KAAK,SAAS,YAAY,gBAAgB,KAAK,GAAG;EACpF,MAAM,WAAY,KAAK,MAA4B;AACnD,SAAO,aAAa,MAAyC,EAC3D,KAAK,YAAY,KAAK,SAAS,EAChC,CAAC;;AAIJ,QACE,oBAAC,QAAD;EACE,MAAM,OAAO;AACX,OAAI,UAAU;;EAEhB,OAAO,EAAE,SAAS,YAAY;YAE7B;EACI;;AAIX,SAAS,gBAAgB,IAA2B;CAIlD,MAAM,IAAI,GAAG;AACb,QAAO,OAAO,MAAM,YAAY,OAAO,EAAE,UAAU,aAAa;;AAGlE,SAAS,YAAe,GAAG,MAAoC;AAC7D,SAAQ,SAAY;AAClB,OAAK,MAAM,KAAK,KACd,KAAI,OAAO,MAAM,WAAY,CAAC,EAAqB,KAAK;WAC/C,KAAK,OAAO,MAAM,YAAY,aAAa,EAClD,CAAC,EAAqB,UAAU"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/provider.tsx","../src/ScreenshotEditor.tsx","../src/UserzTarget.tsx"],"sourcesContent":["import { createUserz, type Userz, type UserzConfig } from '@userz-ai/browser';\nimport {\n createContext,\n type ReactNode,\n useContext,\n useEffect,\n useReducer,\n useRef,\n} from 'react';\n\nconst UserzCtx = createContext<Userz | null>(null);\n\nexport interface UserzProviderProps extends UserzConfig {\n children: ReactNode;\n}\n\n/**\n * React provider that owns one Userz instance for the lifetime of the\n * component tree. Re-renders DON'T tear down the widget — config changes\n * after the first render are ignored (same as Sentry/Datadog SDKs). Use\n * `useUserz()` to call methods (setUser, submit, …) imperatively.\n *\n * StrictMode dev lifecycle is `render → setup → cleanup → setup` (render\n * only fires ONCE per mount cycle). The cleanup destroys the widget and\n * nulls the ref; the second setup re-creates it and forces a re-render so\n * the context value swaps from the destroyed instance to the live one.\n */\nexport function UserzProvider({ children, ...config }: UserzProviderProps) {\n const instance = useRef<Userz | null>(null);\n // Freeze initial config — matches the documented \"ignored after mount\"\n // semantics and gives the StrictMode re-setup something to recreate from.\n const configRef = useRef(config);\n const [, rerender] = useReducer((x: number) => x + 1, 0);\n\n // Initialize once, lazily — and only in the browser. Next.js, Remix, and\n // any other RSC/SSR framework will render this provider on the server to\n // emit the initial HTML; `createUserz` touches `document` immediately so\n // running it server-side crashes the render. The widget has nothing to do\n // during SSR anyway (no bubble to paint, no console to capture), so we\n // defer construction to the client pass.\n if (instance.current === null && typeof document !== 'undefined') {\n instance.current = createUserz(configRef.current);\n }\n\n useEffect(() => {\n // StrictMode remount: the previous cleanup destroyed the widget and\n // nulled the ref, but render doesn't re-fire between cleanup and the\n // next setup. Recreate here, then force a re-render so the context\n // value (currently the destroyed instance) updates for children.\n if (instance.current === null && typeof document !== 'undefined') {\n instance.current = createUserz(configRef.current);\n rerender();\n }\n return () => {\n instance.current?.destroy();\n instance.current = null;\n };\n }, []);\n\n // Pass through identity updates if the parent passes them via prop\n // changes after mount.\n const initialUser = config.initialUser;\n useEffect(() => {\n if (initialUser !== undefined) instance.current?.setUser(initialUser);\n }, [initialUser]);\n\n return <UserzCtx.Provider value={instance.current}>{children}</UserzCtx.Provider>;\n}\n\nexport function useUserz(): Userz {\n const u = useContext(UserzCtx);\n if (!u) throw new Error('useUserz must be used inside <UserzProvider>');\n return u;\n}\n","import { UZ_DEFAULT_BRAND, UZ_DEFAULT_BRAND_FG } from '@userz-ai/browser';\nimport {\n type CSSProperties,\n type ReactNode,\n Suspense,\n useCallback,\n useEffect,\n useState,\n} from 'react';\n\n/**\n * Annotated screenshot editor.\n *\n * Wraps tldraw to give the end-user a quick \"circle the broken thing, type\n * an arrow, save\" workflow before submitting feedback. The tldraw bundle\n * (~600KB) is dynamic-imported on first mount so apps that never open the\n * editor don't pay for it; the component shows a loading state until ready.\n *\n * tldraw is declared as an OPTIONAL peer dep in package.json. Apps that\n * want this component MUST install `tldraw` themselves; without it the\n * component renders an instructive fallback instead of throwing.\n *\n * Usage:\n * <ScreenshotEditor\n * blob={blob}\n * onSave={(annotated) => userz.submit({ ..., attachments: [annotated] })}\n * onCancel={() => setOpen(false)}\n * />\n */\n\nexport interface ScreenshotEditorProps {\n /** PNG/JPEG blob to seed the canvas with. */\n blob: Blob;\n /** Optional dimensions override; default is the blob's natural size. */\n width?: number;\n height?: number;\n /** Receives the annotated PNG. */\n onSave: (annotated: Blob) => void | Promise<void>;\n onCancel?: () => void;\n /** Inline style override on the editor container. */\n style?: CSSProperties;\n className?: string;\n /** Save-button color. Defaults to the Userz brand mint. */\n brandColor?: string;\n /** Save-button text color. Defaults to brand foreground. */\n brandForeground?: string;\n /** Cancel-button surface color. Defaults to white. */\n backgroundColor?: string;\n /** Cancel-button text color. Defaults to near-black. */\n textColor?: string;\n}\n\nexport function ScreenshotEditor(props: ScreenshotEditorProps): ReactNode {\n return (\n <Suspense fallback={<EditorLoadingState />}>\n <LazyEditor {...props} />\n </Suspense>\n );\n}\n\nfunction LazyEditor(props: ScreenshotEditorProps) {\n const [tldraw, setTldraw] = useState<TldrawModule | null | 'missing'>(null);\n\n useEffect(() => {\n let cancelled = false;\n loadTldraw().then(\n (mod) => {\n if (!cancelled) setTldraw(mod ?? 'missing');\n },\n () => {\n if (!cancelled) setTldraw('missing');\n },\n );\n return () => {\n cancelled = true;\n };\n }, []);\n\n if (tldraw === null) return <EditorLoadingState />;\n if (tldraw === 'missing') return <MissingPeerFallback />;\n\n return <TldrawEditor tldraw={tldraw} {...props} />;\n}\n\ninterface TldrawModule {\n Tldraw: (props: Record<string, unknown>) => ReactNode;\n exportAs: (\n editor: unknown,\n shapeIds: unknown[],\n format: string,\n opts?: Record<string, unknown>,\n ) => Promise<Blob>;\n AssetRecordType: {\n createId(): string;\n };\n createShapeId(): string;\n}\n\nasync function loadTldraw(): Promise<TldrawModule | null> {\n try {\n // The string is split with a runtime expression so bundlers don't try\n // to follow the import; tldraw stays out of the build graph entirely\n // for apps that don't install it.\n const name = ['tld', 'raw'].join('');\n const mod = (await import(/* @vite-ignore */ name)) as Partial<TldrawModule>;\n if (!mod.Tldraw) return null;\n return mod as TldrawModule;\n } catch {\n return null;\n }\n}\n\ninterface TldrawEditorProps extends ScreenshotEditorProps {\n tldraw: TldrawModule;\n}\n\nfunction TldrawEditor({\n tldraw,\n blob,\n width,\n height,\n onSave,\n onCancel,\n style,\n className,\n brandColor = UZ_DEFAULT_BRAND,\n brandForeground = UZ_DEFAULT_BRAND_FG,\n backgroundColor = 'white',\n textColor = '#111',\n}: TldrawEditorProps) {\n const [editor, setEditor] = useState<unknown>(null);\n const [saving, setSaving] = useState(false);\n\n const onMount = useCallback(\n async (ed: unknown) => {\n setEditor(ed);\n const url = URL.createObjectURL(blob);\n try {\n const dims = width && height ? { w: width, h: height } : await measureBlob(blob);\n const assetId = tldraw.AssetRecordType.createId();\n const shapeId = tldraw.createShapeId();\n const editorAny = ed as {\n createAssets: (assets: unknown[]) => void;\n createShape: (s: unknown) => void;\n };\n editorAny.createAssets([\n {\n id: assetId,\n type: 'image',\n typeName: 'asset',\n props: {\n name: 'screenshot.png',\n src: url,\n w: dims.w,\n h: dims.h,\n mimeType: blob.type || 'image/png',\n isAnimated: false,\n },\n meta: {},\n },\n ]);\n editorAny.createShape({\n id: shapeId,\n type: 'image',\n x: 0,\n y: 0,\n props: { assetId, w: dims.w, h: dims.h },\n });\n } catch {\n // best-effort seeding; user can still draw on a blank canvas\n }\n },\n [tldraw, blob, width, height],\n );\n\n const handleSave = useCallback(async () => {\n if (!editor || saving) return;\n setSaving(true);\n try {\n const editorAny = editor as { getCurrentPageShapeIds(): Set<unknown> };\n const ids = Array.from(editorAny.getCurrentPageShapeIds());\n const png = await tldraw.exportAs(editor, ids, 'png', { background: false });\n await onSave(png);\n } finally {\n setSaving(false);\n }\n }, [editor, saving, tldraw, onSave]);\n\n const TldrawCmp = tldraw.Tldraw as unknown as (p: {\n onMount: (ed: unknown) => void;\n persistenceKey?: string;\n }) => ReactNode;\n\n return (\n <div\n className={className}\n style={{\n position: 'relative',\n width: '100%',\n height: '60vh',\n minHeight: 360,\n ...style,\n }}\n >\n <TldrawCmp onMount={onMount} />\n <div\n style={{\n position: 'absolute',\n right: 12,\n bottom: 12,\n display: 'flex',\n gap: 8,\n zIndex: 1000,\n }}\n >\n {onCancel && (\n <button\n type=\"button\"\n onClick={onCancel}\n style={editorButtonStyle('ghost', brandColor, brandForeground, backgroundColor, textColor)}\n disabled={saving}\n >\n Cancel\n </button>\n )}\n <button\n type=\"button\"\n onClick={handleSave}\n style={editorButtonStyle('primary', brandColor, brandForeground, backgroundColor, textColor)}\n disabled={saving || !editor}\n >\n {saving ? 'Saving…' : 'Save annotation'}\n </button>\n </div>\n </div>\n );\n}\n\nfunction EditorLoadingState() {\n return (\n <div style={loadingStyle}>\n <span>Loading editor…</span>\n </div>\n );\n}\n\nfunction MissingPeerFallback() {\n return (\n <div style={loadingStyle}>\n <div style={{ maxWidth: 360, textAlign: 'center', lineHeight: 1.45 }}>\n <strong>Screenshot annotation unavailable.</strong>\n <div style={{ marginTop: 8, fontSize: 13, opacity: 0.8 }}>\n Install <code>tldraw</code> in your app to enable the in-widget editor:\n <pre\n style={{\n marginTop: 8,\n padding: 8,\n background: 'rgba(0,0,0,0.05)',\n borderRadius: 6,\n fontSize: 12,\n }}\n >\n pnpm add tldraw\n </pre>\n </div>\n </div>\n </div>\n );\n}\n\nconst loadingStyle: CSSProperties = {\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n width: '100%',\n height: '60vh',\n minHeight: 360,\n border: '1px dashed rgba(0,0,0,0.15)',\n borderRadius: 8,\n fontSize: 14,\n color: '#555',\n};\n\nfunction editorButtonStyle(\n kind: 'primary' | 'ghost',\n brandColor: string,\n brandForeground: string,\n backgroundColor: string,\n textColor: string,\n): CSSProperties {\n if (kind === 'primary') {\n return {\n padding: '8px 14px',\n borderRadius: 6,\n border: 'none',\n background: brandColor,\n color: brandForeground,\n fontWeight: 600,\n cursor: 'pointer',\n boxShadow: '0 4px 12px rgba(0,0,0,0.18)',\n };\n }\n return {\n padding: '8px 14px',\n borderRadius: 6,\n border: '1px solid rgba(0,0,0,0.18)',\n background: backgroundColor,\n color: textColor,\n cursor: 'pointer',\n };\n}\n\nasync function measureBlob(blob: Blob): Promise<{ w: number; h: number }> {\n return new Promise((resolve, reject) => {\n const img = new Image();\n const url = URL.createObjectURL(blob);\n img.onload = () => {\n URL.revokeObjectURL(url);\n resolve({ w: img.naturalWidth, h: img.naturalHeight });\n };\n img.onerror = (err) => {\n URL.revokeObjectURL(url);\n reject(err);\n };\n img.src = url;\n });\n}\n","import {\n Children,\n cloneElement,\n isValidElement,\n type ReactElement,\n type ReactNode,\n useEffect,\n useMemo,\n useRef,\n} from 'react';\nimport { useUserz } from './provider';\n\nexport interface UserzTargetProps {\n /** Stable display name for this target. Surfaces in feedback as the\n * \"component the user clicked on\" label. */\n name: string;\n /** Opt-in metadata you want forwarded with feedback. Keep small + non-sensitive. */\n meta?: Record<string, unknown>;\n /** A single child element (no fragments). We attach a ref to it without\n * a wrapper div so layout is unchanged. */\n children: ReactNode;\n}\n\n/**\n * Wraps a child element to mark it as a \"feedback target\" the end-user can\n * click on while the targeting overlay is active (Ctrl+Shift+U by default).\n *\n * Implementation: clones the single child, attaches a ref, and registers/\n * unregisters with the Userz instance over the child's lifecycle. We do NOT\n * inject a wrapper div — that would break flex/grid layouts. As a result,\n * the child must accept a `ref`; for most DOM elements and `forwardRef`\n * components this is fine. For function components without forwardRef, we\n * fall back to a `display: contents` wrapper.\n */\nexport function UserzTarget({ name, meta, children }: UserzTargetProps) {\n const u = useUserz();\n const ref = useRef<HTMLElement | null>(null);\n // Stable meta identity so the effect only re-runs when shape actually changes.\n // The dep is intentionally the JSON string, not `meta`, so callers passing a\n // fresh object literal each render don't thrash the registration.\n const metaJson = JSON.stringify(meta ?? null);\n // biome-ignore lint/correctness/useExhaustiveDependencies: see above\n const stableMeta = useMemo(() => meta, [metaJson]);\n\n useEffect(() => {\n const el = ref.current;\n if (!el) return;\n return u.registerTarget({ el, name, meta: stableMeta });\n }, [u, name, stableMeta]);\n\n const only = Children.only(children);\n if (isValidElement(only) && (typeof only.type === 'string' || hasForwardedRef(only))) {\n const original = (only.props as { ref?: unknown }).ref;\n return cloneElement(only as ReactElement<{ ref?: unknown }>, {\n ref: composeRefs(ref, original),\n });\n }\n // Function component without forwardRef — use a `display: contents` span\n // so we can attach a ref without affecting layout.\n return (\n <span\n ref={(el) => {\n ref.current = el;\n }}\n style={{ display: 'contents' }}\n >\n {only}\n </span>\n );\n}\n\nfunction hasForwardedRef(el: ReactElement): boolean {\n // Conservative heuristic — React.forwardRef components have a \"$$typeof\"\n // marker on their type. We can't import it portably across React versions,\n // so we check the symbol indirectly.\n const t = el.type as unknown as { $$typeof?: symbol };\n return typeof t === 'object' && typeof t.$$typeof?.toString === 'function';\n}\n\nfunction composeRefs<T>(...refs: unknown[]): (node: T) => void {\n return (node: T) => {\n for (const r of refs) {\n if (typeof r === 'function') (r as (n: T) => void)(node);\n else if (r && typeof r === 'object' && 'current' in r) {\n (r as { current: T }).current = node;\n }\n }\n };\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@userz-ai/react",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.1",
|
|
4
4
|
"description": "React bindings for the Userz feedback widget.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"peerDependencies": {
|
|
24
24
|
"react": "^18.0.0 || ^19.0.0",
|
|
25
25
|
"tldraw": "^4.0.0",
|
|
26
|
-
"@userz-ai/browser": "0.
|
|
26
|
+
"@userz-ai/browser": "0.3.1"
|
|
27
27
|
},
|
|
28
28
|
"peerDependenciesMeta": {
|
|
29
29
|
"tldraw": {
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"tldraw": "^4.5.9",
|
|
38
38
|
"tsdown": "^0.21.9",
|
|
39
39
|
"typescript": "^6.0.3",
|
|
40
|
-
"@userz-ai/browser": "0.
|
|
40
|
+
"@userz-ai/browser": "0.3.1"
|
|
41
41
|
},
|
|
42
42
|
"scripts": {
|
|
43
43
|
"build": "tsdown",
|