@pmate/chat-sdk 0.5.0 → 0.5.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.
@@ -1,10 +1,11 @@
1
- import { useId, useRef, useState } from "react";
1
+ import { useId, useLayoutEffect, useRef, useState } from "react";
2
2
  import { filterMentionCandidates, findMentionQuery, rebaseMentions } from "../mentions/model";
3
3
  /** Attach inputProps to a textarea, render MentionMenu with menuProps, and pass
4
4
  * getMentions() with the untrimmed value when sending. No business lookup is performed. */
5
5
  export function useMentions({ value, onChange, config, disabled = false }) {
6
6
  const id = useId();
7
7
  const inputRef = useRef(null);
8
+ const pendingSelection = useRef(null);
8
9
  const menuRef = useRef(null);
9
10
  const document = useRef({ text: value, mentions: [] });
10
11
  const [caret, setCaret] = useState(0);
@@ -17,6 +18,17 @@ export function useMentions({ value, onChange, config, disabled = false }) {
17
18
  const query = config && focused && !composing && !disabled && !dismissed ? findMentionQuery(value, caret) : null;
18
19
  const candidates = query ? filterMentionCandidates(config.candidates, query.query) : [];
19
20
  const index = Math.min(activeIndex, Math.max(0, candidates.length - 1));
21
+ // Set the caret as soon as the controlled value commits. A later animation
22
+ // frame can otherwise move it backwards after the user's next keystroke.
23
+ useLayoutEffect(() => {
24
+ const pending = pendingSelection.current;
25
+ if (!pending || pending.text !== value)
26
+ return;
27
+ pendingSelection.current = null;
28
+ inputRef.current?.focus();
29
+ inputRef.current?.setSelectionRange(pending.position, pending.position);
30
+ setCaret(pending.position);
31
+ }, [value]);
20
32
  function select(candidate) {
21
33
  if (!query)
22
34
  return;
@@ -27,13 +39,8 @@ export function useMentions({ value, onChange, config, disabled = false }) {
27
39
  mentions: [...rebaseMentions(value, next, mentions), { id: candidate.id, name: candidate.name, start: query.start, end: query.start + insertion.length }].sort((a, b) => a.start - b.start),
28
40
  };
29
41
  setDismissed(true);
42
+ pendingSelection.current = { text: next, position: query.start + insertion.length + 1 };
30
43
  onChange(next);
31
- requestAnimationFrame(() => {
32
- const position = query.start + insertion.length + 1;
33
- inputRef.current?.focus();
34
- inputRef.current?.setSelectionRange(position, position);
35
- setCaret(position);
36
- });
37
44
  }
38
45
  function updateSelection(event) {
39
46
  inputRef.current = event.currentTarget;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pmate/chat-sdk",
3
- "version": "0.5.0",
3
+ "version": "0.5.1",
4
4
  "description": "Cross-platform PMate chat input components, voice input primitives, and ASR helpers",
5
5
  "type": "module",
6
6
  "main": "./lib/index.js",