@citewidget/react 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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 CiteWidget
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 CHANGED
@@ -1,3 +1,93 @@
1
- # Temporary Holding Version
1
+ # @citewidget/react
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ The CiteWidget chat for React and Next.js: visitors ask questions and get answers from your
4
+ documents, with links to the pages they came from. React 18 or 19.
5
+
6
+ ```sh
7
+ npm install @citewidget/react
8
+ ```
9
+
10
+ This package is ES modules only (no CommonJS build): use `import`, not `require()`. Every current bundler and Node 22+ handle that; with an older CommonJS-only setup, load the script tag version instead.
11
+
12
+ ## Quick start
13
+
14
+ ```tsx
15
+ import { CiteWidget } from "@citewidget/react";
16
+
17
+ export function App() {
18
+ return (
19
+ <>
20
+ {/* your app */}
21
+ <CiteWidget workspaceKey="pk_..." primaryColor="#0F766E" />
22
+ </>
23
+ );
24
+ }
25
+ ```
26
+
27
+ `<CiteWidget>` renders nothing where you place it: after mounting it adds the chat as a fixed
28
+ overlay at the end of `<body>`. Put it once, high in the tree (a layout), so it stays mounted
29
+ across pages.
30
+
31
+ ### Next.js (App Router)
32
+
33
+ The package is marked `"use client"`, so a server component such as `app/layout.tsx` can render
34
+ it directly. Nothing touches `window` or `document` during server rendering.
35
+
36
+ ```tsx
37
+ // app/layout.tsx
38
+ import { CiteWidget } from "@citewidget/react";
39
+
40
+ export default function RootLayout({ children }: { children: React.ReactNode }) {
41
+ return (
42
+ <html lang="en">
43
+ <body>
44
+ {children}
45
+ <CiteWidget workspaceKey={process.env.NEXT_PUBLIC_CITEWIDGET_KEY!} />
46
+ </body>
47
+ </html>
48
+ );
49
+ }
50
+ ```
51
+
52
+ The workspace key is public (it starts with `pk_`), so a `NEXT_PUBLIC_` variable is fine.
53
+
54
+ ## Your own button
55
+
56
+ ```tsx
57
+ "use client";
58
+ import { useCiteWidget } from "@citewidget/react";
59
+
60
+ export function AskButton() {
61
+ const widget = useCiteWidget();
62
+ return <button onClick={widget.open}>Ask about this product</button>;
63
+ }
64
+ ```
65
+
66
+ `useCiteWidget()` returns stable `open`, `close`, `toggle` and `newChat` functions for the
67
+ mounted `<CiteWidget>` (the newest one if there are several). Combine it with
68
+ `showLauncher={false}` to hide the round button. Calling them while no widget is mounted logs a
69
+ warning and does nothing.
70
+
71
+ ## Props
72
+
73
+ Every option of `createCiteWidget` from
74
+ [`@citewidget/widget`](https://www.npmjs.com/package/@citewidget/widget) is a prop:
75
+ `workspaceKey` (required), `apiUrl`, `serverUrl`, `loadDashboardSettings`, `title`, `greeting`,
76
+ `botName`, `primaryColor`, `surfaceColor`, `colorScheme`, `position`, `showLauncher`,
77
+ `launcherLabel`, `launcherIcon`, `launcherIconUrl`, `softRefusal`, `goodbye`, `suggestions`,
78
+ `handoff`, `handoffReplyTime`, `leadCapture`, `chatDurationMinutes`, `nonce`, and the callbacks
79
+ `onOpen`, `onClose`, `onHandoff` (called with no arguments). See that package for types, limits
80
+ and defaults.
81
+
82
+ - Settings combine as: built-in defaults < your CiteWidget dashboard < props. Removing a prop
83
+ hands that setting back to the dashboard.
84
+ - Changing props updates the widget; it is rebuilt only when something visible changed (a new
85
+ array with the same questions, or a new callback function, changes nothing). Callbacks always
86
+ use your latest props.
87
+ - Under React Strict Mode's double mount you still get exactly one widget.
88
+ - A missing or invalid `workspaceKey` is logged with `console.error` and nothing is shown; other
89
+ invalid values are ignored with a console warning.
90
+
91
+ ## Licence
92
+
93
+ MIT. See [LICENSE](./LICENSE).
@@ -0,0 +1,8 @@
1
+ import { type CiteWidgetOptions } from "@citewidget/widget";
2
+ /** Every createCiteWidget() option as a prop; workspaceKey is required. */
3
+ export type CiteWidgetProps = CiteWidgetOptions;
4
+ /**
5
+ * The CiteWidget chat. Renders nothing in place: the widget is a fixed overlay appended to
6
+ * <body> after mount, so it is safe in server rendering and under Strict Mode.
7
+ */
8
+ export declare function CiteWidget(props: CiteWidgetProps): null;
@@ -0,0 +1,7 @@
1
+ /**
2
+ * @citewidget/react: the CiteWidget chat for React and Next.js (App Router: the build keeps
3
+ * "use client"; nothing touches window or document during server rendering).
4
+ */
5
+ export { CiteWidget, type CiteWidgetProps } from "./CiteWidget";
6
+ export { useCiteWidget, type CiteWidgetControls } from "./useCiteWidget";
7
+ export type { CiteWidgetColorScheme, CiteWidgetLauncherIcon, CiteWidgetOptions, CiteWidgetPosition, } from "@citewidget/widget";
package/dist/index.js ADDED
@@ -0,0 +1,73 @@
1
+ "use client";
2
+
3
+ // src/CiteWidget.ts
4
+ import { useEffect, useRef } from "react";
5
+ import { createCiteWidget } from "@citewidget/widget";
6
+
7
+ // src/registry.ts
8
+ var mounted = [];
9
+ function registerWidget(widget) {
10
+ return mounted = [...mounted, widget], () => {
11
+ mounted = mounted.filter((entry) => entry !== widget);
12
+ };
13
+ }
14
+ function currentWidget() {
15
+ return mounted[mounted.length - 1] ?? null;
16
+ }
17
+
18
+ // src/CiteWidget.ts
19
+ var CALLBACKS = /* @__PURE__ */ new Set(["onOpen", "onClose", "onHandoff"]);
20
+ function dataOptions(props) {
21
+ return Object.fromEntries(Object.entries(props).filter(([key]) => !CALLBACKS.has(key)));
22
+ }
23
+ function patchFrom(next, previous) {
24
+ return { ...Object.fromEntries(Object.keys(previous).map((key) => [key, void 0])), ...next };
25
+ }
26
+ function CiteWidget(props) {
27
+ let latest = useRef(props), widget = useRef(null), unregister = useRef(() => {
28
+ }), sent = useRef({ workspaceKey: props.workspaceKey }), sentKey = useRef(""), optionsKey = JSON.stringify(dataOptions(props));
29
+ return useEffect(() => {
30
+ latest.current = props;
31
+ }), useEffect(
32
+ () => () => {
33
+ unregister.current(), widget.current?.destroy(), widget.current = null, sentKey.current = "";
34
+ },
35
+ []
36
+ ), useEffect(() => {
37
+ if (optionsKey === sentKey.current) return;
38
+ let next = dataOptions(latest.current);
39
+ try {
40
+ widget.current ? widget.current.update(patchFrom(next, sent.current)) : (widget.current = createCiteWidget({
41
+ ...next,
42
+ onOpen: () => latest.current.onOpen?.(),
43
+ onClose: () => latest.current.onClose?.(),
44
+ onHandoff: () => latest.current.onHandoff?.()
45
+ }), unregister.current = registerWidget(widget.current)), sent.current = next, sentKey.current = optionsKey;
46
+ } catch (error) {
47
+ console.error(error instanceof Error ? error.message : error);
48
+ }
49
+ }, [optionsKey]), null;
50
+ }
51
+
52
+ // src/useCiteWidget.ts
53
+ function call(method) {
54
+ let widget = currentWidget();
55
+ if (widget) {
56
+ widget[method]();
57
+ return;
58
+ }
59
+ console.warn(`[CiteWidget] ${method}() was called, but no <CiteWidget> is mounted.`);
60
+ }
61
+ var controls = Object.freeze({
62
+ open: () => call("open"),
63
+ close: () => call("close"),
64
+ toggle: () => call("toggle"),
65
+ newChat: () => call("newChat")
66
+ });
67
+ function useCiteWidget() {
68
+ return controls;
69
+ }
70
+ export {
71
+ CiteWidget,
72
+ useCiteWidget
73
+ };
@@ -0,0 +1,10 @@
1
+ /**
2
+ * The widgets <CiteWidget> has mounted, newest last, so useCiteWidget() can reach the current one
3
+ * from anywhere in the tree without a provider. Written only from effects (browser), so server
4
+ * rendering never sees or shares it.
5
+ */
6
+ import type { CiteWidgetController } from "@citewidget/widget";
7
+ /** Adds a widget; the returned function removes it again. */
8
+ export declare function registerWidget(widget: CiteWidgetController): () => void;
9
+ /** The most recently mounted widget, or null. */
10
+ export declare function currentWidget(): CiteWidgetController | null;
@@ -0,0 +1,13 @@
1
+ /** Controls for the mounted <CiteWidget>; stable across renders, safe to pass to onClick. */
2
+ export interface CiteWidgetControls {
3
+ open(): void;
4
+ close(): void;
5
+ toggle(): void;
6
+ /** Clears the conversation. */
7
+ newChat(): void;
8
+ }
9
+ /**
10
+ * Opens, closes or resets the mounted <CiteWidget> from your own UI (for example a "Ask about
11
+ * this product" button with showLauncher={false}). With several mounted, the newest one wins.
12
+ */
13
+ export declare function useCiteWidget(): CiteWidgetControls;
package/package.json CHANGED
@@ -1,6 +1,42 @@
1
1
  {
2
2
  "name": "@citewidget/react",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.0",
4
+ "description": "CiteWidget chat for React and Next.js: <CiteWidget workspaceKey=\"pk_...\" /> and useCiteWidget().",
5
+ "type": "module",
6
+ "sideEffects": false,
7
+ "exports": {
8
+ ".": {
9
+ "types": "./dist/index.d.ts",
10
+ "import": "./dist/index.js"
11
+ },
12
+ "./package.json": "./package.json"
13
+ },
14
+ "module": "./dist/index.js",
15
+ "types": "./dist/index.d.ts",
16
+ "files": [
17
+ "dist"
18
+ ],
19
+ "keywords": [
20
+ "citewidget",
21
+ "react",
22
+ "nextjs",
23
+ "chat-widget"
24
+ ],
25
+ "homepage": "https://citewidget.com",
26
+ "license": "MIT",
27
+ "publishConfig": {
28
+ "access": "public"
29
+ },
30
+ "dependencies": {
31
+ "@citewidget/widget": "0.1.0"
32
+ },
33
+ "peerDependencies": {
34
+ "react": ">=18"
35
+ },
36
+ "scripts": {
37
+ "build": "node build.mjs",
38
+ "prepack": "node build.mjs",
39
+ "typecheck": "tsc -p tsconfig.json",
40
+ "test": "vitest run"
41
+ }
42
+ }