@runtypelabs/persona 3.17.0 → 3.19.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.
Files changed (61) hide show
  1. package/README.md +143 -1
  2. package/dist/animations/glyph-cycle.d.cts +1 -1
  3. package/dist/animations/glyph-cycle.d.ts +1 -1
  4. package/dist/animations/{types-HPZY7oAI.d.cts → types-cwY5HaFD.d.cts} +25 -0
  5. package/dist/animations/{types-HPZY7oAI.d.ts → types-cwY5HaFD.d.ts} +25 -0
  6. package/dist/animations/wipe.d.cts +1 -1
  7. package/dist/animations/wipe.d.ts +1 -1
  8. package/dist/index.cjs +47 -47
  9. package/dist/index.cjs.map +1 -1
  10. package/dist/index.d.cts +580 -4
  11. package/dist/index.d.ts +580 -4
  12. package/dist/index.global.js +102 -1636
  13. package/dist/index.global.js.map +1 -1
  14. package/dist/index.js +45 -45
  15. package/dist/index.js.map +1 -1
  16. package/dist/theme-editor.cjs +2844 -752
  17. package/dist/theme-editor.d.cts +337 -1
  18. package/dist/theme-editor.d.ts +337 -1
  19. package/dist/theme-editor.js +2958 -752
  20. package/dist/theme-reference.cjs +1 -1
  21. package/dist/theme-reference.d.cts +14 -0
  22. package/dist/theme-reference.d.ts +14 -0
  23. package/dist/widget.css +780 -0
  24. package/package.json +1 -1
  25. package/src/client.test.ts +134 -0
  26. package/src/client.ts +71 -0
  27. package/src/components/ask-user-question-bubble.test.ts +583 -0
  28. package/src/components/ask-user-question-bubble.ts +924 -0
  29. package/src/components/composer-builder.test.ts +52 -0
  30. package/src/components/composer-builder.ts +67 -490
  31. package/src/components/composer-parts.test.ts +152 -0
  32. package/src/components/composer-parts.ts +452 -0
  33. package/src/components/header-builder.ts +22 -299
  34. package/src/components/header-parts.ts +360 -0
  35. package/src/components/messages.ts +33 -1
  36. package/src/components/panel.test.ts +61 -0
  37. package/src/components/panel.ts +303 -9
  38. package/src/components/pill-composer-builder.test.ts +85 -0
  39. package/src/components/pill-composer-builder.ts +183 -0
  40. package/src/defaults.ts +21 -0
  41. package/src/index.ts +20 -1
  42. package/src/plugins/types.ts +57 -0
  43. package/src/runtime/init.ts +4 -2
  44. package/src/runtime/persist-state.test.ts +152 -0
  45. package/src/session.test.ts +183 -0
  46. package/src/session.ts +242 -3
  47. package/src/styles/widget.css +780 -0
  48. package/src/types/theme.ts +15 -0
  49. package/src/types.ts +271 -1
  50. package/src/ui.ask-user-question-plugin.test.ts +649 -0
  51. package/src/ui.component-directive.test.ts +183 -0
  52. package/src/ui.composer-bar.test.ts +1009 -0
  53. package/src/ui.ts +1439 -76
  54. package/src/utils/attachment-manager.ts +1 -1
  55. package/src/utils/dock.test.ts +45 -0
  56. package/src/utils/dock.ts +3 -0
  57. package/src/utils/icons.ts +314 -58
  58. package/src/utils/storage.ts +10 -2
  59. package/src/utils/stream-animation.ts +7 -2
  60. package/src/utils/theme.test.ts +36 -0
  61. package/src/utils/tokens.ts +23 -0
@@ -217,6 +217,18 @@ interface MessageTokens {
217
217
  /** Border color between messages in the thread. */
218
218
  border?: TokenReference<'color'>;
219
219
  }
220
+ /**
221
+ * Welcome / intro card rendered above the message list when no messages exist.
222
+ * Set `copy.showWelcomeCard: false` to hide it; use `layout.slots["body-top"]`
223
+ * to replace it wholesale.
224
+ */
225
+ interface IntroCardTokens extends ComponentTokenSet {
226
+ background?: TokenReference<'color'>;
227
+ borderRadius?: TokenReference<'radius'>;
228
+ padding?: TokenReference<'spacing'>;
229
+ /** Box-shadow on the intro card (token ref or raw CSS, e.g. `none`). */
230
+ shadow?: string;
231
+ }
220
232
  /** Collapsible widget chrome (tool bubbles, reasoning bubbles, approval bubbles). */
221
233
  interface CollapsibleWidgetTokens {
222
234
  /** Background for content areas. */
@@ -414,6 +426,8 @@ interface ComponentTokens {
414
426
  panel: PanelTokens;
415
427
  header: HeaderTokens;
416
428
  message: MessageTokens;
429
+ /** Welcome / intro card shown above the message list. */
430
+ introCard?: IntroCardTokens;
417
431
  /** Markdown surfaces (chat + artifact pane). */
418
432
  markdown?: MarkdownTokens;
419
433
  voice: VoiceTokens;
@@ -558,6 +572,61 @@ interface AgentWidgetPlugin {
558
572
  defaultRenderer: () => HTMLElement;
559
573
  config: AgentWidgetConfig;
560
574
  }) => HTMLElement | null;
575
+ /**
576
+ * Custom renderer for `ask_user_question` tool calls.
577
+ *
578
+ * When a plugin returns an `HTMLElement`, it is inserted into the transcript
579
+ * in place of the default (which is no transcript bubble — the built-in
580
+ * renders a sheet over the composer). The built-in composer-overlay sheet
581
+ * is suppressed so the plugin's UI fully owns the interaction.
582
+ *
583
+ * Return `null` to fall through to the built-in overlay sheet.
584
+ *
585
+ * The context gives you a pre-parsed `payload` (may be partial while the
586
+ * tool call is still streaming — check `complete`) and two callbacks:
587
+ * `resolve(answer)` resumes the paused LOCAL tool with the user's answer,
588
+ * and `dismiss()` cancels with the sentinel `"(dismissed)"`.
589
+ *
590
+ * @example
591
+ * ```typescript
592
+ * renderAskUserQuestion: ({ payload, resolve, dismiss }) => {
593
+ * const prompt = payload.questions?.[0];
594
+ * if (!prompt) return null;
595
+ * const root = document.createElement("div");
596
+ * root.textContent = prompt.question ?? "";
597
+ * (prompt.options ?? []).forEach((option) => {
598
+ * const btn = document.createElement("button");
599
+ * btn.textContent = option.label;
600
+ * btn.addEventListener("click", () => resolve(option.label));
601
+ * root.appendChild(btn);
602
+ * });
603
+ * return root;
604
+ * }
605
+ * ```
606
+ */
607
+ renderAskUserQuestion?: (context: {
608
+ message: AgentWidgetMessage;
609
+ /**
610
+ * Parsed `{ questions: [...] }` payload. May be partial while the tool
611
+ * call is still streaming; see `complete`. `null` when no payload has
612
+ * arrived yet.
613
+ */
614
+ payload: Partial<AskUserQuestionPayload> | null;
615
+ /** `true` once the tool-call args have fully streamed in. */
616
+ complete: boolean;
617
+ /**
618
+ * Resume the paused LOCAL tool with the user's answer. Posts to the
619
+ * resume endpoint, pipes the SSE stream back into the session, and
620
+ * appends a user-visible answer bubble to the transcript.
621
+ */
622
+ resolve: (answer: string) => void;
623
+ /**
624
+ * Cancel the question. Resumes with the sentinel `"(dismissed)"` so the
625
+ * server doesn't sit in `waiting_for_local` forever. Idempotent.
626
+ */
627
+ dismiss: () => void;
628
+ config: AgentWidgetConfig;
629
+ }) => HTMLElement | null;
561
630
  /**
562
631
  * Custom renderer for approval bubbles
563
632
  * Return null to use default renderer
@@ -778,6 +847,31 @@ type AgentMessageMetadata = {
778
847
  * or `prompt` step inside the nested flow). Stable key for that step.
779
848
  */
780
849
  parentStepId?: string;
850
+ /**
851
+ * Set to `true` on a tool-variant message produced from a `step_await`
852
+ * event (`awaitReason: "local_tool_required"`). Signals to UI code that
853
+ * the tool call is a LOCAL tool and the server is paused waiting for a
854
+ * `POST /v1/dispatch/resume` with the user's answer keyed by tool name.
855
+ */
856
+ awaitingLocalTool?: boolean;
857
+ /**
858
+ * Set to `true` once the user has picked / typed / dismissed an answer for
859
+ * an `ask_user_question` tool call, so renderers stop re-mounting the
860
+ * answer-pill sheet for this tool call on subsequent render passes.
861
+ */
862
+ askUserQuestionAnswered?: boolean;
863
+ /**
864
+ * In-progress answers for a multi-question `ask_user_question` payload,
865
+ * keyed by question text. Persisted across refresh so the user lands back
866
+ * where they were if the page reloads mid-flow. Cleared once
867
+ * `askUserQuestionAnswered` flips to `true`.
868
+ */
869
+ askUserQuestionAnswers?: Record<string, string | string[]>;
870
+ /**
871
+ * Current page index for a multi-question `ask_user_question` payload's
872
+ * paginated stepper. Persists alongside `askUserQuestionAnswers`.
873
+ */
874
+ askUserQuestionIndex?: number;
781
875
  };
782
876
  type AgentWidgetRequestMiddlewareContext = {
783
877
  payload: AgentWidgetRequestPayload;
@@ -825,6 +919,8 @@ type AgentWidgetActionHandler = (action: AgentWidgetParsedAction, context: Agent
825
919
  type AgentWidgetStoredState = {
826
920
  messages?: AgentWidgetMessage[];
827
921
  metadata?: Record<string, unknown>;
922
+ artifacts?: PersonaArtifactRecord[];
923
+ selectedArtifactId?: string | null;
828
924
  };
829
925
  interface AgentWidgetStorageAdapter {
830
926
  load?: () => AgentWidgetStoredState | null | Promise<AgentWidgetStoredState | null>;
@@ -1382,6 +1478,113 @@ type AgentWidgetFeatureFlags = {
1382
1478
  artifacts?: AgentWidgetArtifactsFeature;
1383
1479
  /** Reveal animation for streaming assistant text. */
1384
1480
  streamAnimation?: AgentWidgetStreamAnimationFeature;
1481
+ /**
1482
+ * Built-in interactive answer-pill sheet shown when the assistant invokes
1483
+ * the `ask_user_question` tool. Slides up over the composer with tappable
1484
+ * pills + optional free-text input.
1485
+ */
1486
+ askUserQuestion?: AgentWidgetAskUserQuestionFeature;
1487
+ };
1488
+ /**
1489
+ * Single selectable option in an `ask_user_question` prompt.
1490
+ * Mirrors Anthropic's AskUserQuestion schema.
1491
+ */
1492
+ type AskUserQuestionOption = {
1493
+ /** Pill label (required). */
1494
+ label: string;
1495
+ /** Optional long-form description (shown as a subtitle on tap-hover). */
1496
+ description?: string;
1497
+ /** Optional rich preview — reserved for future rendering; ignored in v1. */
1498
+ preview?: string;
1499
+ };
1500
+ /**
1501
+ * A single question in an `ask_user_question` tool call.
1502
+ * The tool may carry 1–8 prompts. When more than one is supplied, the built-in
1503
+ * renderer paginates them as a "Question N of M" stepper with Back / Next /
1504
+ * Submit-all controls; single-question payloads render without stepper chrome.
1505
+ */
1506
+ type AskUserQuestionPrompt = {
1507
+ /** The question text shown to the user. */
1508
+ question: string;
1509
+ /** Optional short header label (≤12 chars) used as a compact group title. */
1510
+ header?: string;
1511
+ /** 2–4 selectable options. */
1512
+ options: AskUserQuestionOption[];
1513
+ /** When true, the user can pick multiple options and submit together. Default false. */
1514
+ multiSelect?: boolean;
1515
+ /** When true, a free-text "Other…" pill expands to an input. Default true. */
1516
+ allowFreeText?: boolean;
1517
+ };
1518
+ /** Parsed payload of an `ask_user_question` tool call. */
1519
+ type AskUserQuestionPayload = {
1520
+ /** 1–8 questions. Anything beyond the renderer's cap is truncated with a console warning. */
1521
+ questions: AskUserQuestionPrompt[];
1522
+ };
1523
+ /**
1524
+ * Style overrides for the answer-pill sheet. All values are raw CSS strings
1525
+ * and are plumbed through as CSS custom properties on the sheet root.
1526
+ */
1527
+ type AgentWidgetAskUserQuestionStyles = {
1528
+ sheetBackground?: string;
1529
+ sheetBorder?: string;
1530
+ sheetShadow?: string;
1531
+ pillBackground?: string;
1532
+ pillBackgroundSelected?: string;
1533
+ pillTextColor?: string;
1534
+ pillTextColorSelected?: string;
1535
+ pillBorderRadius?: string;
1536
+ customInputBackground?: string;
1537
+ };
1538
+ /**
1539
+ * Feature config for the built-in `ask_user_question` answer-pill sheet.
1540
+ * When a tool call with the name `ask_user_question` arrives, the widget
1541
+ * renders an interactive sheet over the composer in place of the generic
1542
+ * tool bubble.
1543
+ */
1544
+ type AgentWidgetAskUserQuestionFeature = {
1545
+ /** Enable the feature. Defaults to true. When false, `ask_user_question` renders as a regular tool bubble. */
1546
+ enabled?: boolean;
1547
+ /** Slide-in animation duration in ms. Defaults to 180. */
1548
+ slideInMs?: number;
1549
+ /** Label for the free-text pill. Defaults to "Other…". */
1550
+ freeTextLabel?: string;
1551
+ /** Placeholder text in the free-text input. Defaults to "Type your answer…". */
1552
+ freeTextPlaceholder?: string;
1553
+ /** Button label for submitting multi-select / free-text answers. Defaults to "Send". */
1554
+ submitLabel?: string;
1555
+ /** Button label advancing to the next question in grouped (paginated) payloads. Defaults to "Next". */
1556
+ nextLabel?: string;
1557
+ /** Button label moving back to the previous question in grouped payloads. Defaults to "Back". */
1558
+ backLabel?: string;
1559
+ /** Button label submitting all answers from the final page of a grouped payload. Defaults to "Submit all". */
1560
+ submitAllLabel?: string;
1561
+ /**
1562
+ * In grouped (multi-question) mode, auto-advance to the next page after a
1563
+ * single-select pill pick or free-text submit on intermediate pages.
1564
+ * Defaults to `true`. The final page never auto-submits — users always
1565
+ * confirm with an explicit "Submit all" click. Multi-select pages always
1566
+ * require an explicit Next regardless of this setting.
1567
+ */
1568
+ groupedAutoAdvance?: boolean;
1569
+ /**
1570
+ * Visual layout for the option list.
1571
+ * - `"rows"` (default) — full-width stacked rows with always-visible
1572
+ * descriptions, right-edge number badges (single-select) or checkboxes
1573
+ * (multi-select), and an always-visible inline "Other" input.
1574
+ * - `"pills"` — legacy compact pill list with horizontal wrap; description
1575
+ * surfaces as a tooltip and the "Other…" pill expands on click.
1576
+ */
1577
+ layout?: "rows" | "pills";
1578
+ /**
1579
+ * Button label for skipping the current question in grouped payloads.
1580
+ * Defaults to "Skip". On intermediate pages Skip advances without recording
1581
+ * an answer; on the final page Skip submits the partial answer record
1582
+ * (skipped questions absent from the resolved object). For single-question
1583
+ * payloads Skip behaves like dismiss.
1584
+ */
1585
+ skipLabel?: string;
1586
+ /** Style overrides for the sheet and pills. */
1587
+ styles?: AgentWidgetAskUserQuestionStyles;
1385
1588
  };
1386
1589
  type SSEEventRecord = {
1387
1590
  id: string;
@@ -1523,6 +1726,101 @@ type AgentWidgetDockConfig = {
1523
1726
  */
1524
1727
  reveal?: "resize" | "overlay" | "push" | "emerge";
1525
1728
  };
1729
+ /**
1730
+ * Layout configuration for `mountMode: "composer-bar"`. Controls how the
1731
+ * collapsed pill is positioned and sized, and how the panel expands when
1732
+ * the user opens it.
1733
+ */
1734
+ type AgentWidgetComposerBarConfig = {
1735
+ /**
1736
+ * Max-width of the collapsed pill composer at the bottom of the viewport.
1737
+ * @default "720px"
1738
+ */
1739
+ collapsedMaxWidth?: string;
1740
+ /**
1741
+ * Bottom offset (CSS length) from the viewport edge in the collapsed state.
1742
+ * @default "16px"
1743
+ */
1744
+ bottomOffset?: string;
1745
+ /**
1746
+ * Auto-expand the panel when the user submits a message while the composer
1747
+ * is collapsed. When false, the message still sends but the panel remains
1748
+ * collapsed (the host can drive expansion programmatically).
1749
+ * @default true
1750
+ */
1751
+ expandOnSubmit?: boolean;
1752
+ /**
1753
+ * Size of the expanded chat panel.
1754
+ * - `"anchored"` (default): the pill stays at the bottom of the viewport
1755
+ * and the chat history grows upward into a centered column above it.
1756
+ * Width is driven by `expandedMaxWidth`; the panel's top edge sits at
1757
+ * `expandedTopOffset` from the viewport top.
1758
+ * - `"fullscreen"`: covers the entire viewport (mobile-app style). Inner
1759
+ * content is centered horizontally via `contentMaxWidth`.
1760
+ * - `"modal"`: centered sheet with margins; size driven by
1761
+ * `modalMaxWidth` / `modalMaxHeight`.
1762
+ * @default "anchored"
1763
+ */
1764
+ expandedSize?: "anchored" | "fullscreen" | "modal";
1765
+ /**
1766
+ * When `expandedSize === "anchored"`, max-width of the expanded panel
1767
+ * column. Capped at `calc(100vw - 32px)` on narrow viewports.
1768
+ * @default "880px"
1769
+ */
1770
+ expandedMaxWidth?: string;
1771
+ /**
1772
+ * When `expandedSize === "anchored"`, distance from the viewport top to
1773
+ * the panel's top edge. Accepts any CSS length.
1774
+ * @default "5vh"
1775
+ */
1776
+ expandedTopOffset?: string;
1777
+ /**
1778
+ * Max-width applied to messages, composer form, suggestions, and
1779
+ * attachment previews so they center horizontally inside the expanded
1780
+ * panel. Falls back to `layout.contentMaxWidth` when set; otherwise
1781
+ * defaults to this value.
1782
+ * @default "720px"
1783
+ */
1784
+ contentMaxWidth?: string;
1785
+ /**
1786
+ * When `expandedSize === "modal"`, max-width of the expanded sheet.
1787
+ * @default "880px"
1788
+ */
1789
+ modalMaxWidth?: string;
1790
+ /**
1791
+ * When `expandedSize === "modal"`, max-height of the expanded sheet.
1792
+ * @default "min(90vh, 800px)"
1793
+ */
1794
+ modalMaxHeight?: string;
1795
+ /**
1796
+ * Configuration for the "peek" banner — the chrome-less row above the
1797
+ * collapsed pill that previews the most recent assistant message.
1798
+ */
1799
+ peek?: AgentWidgetComposerBarPeekConfig;
1800
+ };
1801
+ /**
1802
+ * Configuration for the composer-bar peek banner. Reuses the same
1803
+ * `streamAnimation` shape developers already configure for the main message
1804
+ * stream, so the surface for animations is identical across both contexts.
1805
+ *
1806
+ * Resolution order:
1807
+ * - If `peek.streamAnimation` is set, those values apply to the peek.
1808
+ * - Otherwise the peek inherits from `features.streamAnimation`.
1809
+ *
1810
+ * Per-surface carve-outs:
1811
+ * - `bubbleClass` from a plugin (used by `pop-bubble`) is ignored — the peek
1812
+ * has no bubble analog.
1813
+ * - `containerClass`, `wrap` ("char" | "word"), `useCaret`, `placeholder`
1814
+ * ("skeleton"), `buffer` ("word" | "line"), `speed`, `duration`, and
1815
+ * custom plugins all apply unchanged.
1816
+ */
1817
+ type AgentWidgetComposerBarPeekConfig = {
1818
+ /**
1819
+ * Reveal animation for the peek's trailing-message preview. Same shape as
1820
+ * `features.streamAnimation`. Omit to inherit from the main stream config.
1821
+ */
1822
+ streamAnimation?: AgentWidgetStreamAnimationFeature;
1823
+ };
1526
1824
  type AgentWidgetLauncherConfig = {
1527
1825
  enabled?: boolean;
1528
1826
  title?: string;
@@ -1537,14 +1835,22 @@ type AgentWidgetLauncherConfig = {
1537
1835
  * Controls how the launcher panel is mounted relative to the host page.
1538
1836
  * - "floating": default floating launcher / panel behavior
1539
1837
  * - "docked": wraps the target container and renders as a sibling dock
1838
+ * - "composer-bar": persistent rounded-pill composer fixed to the bottom of
1839
+ * the viewport that morphs into a fullscreen (or modal) chat panel on
1840
+ * submit and minimizes back to the pill on close.
1540
1841
  *
1541
1842
  * @default "floating"
1542
1843
  */
1543
- mountMode?: "floating" | "docked";
1844
+ mountMode?: "floating" | "docked" | "composer-bar";
1544
1845
  /**
1545
1846
  * Layout configuration for docked mode.
1546
1847
  */
1547
1848
  dock?: AgentWidgetDockConfig;
1849
+ /**
1850
+ * Layout configuration for composer-bar mode.
1851
+ * Only applies when `mountMode === "composer-bar"`.
1852
+ */
1853
+ composerBar?: AgentWidgetComposerBarConfig;
1548
1854
  autoExpand?: boolean;
1549
1855
  width?: string;
1550
1856
  /**
@@ -3121,6 +3427,17 @@ type AgentWidgetConfig = {
3121
3427
  autoFocusInput?: boolean;
3122
3428
  launcher?: AgentWidgetLauncherConfig;
3123
3429
  initialMessages?: AgentWidgetMessage[];
3430
+ /**
3431
+ * Artifacts to hydrate into the pane at init. Typically populated from
3432
+ * `storageAdapter.load()` alongside `initialMessages` so the artifact pane
3433
+ * survives a page refresh.
3434
+ */
3435
+ initialArtifacts?: PersonaArtifactRecord[];
3436
+ /**
3437
+ * Which artifact id (if any) should be selected in the pane at init. Paired
3438
+ * with `initialArtifacts`.
3439
+ */
3440
+ initialSelectedArtifactId?: string | null;
3124
3441
  suggestionChips?: string[];
3125
3442
  suggestionChipsConfig?: AgentWidgetSuggestionChipsConfig;
3126
3443
  debug?: boolean;
@@ -3501,6 +3818,13 @@ type AgentWidgetConfig = {
3501
3818
  * When `true`, uses default settings with sessionStorage.
3502
3819
  * When an object, allows customizing storage type, key prefix, and what to persist.
3503
3820
  *
3821
+ * Setting this to `false` is the explicit kill-switch: it disables UI-state
3822
+ * persistence **and** message-history persistence. When `false`, any
3823
+ * `storageAdapter` you configure is ignored and the default localStorage
3824
+ * adapter is not created — no chat history is read or written. Pass `true`
3825
+ * (or omit) to keep the default behavior of persisting messages via the
3826
+ * configured `storageAdapter` (or the built-in localStorage adapter).
3827
+ *
3504
3828
  * @example
3505
3829
  * ```typescript
3506
3830
  * // Simple usage - persist open state in sessionStorage
@@ -3523,6 +3847,14 @@ type AgentWidgetConfig = {
3523
3847
  * }
3524
3848
  * }
3525
3849
  * ```
3850
+ *
3851
+ * @example
3852
+ * ```typescript
3853
+ * // Ephemeral widget — no message history written anywhere
3854
+ * config: {
3855
+ * persistState: false
3856
+ * }
3857
+ * ```
3526
3858
  */
3527
3859
  persistState?: boolean | AgentWidgetPersistStateConfig;
3528
3860
  /**
@@ -4191,6 +4523,10 @@ type Controller = {
4191
4523
  upsertArtifact: (manual: PersonaArtifactManualUpsert) => PersonaArtifactRecord | null;
4192
4524
  selectArtifact: (id: string) => void;
4193
4525
  clearArtifacts: () => void;
4526
+ /** Read current artifacts (useful on init to rebuild host-side tab state after hydration). */
4527
+ getArtifacts: () => PersonaArtifactRecord[];
4528
+ /** Read the currently selected artifact id (paired with `getArtifacts`). */
4529
+ getSelectedArtifactId: () => string | null;
4194
4530
  /**
4195
4531
  * Focus the chat input. Returns true if focus succeeded, false if panel is closed
4196
4532
  * (launcher mode) or textarea is unavailable.