@usereq/widget 0.2.14 → 0.2.17

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@usereq/widget",
3
- "version": "0.2.14",
3
+ "version": "0.2.17",
4
4
  "type": "module",
5
5
  "main": "./src/index.ts",
6
6
  "types": "./src/index.ts",
@@ -76,7 +76,7 @@
76
76
  },
77
77
  "dependencies": {
78
78
  "@streamdown/mermaid": "^1.0.2",
79
- "@usereq/ui": "^0.2.20",
79
+ "@usereq/ui": "^0.2.22",
80
80
  "lucide-react": "^0.553.0",
81
81
  "motion": "^12.23.24",
82
82
  "react": "^19.2.0",
@@ -223,8 +223,9 @@ export class AgentWidgetElement extends HTMLElement {
223
223
  const placement = this.getResolvedPlacement();
224
224
  const variant = this.getResolvedVariant();
225
225
  const preset = this.getResolvedPreset();
226
- const autoStartEnabled =
227
- this.sessionConfig?.widgetConfig.widgetBehavior?.autoStart === true;
226
+ const behavior = this.sessionConfig?.widgetConfig.widgetBehavior;
227
+ const autoStartEnabled = behavior?.autoStart === true;
228
+ const alwaysOpen = behavior?.alwaysOpen === true;
228
229
  const awaitingConversation =
229
230
  this.status === "booting" ||
230
231
  (this.sessionState != null && !this.sessionState.conversationId);
@@ -267,6 +268,7 @@ export class AgentWidgetElement extends HTMLElement {
267
268
  spotlights: this.getSpotlights(),
268
269
  ctaLink: this.getCtaLink(),
269
270
  open: this.open,
271
+ alwaysOpen,
270
272
  status: this.status,
271
273
  messages: this.messages,
272
274
  errorMessage: this.errorMessage,
@@ -333,6 +335,13 @@ export class AgentWidgetElement extends HTMLElement {
333
335
  this.welcomeMessage = state.session.widgetConfig.welcomeMessage;
334
336
  this.status = "ready";
335
337
  this.bootstrapPhase = "ready";
338
+ // Apply the agent's configured open-state on mount. `alwaysOpen`
339
+ // wins (the panel is pinned regardless), and `defaultOpen` opens it
340
+ // once if the visitor hasn't already interacted with the launcher.
341
+ const behavior = state.session.widgetConfig.widgetBehavior;
342
+ if (behavior?.alwaysOpen || behavior?.defaultOpen) {
343
+ this.open = true;
344
+ }
336
345
  this.renderWidget();
337
346
  } catch (error) {
338
347
  if (runVersion !== this.bootstrapVersion) {
@@ -50,6 +50,12 @@ export type WidgetRuntimeProps = {
50
50
  spotlights: string[];
51
51
  ctaLink?: string | null;
52
52
  open: boolean;
53
+ /**
54
+ * When true, the embedded panel is pinned open and the launcher is hidden.
55
+ * Mirrors `WidgetBehavior.alwaysOpen` and short-circuits the trigger-rule
56
+ * gate (a pinned widget has no "open" event to trigger on).
57
+ */
58
+ alwaysOpen?: boolean;
53
59
  status: WidgetRuntimeStatus;
54
60
  messages: WidgetMessage[];
55
61
  errorMessage: string | null;
@@ -88,6 +94,7 @@ export function WidgetRuntime({
88
94
  spotlights,
89
95
  ctaLink,
90
96
  open,
97
+ alwaysOpen = false,
91
98
  status,
92
99
  messages,
93
100
  errorMessage,
@@ -101,7 +108,10 @@ export function WidgetRuntime({
101
108
  portalContainer,
102
109
  onLauncherClick,
103
110
  }: WidgetRuntimeProps) {
104
- const { ready } = useWidgetTriggerGate(triggerRule);
111
+ // Always-open widgets bypass the trigger-rule gate — the panel renders
112
+ // unconditionally, so any "open on X seconds / on scroll / on click"
113
+ // configuration would be meaningless. Pass `null` to short-circuit it.
114
+ const { ready } = useWidgetTriggerGate(alwaysOpen ? null : triggerRule);
105
115
  const { horizontal, vertical } = splitWidgetPlacement(placement);
106
116
 
107
117
  const { panelMessages, confirmation } = React.useMemo(
@@ -210,6 +220,7 @@ export function WidgetRuntime({
210
220
  status={RUNTIME_TO_PANEL_STATUS[status]}
211
221
  errorMessage={errorMessage}
212
222
  open={open}
223
+ alwaysOpen={alwaysOpen}
213
224
  onOpenChange={onOpenChange}
214
225
  onSendMessage={handleSend}
215
226
  onStopConversation={
@@ -30,11 +30,21 @@ export type WidgetTriggerRule =
30
30
  | { triggerRuleType: "element_displayed"; elementDisplayed: string };
31
31
 
32
32
  export type WidgetBehavior = {
33
+ /** Eagerly spin up the server-side conversation when the widget mounts. */
33
34
  autoStart: boolean;
35
+ /** Open the panel as soon as the widget mounts. The launcher stays visible
36
+ * and the visitor can still close the panel from there. */
37
+ defaultOpen: boolean;
38
+ /** Pin the panel open: launcher is hidden, the close affordance is hidden,
39
+ * and the panel is rendered inline as the only thing this widget shows.
40
+ * Useful for full-page or dedicated-chat embeds. Implies `defaultOpen`. */
41
+ alwaysOpen: boolean;
34
42
  };
35
43
 
36
44
  export const DEFAULT_WIDGET_BEHAVIOR: WidgetBehavior = {
37
45
  autoStart: false,
46
+ defaultOpen: false,
47
+ alwaysOpen: false,
38
48
  };
39
49
 
40
50
  export function normalizeWidgetBehavior(
@@ -42,6 +52,8 @@ export function normalizeWidgetBehavior(
42
52
  ): WidgetBehavior {
43
53
  return {
44
54
  autoStart: Boolean(behavior?.autoStart),
55
+ defaultOpen: Boolean(behavior?.defaultOpen),
56
+ alwaysOpen: Boolean(behavior?.alwaysOpen),
45
57
  };
46
58
  }
47
59