@paramms/chat-widget 1.0.0 → 1.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/dist/index.js +359 -318
- package/dist/index.js.map +1 -1
- package/dist/react.d.ts +20 -5
- package/dist/react.js +6 -0
- package/dist/react.js.map +1 -1
- package/dist/renderer.d.ts +4 -0
- package/package.json +10 -4
package/dist/react.d.ts
CHANGED
|
@@ -85,25 +85,40 @@ export interface MarketplaceChatProps extends BaseProps {
|
|
|
85
85
|
listingStatus?: string;
|
|
86
86
|
}
|
|
87
87
|
/**
|
|
88
|
-
* Marketplace chat
|
|
89
|
-
* their active threads via a WhatsApp-style list. Only `url` and `profileId`
|
|
90
|
-
* are required. All other props are optional.
|
|
88
|
+
* Marketplace chat widget. Two modes depending on whether `listingId` is provided:
|
|
91
89
|
*
|
|
92
|
-
*
|
|
90
|
+
* **With `listingId` (listing page)** — opens that listing's chat immediately.
|
|
91
|
+
* No list screen. The buyer is already looking at the item so context is clear.
|
|
93
92
|
* ```tsx
|
|
93
|
+
* // On your listing detail page:
|
|
94
94
|
* <MarketplaceChat
|
|
95
95
|
* url={process.env.NEXT_PUBLIC_RELAY_WS_URL}
|
|
96
96
|
* profileId={process.env.NEXT_PUBLIC_RELAY_PROFILE_ID}
|
|
97
97
|
* listingId={car.id}
|
|
98
98
|
* listingTitle={car.title}
|
|
99
99
|
* listingPrice={car.price}
|
|
100
|
-
* listingMeta={`${car.mileage.toLocaleString()} km · ${car.
|
|
100
|
+
* listingMeta={`${car.mileage.toLocaleString()} km · ${car.gearbox}`}
|
|
101
101
|
* listingStatus="Available"
|
|
102
102
|
* userId={session?.user.id}
|
|
103
103
|
* userName={session?.user.name}
|
|
104
104
|
* userEmail={session?.user.email}
|
|
105
105
|
* />
|
|
106
106
|
* ```
|
|
107
|
+
*
|
|
108
|
+
* **Without `listingId` (My Messages / inbox page)** — shows the WhatsApp-style
|
|
109
|
+
* thread list as the home screen. No conversation is opened until the user taps one.
|
|
110
|
+
* Use this on a dedicated `/messages` or `/inbox` page.
|
|
111
|
+
* ```tsx
|
|
112
|
+
* // On your /messages page:
|
|
113
|
+
* <MarketplaceChat
|
|
114
|
+
* url={process.env.NEXT_PUBLIC_RELAY_WS_URL}
|
|
115
|
+
* profileId={process.env.NEXT_PUBLIC_RELAY_PROFILE_ID}
|
|
116
|
+
* userId={session?.user.id}
|
|
117
|
+
* userName={session?.user.name}
|
|
118
|
+
* />
|
|
119
|
+
* ```
|
|
120
|
+
*
|
|
121
|
+
* Only `url` and `profileId` are required. All other props are optional.
|
|
107
122
|
*/
|
|
108
123
|
export declare function MarketplaceChat({ url, profileId, listingId, listingTitle, listingMeta, listingPrice, listingStatus, userId, userName, userEmail, userAvatar, accent, launcher, position, quickReplies, height, i18n, translateLang, }: MarketplaceChatProps): JSX.Element;
|
|
109
124
|
export {};
|
package/dist/react.js
CHANGED
|
@@ -94,6 +94,9 @@ function K({
|
|
|
94
94
|
el: e.current,
|
|
95
95
|
url: l,
|
|
96
96
|
profileId: m,
|
|
97
|
+
// ── Listing context ────────────────────────────────────────────────────
|
|
98
|
+
// When listingId is present: open that listing's chat directly (no list).
|
|
99
|
+
// When listingId is absent: show the thread list (dedicated inbox page).
|
|
97
100
|
...t ? { subjectId: `listing_${t}` } : {},
|
|
98
101
|
...o ? { token: o } : {},
|
|
99
102
|
...o || h || b || n ? {
|
|
@@ -112,6 +115,9 @@ function K({
|
|
|
112
115
|
...p ? { status: p } : {}
|
|
113
116
|
}
|
|
114
117
|
} : {},
|
|
118
|
+
// showChatList is true in both cases but behaves differently:
|
|
119
|
+
// • With subjectId → chat screen shown immediately on open (in index.ts)
|
|
120
|
+
// • Without subjectId → list screen is home (the dedicated inbox case)
|
|
115
121
|
showChatList: !0,
|
|
116
122
|
quickReplies: L ?? F,
|
|
117
123
|
...C ? { accent: C } : {},
|
package/dist/react.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"react.js","sources":["../src/react.tsx"],"sourcesContent":["// react.tsx — React wrapper around mount().\n//\n// Next.js App Router: add 'use client' to whichever file imports these.\n// The widget needs WebSocket + DOM — it cannot render on the server.\n//\n// Import path: '@paramms/chat-widget/react'\n//\n// Two components:\n// ChatWidget — general support chat (hotel, SaaS, helpdesk, etc.)\n// MarketplaceChat — one thread per listing (used cars, rentals, etc.)\n//\n// Both: only url + profileId are required. userId is always optional —\n// anonymous guests are handled automatically via localStorage.\n\nimport { useEffect, useRef } from 'react'\nimport { mount, type MountOptions, type WidgetHandle } from './index.js'\n\n// ── Shared base props ─────────────────────────────────────────────────────────\n\ninterface BaseProps {\n /** Relay WebSocket URL — set as NEXT_PUBLIC_RELAY_WS_URL in .env — required */\n url: string\n /** Your Relay profile ID — set as NEXT_PUBLIC_RELAY_PROFILE_ID in .env — required */\n profileId: string\n\n /** Your logged-in user's stable ID. When omitted the widget automatically\n * assigns a persistent anonymous ID from localStorage — no login required. */\n userId?: string\n /** Shown to agents instead of the raw user ID */\n userName?: string\n /** Shown to agents so they can follow up by email */\n userEmail?: string\n /** Avatar URL shown in the widget header and dashboard */\n userAvatar?: string\n\n /** Brand colour hex, e.g. \"#1a56db\". Defaults to the profile theme colour. */\n accent?: string\n /** Render as a floating launcher button instead of inline */\n launcher?: boolean\n /** Launcher position — default 'bottom-right' */\n position?: 'bottom-right' | 'bottom-left'\n /** Pre-set reply chips shown above the input */\n quickReplies?: string[]\n /** Container height when rendered inline. Default: '100%' */\n height?: string\n /** i18n string overrides for non-English sites */\n i18n?: MountOptions['i18n']\n /** ISO language code for auto-translating incoming messages */\n translateLang?: string\n}\n\n// ── ChatWidget ────────────────────────────────────────────────────────────────\n\nexport interface ChatWidgetProps extends BaseProps {\n /** Optional context card shown at the top of the chat\n * (e.g. the support ticket, booking, or order being discussed) */\n contextTitle?: string\n contextSubtitle?: string\n contextStatus?: string\n\n /** Show the WhatsApp-style thread list as the home screen.\n * Use when users can have more than one thread on this profile\n * (e.g. one thread per support ticket, one per booking). */\n showChatList?: boolean\n\n /** Stable item ID — pins this conversation to a specific item/thread.\n * When showChatList is also true, the widget opens this thread immediately\n * but the user can navigate back to the full list. */\n subjectId?: string\n}\n\n/**\n * General-purpose support chat widget. Only `url` and `profileId` are required.\n * All other props are optional — anonymous guests work without any configuration.\n *\n * @example Basic support chat\n * ```tsx\n * <ChatWidget\n * url={process.env.NEXT_PUBLIC_RELAY_WS_URL}\n * profileId={process.env.NEXT_PUBLIC_RELAY_PROFILE_ID}\n * userId={session?.user.id}\n * userName={session?.user.name}\n * userEmail={session?.user.email}\n * />\n * ```\n *\n * @example Multi-thread (tickets, bookings, orders)\n * ```tsx\n * <ChatWidget\n * url={...} profileId={...}\n * showChatList\n * subjectId={`ticket_${ticket.id}`}\n * contextTitle={ticket.title}\n * contextStatus={ticket.status}\n * userId={session?.user.id}\n * />\n * ```\n */\nexport function ChatWidget({\n url, profileId,\n userId, userName, userEmail, userAvatar,\n contextTitle, contextSubtitle, contextStatus,\n showChatList, subjectId,\n accent, launcher, position, quickReplies, height = '100%',\n i18n, translateLang,\n}: ChatWidgetProps): JSX.Element {\n const ref = useRef<HTMLDivElement>(null)\n const handleRef = useRef<WidgetHandle | null>(null)\n\n useEffect(() => {\n if (!ref.current) return\n handleRef.current = mount({\n el: ref.current,\n url,\n profileId,\n ...(subjectId ? { subjectId } : {}),\n ...(userId ? { token: userId } : {}),\n ...(userId || userName || userEmail || userAvatar ? {\n user: {\n ...(userName ? { name: userName } : {}),\n ...(userEmail ? { email: userEmail } : {}),\n ...(userAvatar ? { avatar: userAvatar } : {}),\n },\n } : {}),\n ...(contextTitle ? {\n subject: {\n title: contextTitle,\n ...(contextSubtitle ? { subtitle: contextSubtitle } : {}),\n ...(contextStatus ? { status: contextStatus } : {}),\n },\n } : {}),\n ...(showChatList ? { showChatList: true } : {}),\n ...(quickReplies ? { quickReplies } : {}),\n ...(accent ? { accent } : {}),\n ...(translateLang ? { translateLang } : {}),\n ...(i18n ? { i18n } : {}),\n ...(launcher ? { launcher, position: position ?? 'bottom-right' } : {}),\n })\n return () => { handleRef.current?.close(); handleRef.current = null }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [url, profileId, subjectId, userId])\n\n if (launcher) return <></>\n return (\n <div\n ref={ref}\n style={{ width: '100%', height, minHeight: height === '100%' ? '480px' : undefined }}\n />\n )\n}\n\n// ── MarketplaceChat ───────────────────────────────────────────────────────────\n\nexport interface MarketplaceChatProps extends BaseProps {\n /** Unique ID for this listing — each listing gets its own thread.\n * Omit for a general (non-item-specific) conversation. */\n listingId?: string\n /** Shown at the top of the chat — e.g. \"2019 Toyota Camry SE\" */\n listingTitle?: string\n /** One-line detail — e.g. \"45,000 km · Automatic\" */\n listingMeta?: string\n /** Price shown as a tag — e.g. 12500 → \"$12,500\" */\n listingPrice?: number\n /** Status badge — e.g. \"Available\", \"Sold\", \"Pending\" */\n listingStatus?: string\n}\n\nconst DEFAULT_MARKETPLACE_REPLIES = [\n 'Is this still available?',\n 'Can I schedule a viewing?',\n 'What is your best price?',\n]\n\n/**\n * Marketplace chat — one thread per listing, guests can switch between all\n * their active threads via a WhatsApp-style list. Only `url` and `profileId`\n * are required. All other props are optional.\n *\n * @example Used car listing\n * ```tsx\n * <MarketplaceChat\n * url={process.env.NEXT_PUBLIC_RELAY_WS_URL}\n * profileId={process.env.NEXT_PUBLIC_RELAY_PROFILE_ID}\n * listingId={car.id}\n * listingTitle={car.title}\n * listingPrice={car.price}\n * listingMeta={`${car.mileage.toLocaleString()} km · ${car.transmission}`}\n * listingStatus=\"Available\"\n * userId={session?.user.id}\n * userName={session?.user.name}\n * userEmail={session?.user.email}\n * />\n * ```\n */\nexport function MarketplaceChat({\n url, profileId,\n listingId, listingTitle, listingMeta, listingPrice, listingStatus,\n userId, userName, userEmail, userAvatar,\n accent, launcher, position, quickReplies, height = '100%',\n i18n, translateLang,\n}: MarketplaceChatProps): JSX.Element {\n const ref = useRef<HTMLDivElement>(null)\n const handleRef = useRef<WidgetHandle | null>(null)\n\n useEffect(() => {\n if (!ref.current) return\n handleRef.current = mount({\n el: ref.current,\n url,\n profileId,\n ...(listingId ? { subjectId: `listing_${listingId}` } : {}),\n ...(userId ? { token: userId } : {}),\n ...(userId || userName || userEmail || userAvatar ? {\n user: {\n ...(userName ? { name: userName } : {}),\n ...(userEmail ? { email: userEmail } : {}),\n ...(userAvatar ? { avatar: userAvatar } : {}),\n ...(listingId ? { meta: { listingId } } : {}),\n },\n } : {}),\n ...(listingTitle ? {\n subject: {\n title: listingTitle,\n ...(listingMeta ? { subtitle: listingMeta } : {}),\n ...(listingPrice ? { tags: [`$${listingPrice.toLocaleString()}`] } : {}),\n ...(listingStatus ? { status: listingStatus } : {}),\n },\n } : {}),\n showChatList: true,\n quickReplies: quickReplies ?? DEFAULT_MARKETPLACE_REPLIES,\n ...(accent ? { accent } : {}),\n ...(translateLang ? { translateLang } : {}),\n ...(i18n ? { i18n } : {}),\n ...(launcher ? { launcher, position: position ?? 'bottom-right' } : {}),\n })\n return () => { handleRef.current?.close(); handleRef.current = null }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [url, profileId, listingId, userId])\n\n if (launcher) return <></>\n return (\n <div\n ref={ref}\n style={{ width: '100%', height, minHeight: height === '100%' ? '480px' : undefined }}\n />\n )\n}\n"],"names":["ChatWidget","url","profileId","userId","userName","userEmail","userAvatar","contextTitle","contextSubtitle","contextStatus","showChatList","subjectId","accent","launcher","position","quickReplies","height","i18n","translateLang","ref","useRef","handleRef","useEffect","mount","_a","jsx","Fragment","DEFAULT_MARKETPLACE_REPLIES","MarketplaceChat","listingId","listingTitle","listingMeta","listingPrice","listingStatus"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"react.js","sources":["../src/react.tsx"],"sourcesContent":["// react.tsx — React wrapper around mount().\n//\n// Next.js App Router: add 'use client' to whichever file imports these.\n// The widget needs WebSocket + DOM — it cannot render on the server.\n//\n// Import path: '@paramms/chat-widget/react'\n//\n// Two components:\n// ChatWidget — general support chat (hotel, SaaS, helpdesk, etc.)\n// MarketplaceChat — one thread per listing (used cars, rentals, etc.)\n//\n// Both: only url + profileId are required. userId is always optional —\n// anonymous guests are handled automatically via localStorage.\n\nimport { useEffect, useRef } from 'react'\nimport { mount, type MountOptions, type WidgetHandle } from './index.js'\n\n// ── Shared base props ─────────────────────────────────────────────────────────\n\ninterface BaseProps {\n /** Relay WebSocket URL — set as NEXT_PUBLIC_RELAY_WS_URL in .env — required */\n url: string\n /** Your Relay profile ID — set as NEXT_PUBLIC_RELAY_PROFILE_ID in .env — required */\n profileId: string\n\n /** Your logged-in user's stable ID. When omitted the widget automatically\n * assigns a persistent anonymous ID from localStorage — no login required. */\n userId?: string\n /** Shown to agents instead of the raw user ID */\n userName?: string\n /** Shown to agents so they can follow up by email */\n userEmail?: string\n /** Avatar URL shown in the widget header and dashboard */\n userAvatar?: string\n\n /** Brand colour hex, e.g. \"#1a56db\". Defaults to the profile theme colour. */\n accent?: string\n /** Render as a floating launcher button instead of inline */\n launcher?: boolean\n /** Launcher position — default 'bottom-right' */\n position?: 'bottom-right' | 'bottom-left'\n /** Pre-set reply chips shown above the input */\n quickReplies?: string[]\n /** Container height when rendered inline. Default: '100%' */\n height?: string\n /** i18n string overrides for non-English sites */\n i18n?: MountOptions['i18n']\n /** ISO language code for auto-translating incoming messages */\n translateLang?: string\n}\n\n// ── ChatWidget ────────────────────────────────────────────────────────────────\n\nexport interface ChatWidgetProps extends BaseProps {\n /** Optional context card shown at the top of the chat\n * (e.g. the support ticket, booking, or order being discussed) */\n contextTitle?: string\n contextSubtitle?: string\n contextStatus?: string\n\n /** Show the WhatsApp-style thread list as the home screen.\n * Use when users can have more than one thread on this profile\n * (e.g. one thread per support ticket, one per booking). */\n showChatList?: boolean\n\n /** Stable item ID — pins this conversation to a specific item/thread.\n * When showChatList is also true, the widget opens this thread immediately\n * but the user can navigate back to the full list. */\n subjectId?: string\n}\n\n/**\n * General-purpose support chat widget. Only `url` and `profileId` are required.\n * All other props are optional — anonymous guests work without any configuration.\n *\n * @example Basic support chat\n * ```tsx\n * <ChatWidget\n * url={process.env.NEXT_PUBLIC_RELAY_WS_URL}\n * profileId={process.env.NEXT_PUBLIC_RELAY_PROFILE_ID}\n * userId={session?.user.id}\n * userName={session?.user.name}\n * userEmail={session?.user.email}\n * />\n * ```\n *\n * @example Multi-thread (tickets, bookings, orders)\n * ```tsx\n * <ChatWidget\n * url={...} profileId={...}\n * showChatList\n * subjectId={`ticket_${ticket.id}`}\n * contextTitle={ticket.title}\n * contextStatus={ticket.status}\n * userId={session?.user.id}\n * />\n * ```\n */\nexport function ChatWidget({\n url, profileId,\n userId, userName, userEmail, userAvatar,\n contextTitle, contextSubtitle, contextStatus,\n showChatList, subjectId,\n accent, launcher, position, quickReplies, height = '100%',\n i18n, translateLang,\n}: ChatWidgetProps): JSX.Element {\n const ref = useRef<HTMLDivElement>(null)\n const handleRef = useRef<WidgetHandle | null>(null)\n\n useEffect(() => {\n if (!ref.current) return\n handleRef.current = mount({\n el: ref.current,\n url,\n profileId,\n ...(subjectId ? { subjectId } : {}),\n ...(userId ? { token: userId } : {}),\n ...(userId || userName || userEmail || userAvatar ? {\n user: {\n ...(userName ? { name: userName } : {}),\n ...(userEmail ? { email: userEmail } : {}),\n ...(userAvatar ? { avatar: userAvatar } : {}),\n },\n } : {}),\n ...(contextTitle ? {\n subject: {\n title: contextTitle,\n ...(contextSubtitle ? { subtitle: contextSubtitle } : {}),\n ...(contextStatus ? { status: contextStatus } : {}),\n },\n } : {}),\n ...(showChatList ? { showChatList: true } : {}),\n ...(quickReplies ? { quickReplies } : {}),\n ...(accent ? { accent } : {}),\n ...(translateLang ? { translateLang } : {}),\n ...(i18n ? { i18n } : {}),\n ...(launcher ? { launcher, position: position ?? 'bottom-right' } : {}),\n })\n return () => { handleRef.current?.close(); handleRef.current = null }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [url, profileId, subjectId, userId])\n\n if (launcher) return <></>\n return (\n <div\n ref={ref}\n style={{ width: '100%', height, minHeight: height === '100%' ? '480px' : undefined }}\n />\n )\n}\n\n// ── MarketplaceChat ───────────────────────────────────────────────────────────\n\nexport interface MarketplaceChatProps extends BaseProps {\n /** Unique ID for this listing — each listing gets its own thread.\n * Omit for a general (non-item-specific) conversation. */\n listingId?: string\n /** Shown at the top of the chat — e.g. \"2019 Toyota Camry SE\" */\n listingTitle?: string\n /** One-line detail — e.g. \"45,000 km · Automatic\" */\n listingMeta?: string\n /** Price shown as a tag — e.g. 12500 → \"$12,500\" */\n listingPrice?: number\n /** Status badge — e.g. \"Available\", \"Sold\", \"Pending\" */\n listingStatus?: string\n}\n\nconst DEFAULT_MARKETPLACE_REPLIES = [\n 'Is this still available?',\n 'Can I schedule a viewing?',\n 'What is your best price?',\n]\n\n/**\n * Marketplace chat widget. Two modes depending on whether `listingId` is provided:\n *\n * **With `listingId` (listing page)** — opens that listing's chat immediately.\n * No list screen. The buyer is already looking at the item so context is clear.\n * ```tsx\n * // On your listing detail page:\n * <MarketplaceChat\n * url={process.env.NEXT_PUBLIC_RELAY_WS_URL}\n * profileId={process.env.NEXT_PUBLIC_RELAY_PROFILE_ID}\n * listingId={car.id}\n * listingTitle={car.title}\n * listingPrice={car.price}\n * listingMeta={`${car.mileage.toLocaleString()} km · ${car.gearbox}`}\n * listingStatus=\"Available\"\n * userId={session?.user.id}\n * userName={session?.user.name}\n * userEmail={session?.user.email}\n * />\n * ```\n *\n * **Without `listingId` (My Messages / inbox page)** — shows the WhatsApp-style\n * thread list as the home screen. No conversation is opened until the user taps one.\n * Use this on a dedicated `/messages` or `/inbox` page.\n * ```tsx\n * // On your /messages page:\n * <MarketplaceChat\n * url={process.env.NEXT_PUBLIC_RELAY_WS_URL}\n * profileId={process.env.NEXT_PUBLIC_RELAY_PROFILE_ID}\n * userId={session?.user.id}\n * userName={session?.user.name}\n * />\n * ```\n *\n * Only `url` and `profileId` are required. All other props are optional.\n */\nexport function MarketplaceChat({\n url, profileId,\n listingId, listingTitle, listingMeta, listingPrice, listingStatus,\n userId, userName, userEmail, userAvatar,\n accent, launcher, position, quickReplies, height = '100%',\n i18n, translateLang,\n}: MarketplaceChatProps): JSX.Element {\n const ref = useRef<HTMLDivElement>(null)\n const handleRef = useRef<WidgetHandle | null>(null)\n\n useEffect(() => {\n if (!ref.current) return\n handleRef.current = mount({\n el: ref.current,\n url,\n profileId,\n\n // ── Listing context ────────────────────────────────────────────────────\n // When listingId is present: open that listing's chat directly (no list).\n // When listingId is absent: show the thread list (dedicated inbox page).\n ...(listingId ? { subjectId: `listing_${listingId}` } : {}),\n\n ...(userId ? { token: userId } : {}),\n ...(userId || userName || userEmail || userAvatar ? {\n user: {\n ...(userName ? { name: userName } : {}),\n ...(userEmail ? { email: userEmail } : {}),\n ...(userAvatar ? { avatar: userAvatar } : {}),\n ...(listingId ? { meta: { listingId } } : {}),\n },\n } : {}),\n\n ...(listingTitle ? {\n subject: {\n title: listingTitle,\n ...(listingMeta ? { subtitle: listingMeta } : {}),\n ...(listingPrice ? { tags: [`$${listingPrice.toLocaleString()}`] } : {}),\n ...(listingStatus ? { status: listingStatus } : {}),\n },\n } : {}),\n\n // showChatList is true in both cases but behaves differently:\n // • With subjectId → chat screen shown immediately on open (in index.ts)\n // • Without subjectId → list screen is home (the dedicated inbox case)\n showChatList: true,\n quickReplies: quickReplies ?? DEFAULT_MARKETPLACE_REPLIES,\n ...(accent ? { accent } : {}),\n ...(translateLang ? { translateLang } : {}),\n ...(i18n ? { i18n } : {}),\n ...(launcher ? { launcher, position: position ?? 'bottom-right' } : {}),\n })\n return () => { handleRef.current?.close(); handleRef.current = null }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [url, profileId, listingId, userId])\n\n if (launcher) return <></>\n return (\n <div\n ref={ref}\n style={{ width: '100%', height, minHeight: height === '100%' ? '480px' : undefined }}\n />\n )\n}\n"],"names":["ChatWidget","url","profileId","userId","userName","userEmail","userAvatar","contextTitle","contextSubtitle","contextStatus","showChatList","subjectId","accent","launcher","position","quickReplies","height","i18n","translateLang","ref","useRef","handleRef","useEffect","mount","_a","jsx","Fragment","DEFAULT_MARKETPLACE_REPLIES","MarketplaceChat","listingId","listingTitle","listingMeta","listingPrice","listingStatus"],"mappings":"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"}
|
package/dist/renderer.d.ts
CHANGED
|
@@ -22,6 +22,10 @@ export interface WidgetConfig {
|
|
|
22
22
|
offline?: string;
|
|
23
23
|
poweredBy?: string;
|
|
24
24
|
};
|
|
25
|
+
/** When showChatList=true, start on the chat screen instead of the list screen.
|
|
26
|
+
* Set when a specific conversation/listing is already known (e.g. MarketplaceChat
|
|
27
|
+
* with listingId). The list is still accessible via the back button. */
|
|
28
|
+
startOnChat?: boolean;
|
|
25
29
|
}
|
|
26
30
|
export interface RendererHandlers {
|
|
27
31
|
onSend(text: string): void;
|
package/package.json
CHANGED
|
@@ -1,13 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@paramms/chat-widget",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"description": "Embeddable real-time chat widget for the Relay platform",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
8
|
-
"url": "https://github.com/paramms/
|
|
8
|
+
"url": "https://github.com/paramms/chat-widget"
|
|
9
9
|
},
|
|
10
|
-
"keywords": [
|
|
10
|
+
"keywords": [
|
|
11
|
+
"chat",
|
|
12
|
+
"widget",
|
|
13
|
+
"relay",
|
|
14
|
+
"customer-support",
|
|
15
|
+
"live-chat"
|
|
16
|
+
],
|
|
11
17
|
"files": [
|
|
12
18
|
"dist/index.js",
|
|
13
19
|
"dist/index.js.map",
|
|
@@ -64,4 +70,4 @@
|
|
|
64
70
|
"optional": true
|
|
65
71
|
}
|
|
66
72
|
}
|
|
67
|
-
}
|
|
73
|
+
}
|