@blocksdiy/react-common 1.70.2 → 1.71.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.
Files changed (75) hide show
  1. package/dist/comments/anchor.d.ts +96 -0
  2. package/dist/comments/anchor.d.ts.map +1 -0
  3. package/dist/comments/anchor.js +346 -0
  4. package/dist/comments/api.d.ts +38 -0
  5. package/dist/comments/api.d.ts.map +1 -0
  6. package/dist/comments/api.js +62 -0
  7. package/dist/comments/comment-agents.d.ts +28 -0
  8. package/dist/comments/comment-agents.d.ts.map +1 -0
  9. package/dist/comments/comment-agents.js +32 -0
  10. package/dist/comments/comment-mode.d.ts +115 -0
  11. package/dist/comments/comment-mode.d.ts.map +1 -0
  12. package/dist/comments/comment-mode.js +176 -0
  13. package/dist/comments/composer.d.ts +133 -0
  14. package/dist/comments/composer.d.ts.map +1 -0
  15. package/dist/comments/composer.js +273 -0
  16. package/dist/comments/index.d.ts +19 -0
  17. package/dist/comments/index.d.ts.map +1 -0
  18. package/dist/comments/index.js +23 -0
  19. package/dist/comments/layout.d.ts +25 -0
  20. package/dist/comments/layout.d.ts.map +1 -0
  21. package/dist/comments/layout.js +83 -0
  22. package/dist/comments/mentions.d.ts +32 -0
  23. package/dist/comments/mentions.d.ts.map +1 -0
  24. package/dist/comments/mentions.js +30 -0
  25. package/dist/comments/menu.d.ts +33 -0
  26. package/dist/comments/menu.d.ts.map +1 -0
  27. package/dist/comments/menu.js +47 -0
  28. package/dist/comments/open-comment.d.ts +28 -0
  29. package/dist/comments/open-comment.d.ts.map +1 -0
  30. package/dist/comments/open-comment.js +9 -0
  31. package/dist/comments/page-pick.d.ts +76 -0
  32. package/dist/comments/page-pick.d.ts.map +1 -0
  33. package/dist/comments/page-pick.js +283 -0
  34. package/dist/comments/pick-message.d.ts +11 -0
  35. package/dist/comments/pick-message.d.ts.map +1 -0
  36. package/dist/comments/pick-message.js +33 -0
  37. package/dist/comments/pin-view.d.ts +107 -0
  38. package/dist/comments/pin-view.d.ts.map +1 -0
  39. package/dist/comments/pin-view.js +248 -0
  40. package/dist/comments/pins.d.ts +126 -0
  41. package/dist/comments/pins.d.ts.map +1 -0
  42. package/dist/comments/pins.js +218 -0
  43. package/dist/comments/text-pick.d.ts +69 -0
  44. package/dist/comments/text-pick.d.ts.map +1 -0
  45. package/dist/comments/text-pick.js +307 -0
  46. package/dist/comments/threads-store.d.ts +119 -0
  47. package/dist/comments/threads-store.d.ts.map +1 -0
  48. package/dist/comments/threads-store.js +286 -0
  49. package/dist/comments/types.d.ts +235 -0
  50. package/dist/comments/types.d.ts.map +1 -0
  51. package/dist/comments/types.js +109 -0
  52. package/dist/comments/use-comment-menu.d.ts +69 -0
  53. package/dist/comments/use-comment-menu.d.ts.map +1 -0
  54. package/dist/comments/use-comment-menu.js +140 -0
  55. package/dist/comments/use-held.d.ts +3 -0
  56. package/dist/comments/use-held.d.ts.map +1 -0
  57. package/dist/comments/use-held.js +16 -0
  58. package/dist/comments/use-thread-scroll.d.ts +23 -0
  59. package/dist/comments/use-thread-scroll.d.ts.map +1 -0
  60. package/dist/comments/use-thread-scroll.js +89 -0
  61. package/dist/hooks/agent-conversation-context.d.ts +12 -1
  62. package/dist/hooks/agent-conversation-context.d.ts.map +1 -1
  63. package/dist/hooks/agent-conversation-context.js +12 -5
  64. package/dist/hooks/agent-thread-live-update.d.ts +5 -2
  65. package/dist/hooks/agent-thread-live-update.d.ts.map +1 -1
  66. package/dist/hooks/agent-thread-live-update.js +14 -0
  67. package/dist/hooks/use-agent-conversation.d.ts +8 -0
  68. package/dist/hooks/use-agent-conversation.d.ts.map +1 -1
  69. package/dist/hooks/use-agent-conversation.js +10 -0
  70. package/dist/hooks/use-agent-threads.d.ts +1 -0
  71. package/dist/hooks/use-agent-threads.d.ts.map +1 -1
  72. package/dist/hooks/use-agent-threads.js +4 -1
  73. package/dist/iframe-communication/types.d.ts +25 -0
  74. package/dist/iframe-communication/types.d.ts.map +1 -1
  75. package/package.json +5 -2
@@ -0,0 +1,115 @@
1
+ /**
2
+ * Comment mode: whether the app shows its comments right now. The platform turns it on and off
3
+ * (its Comment button, over the iframe bridge: app-render calls `setCommentMode`); the app's
4
+ * comments surface (the registry `comments` item) reads it and can turn it off itself (Esc), which
5
+ * the platform hears through `onCommentModeExit`. Host and app share this module: it is a UMD
6
+ * external app-render provides, so no window global is needed.
7
+ *
8
+ * Turning it on is a handshake: it resolves `true` once a surface is mounted, waiting a little for
9
+ * one (the bundle may still be loading). An app without the comments component never answers, so
10
+ * the platform learns it can't show comments and the mode stays off.
11
+ *
12
+ * The app can also open one thread itself (a comment's link in the agent chat): `openCommentThread`
13
+ * asks the host for comment mode, as its Comment button would, and the surface goes to the thread.
14
+ * Only where the platform offers comments (`CommentOptions.isOffered`): not in its builder.
15
+ */
16
+ /**
17
+ * What the app's comments show, as the platform's Comment button sets it. `mentionableAgents`: the
18
+ * block names of the agents this person may mention (their roles' agent mini-apps); all of the
19
+ * app's agents when absent (the platform hasn't sent its options).
20
+ */
21
+ export interface CommentOptions {
22
+ /** Pins of resolved threads and outdated ones (their text changed) show too. */
23
+ showResolved: boolean;
24
+ mentionableAgents?: readonly string[];
25
+ /**
26
+ * Whether the platform offers comments here (its `app-comments` flag on, the app having the
27
+ * comments component, and not in its builder): when `false` no thread opens from inside the app,
28
+ * so its links back to one hide. Absent means offered: a platform from before the field sends
29
+ * none, and an app-render from before it ignores it.
30
+ */
31
+ isOffered?: boolean;
32
+ }
33
+ export declare const DEFAULT_COMMENT_OPTIONS: CommentOptions;
34
+ /** A thread to open in the app: on its page, scrolled to, open. */
35
+ export interface CommentThreadOpenRequest {
36
+ threadId: number;
37
+ /** The page the thread is on (`CommentThreadSummaryDto.routePath`). */
38
+ routePath: string;
39
+ }
40
+ /** How long turning comment mode on waits for a comments surface to mount. */
41
+ export declare const COMMENT_MODE_CLAIM_TIMEOUT_MS = 5000;
42
+ export interface CommentModeTimers {
43
+ setTimeout(callback: () => void, ms: number): ReturnType<typeof setTimeout>;
44
+ clearTimeout(id: ReturnType<typeof setTimeout>): void;
45
+ }
46
+ export interface CommentModeStore {
47
+ /** The host: turn comment mode on or off. On resolves false when no surface mounted in time. */
48
+ set(isOn: boolean): Promise<boolean>;
49
+ isOn(): boolean;
50
+ subscribe(listener: () => void): () => void;
51
+ /** The app: a comments surface is mounted while the returned function hasn't been called. */
52
+ registerSurface(): () => void;
53
+ /** The app: the surface turned comment mode off itself (Esc). The host hears it. */
54
+ exit(): void;
55
+ /** The host: hears when the app turned comment mode off itself. */
56
+ onExit(listener: () => void): () => void;
57
+ /** The host: what the comments show (resolved and outdated pins, agents to mention) and if they're offered. */
58
+ setOptions(options: CommentOptions): void;
59
+ options(): CommentOptions;
60
+ /** The app's surface: opens the threads asked for while the returned function hasn't been called. */
61
+ handleOpenThread(handler: (request: CommentThreadOpenRequest) => void): () => void;
62
+ /** The host: hears the app ask for comment mode (to show a thread it opens). */
63
+ onRequestOn(listener: () => void): () => void;
64
+ /**
65
+ * Whether a thread can be opened from inside the app: the host offers comments here (the options'
66
+ * `isOffered` isn't `false`), listens, and a surface opens it.
67
+ */
68
+ canOpenThread(): boolean;
69
+ /** The app: asks the host for comment mode and the surface for the thread. False when it can't here. */
70
+ openThread(request: CommentThreadOpenRequest): boolean;
71
+ }
72
+ export declare const createCommentModeStore: (timers?: CommentModeTimers) => CommentModeStore;
73
+ /** The one store the host and the app share. */
74
+ export declare const commentMode: CommentModeStore;
75
+ /** The host: turn comment mode on or off (resolves false when the app has no comments surface). */
76
+ export declare const setCommentMode: (isOn: boolean) => Promise<boolean>;
77
+ /** The host: hears when the app turned comment mode off itself. Returns the unsubscribe. */
78
+ export declare const onCommentModeExit: (listener: () => void) => (() => void);
79
+ /** The host: what the app's comments show (the Comment button's options), and whether they're offered. */
80
+ export declare const setCommentOptions: (next: CommentOptions) => void;
81
+ /** The host: hears the app ask for comment mode. Returns the unsubscribe. */
82
+ export declare const onCommentModeRequest: (listener: () => void) => (() => void);
83
+ /**
84
+ * The app (the agent chat's comment message): opens a thread — its page, comment mode on, the
85
+ * thread open. False when it can't here: outside the platform, where it doesn't offer comments (its
86
+ * builder), or without the comments component.
87
+ */
88
+ export declare const openCommentThread: (request: CommentThreadOpenRequest, store?: CommentModeStore) => boolean;
89
+ /** Whether `openCommentThread` can open a thread here: the link back to it shows only then. */
90
+ export declare const canOpenCommentThread: (store?: CommentModeStore) => boolean;
91
+ /**
92
+ * `canOpenCommentThread`, kept current: the link back to a thread re-renders when the host stops
93
+ * offering comments (its builder) or offers them again, rather than staying until something else
94
+ * renders it.
95
+ */
96
+ export declare const useCanOpenCommentThread: (store?: CommentModeStore) => boolean;
97
+ export interface UseCommentsSurfaceOptions {
98
+ /**
99
+ * Opens a thread `openCommentThread` asked for: navigate to its page with the thread's link
100
+ * (`?comment=<id>`), which the comments then open.
101
+ */
102
+ onOpenThread?: (request: CommentThreadOpenRequest) => void;
103
+ store?: CommentModeStore;
104
+ }
105
+ /**
106
+ * The app's comments surface: registers itself while mounted and reads the mode. `exit` turns it off
107
+ * from inside the app and tells the host. It opens the threads the app asks for (a comment's link in
108
+ * the agent chat) and reads the host's options.
109
+ */
110
+ export declare const useCommentsSurface: ({ onOpenThread, store }?: UseCommentsSurfaceOptions) => {
111
+ isCommentMode: boolean;
112
+ options: CommentOptions;
113
+ exit: () => void;
114
+ };
115
+ //# sourceMappingURL=comment-mode.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"comment-mode.d.ts","sourceRoot":"","sources":["../../src/comments/comment-mode.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,176 @@
1
+ import { useCallback, useEffect, useRef, useSyncExternalStore } from "react";
2
+ export const DEFAULT_COMMENT_OPTIONS = { showResolved: false };
3
+ const sameOptions = (a, b) => a.showResolved === b.showResolved &&
4
+ a.mentionableAgents?.join("\n") === b.mentionableAgents?.join("\n") &&
5
+ a.isOffered === b.isOffered;
6
+ /** How long turning comment mode on waits for a comments surface to mount. */
7
+ export const COMMENT_MODE_CLAIM_TIMEOUT_MS = 5000;
8
+ const defaultTimers = {
9
+ setTimeout: (callback, ms) => setTimeout(callback, ms),
10
+ clearTimeout: (id) => clearTimeout(id),
11
+ };
12
+ export const createCommentModeStore = (timers = defaultTimers) => {
13
+ let on = false;
14
+ let surfaces = 0;
15
+ let pending = null;
16
+ const listeners = new Set();
17
+ const exitListeners = new Set();
18
+ let options = DEFAULT_COMMENT_OPTIONS;
19
+ let openThreadHandler = null;
20
+ const requestOnListeners = new Set();
21
+ const emit = () => listeners.forEach((listener) => listener());
22
+ const canOpenThread = () => options.isOffered !== false && requestOnListeners.size > 0 && openThreadHandler !== null;
23
+ const settlePending = (claimed) => {
24
+ if (pending) {
25
+ timers.clearTimeout(pending.timer);
26
+ pending.resolve(claimed);
27
+ pending = null;
28
+ }
29
+ };
30
+ const turnOn = () => {
31
+ if (!on) {
32
+ on = true;
33
+ emit();
34
+ }
35
+ };
36
+ const turnOff = () => {
37
+ if (on) {
38
+ on = false;
39
+ emit();
40
+ }
41
+ };
42
+ return {
43
+ set(isOn) {
44
+ // A newer request supersedes one still waiting for a surface.
45
+ settlePending(false);
46
+ if (!isOn) {
47
+ turnOff();
48
+ return Promise.resolve(true);
49
+ }
50
+ if (surfaces > 0) {
51
+ turnOn();
52
+ return Promise.resolve(true);
53
+ }
54
+ return new Promise((resolve) => {
55
+ pending = {
56
+ resolve,
57
+ timer: timers.setTimeout(() => {
58
+ pending = null;
59
+ resolve(false);
60
+ }, COMMENT_MODE_CLAIM_TIMEOUT_MS),
61
+ };
62
+ });
63
+ },
64
+ isOn: () => on,
65
+ subscribe(listener) {
66
+ listeners.add(listener);
67
+ return () => listeners.delete(listener);
68
+ },
69
+ registerSurface() {
70
+ surfaces += 1;
71
+ if (pending) {
72
+ turnOn();
73
+ settlePending(true);
74
+ }
75
+ let registered = true;
76
+ return () => {
77
+ if (!registered) {
78
+ return;
79
+ }
80
+ registered = false;
81
+ surfaces -= 1;
82
+ if (surfaces === 0) {
83
+ turnOff();
84
+ }
85
+ };
86
+ },
87
+ exit() {
88
+ if (on) {
89
+ turnOff();
90
+ exitListeners.forEach((listener) => listener());
91
+ }
92
+ },
93
+ onExit(listener) {
94
+ exitListeners.add(listener);
95
+ return () => exitListeners.delete(listener);
96
+ },
97
+ setOptions(next) {
98
+ if (!sameOptions(next, options)) {
99
+ options = next;
100
+ emit();
101
+ }
102
+ },
103
+ options: () => options,
104
+ // Both decide `canOpenThread`: its subscribers hear each change.
105
+ handleOpenThread(handler) {
106
+ openThreadHandler = handler;
107
+ emit();
108
+ return () => {
109
+ if (openThreadHandler === handler) {
110
+ openThreadHandler = null;
111
+ emit();
112
+ }
113
+ };
114
+ },
115
+ onRequestOn(listener) {
116
+ requestOnListeners.add(listener);
117
+ emit();
118
+ return () => {
119
+ if (requestOnListeners.delete(listener)) {
120
+ emit();
121
+ }
122
+ };
123
+ },
124
+ canOpenThread,
125
+ openThread(request) {
126
+ if (!canOpenThread()) {
127
+ return false;
128
+ }
129
+ requestOnListeners.forEach((listener) => listener());
130
+ openThreadHandler?.(request);
131
+ return true;
132
+ },
133
+ };
134
+ };
135
+ /** The one store the host and the app share. */
136
+ export const commentMode = createCommentModeStore();
137
+ /** The host: turn comment mode on or off (resolves false when the app has no comments surface). */
138
+ export const setCommentMode = (isOn) => commentMode.set(isOn);
139
+ /** The host: hears when the app turned comment mode off itself. Returns the unsubscribe. */
140
+ export const onCommentModeExit = (listener) => commentMode.onExit(listener);
141
+ /** The host: what the app's comments show (the Comment button's options), and whether they're offered. */
142
+ export const setCommentOptions = (next) => commentMode.setOptions(next);
143
+ /** The host: hears the app ask for comment mode. Returns the unsubscribe. */
144
+ export const onCommentModeRequest = (listener) => commentMode.onRequestOn(listener);
145
+ /**
146
+ * The app (the agent chat's comment message): opens a thread — its page, comment mode on, the
147
+ * thread open. False when it can't here: outside the platform, where it doesn't offer comments (its
148
+ * builder), or without the comments component.
149
+ */
150
+ export const openCommentThread = (request, store = commentMode) => store.openThread(request);
151
+ /** Whether `openCommentThread` can open a thread here: the link back to it shows only then. */
152
+ export const canOpenCommentThread = (store = commentMode) => store.canOpenThread();
153
+ /**
154
+ * `canOpenCommentThread`, kept current: the link back to a thread re-renders when the host stops
155
+ * offering comments (its builder) or offers them again, rather than staying until something else
156
+ * renders it.
157
+ */
158
+ export const useCanOpenCommentThread = (store = commentMode) => useSyncExternalStore(store.subscribe, store.canOpenThread, () => false);
159
+ /**
160
+ * The app's comments surface: registers itself while mounted and reads the mode. `exit` turns it off
161
+ * from inside the app and tells the host. It opens the threads the app asks for (a comment's link in
162
+ * the agent chat) and reads the host's options.
163
+ */
164
+ export const useCommentsSurface = ({ onOpenThread, store = commentMode } = {}) => {
165
+ useEffect(() => store.registerSurface(), [store]);
166
+ const latestOpenThread = useRef(onOpenThread);
167
+ useEffect(() => {
168
+ latestOpenThread.current = onOpenThread;
169
+ });
170
+ const isHandlingOpenThread = !!onOpenThread;
171
+ useEffect(() => (isHandlingOpenThread ? store.handleOpenThread((request) => latestOpenThread.current?.(request)) : undefined), [isHandlingOpenThread, store]);
172
+ const isCommentMode = useSyncExternalStore(store.subscribe, store.isOn, () => false);
173
+ const options = useSyncExternalStore(store.subscribe, store.options, () => DEFAULT_COMMENT_OPTIONS);
174
+ const exit = useCallback(() => store.exit(), [store]);
175
+ return { isCommentMode, options, exit };
176
+ };
@@ -0,0 +1,133 @@
1
+ import { type RefObject } from "react";
2
+ import { type Mention } from "./mentions.js";
3
+ import type { MentionableUserDto } from "./types.js";
4
+ /**
5
+ * The comment composer's logic, headless: the text, the mentions in it, the `@` menu, the "Send to"
6
+ * agent line, and the body to store. The registry's composer only draws it.
7
+ *
8
+ * Each mention is a range of the text (`@Name`, exactly) tied to who it names, so two people with
9
+ * the same name stay apart and a typed `@Dana` is not a mention until picked. Every edit moves the
10
+ * ranges: an edit before one shifts it, an edit inside one turns it back into plain text, and
11
+ * Backspace right after one removes it whole.
12
+ */
13
+ export interface ComposerMention {
14
+ start: number;
15
+ /** Exclusive: `text.slice(start, end)` is `@` + the mention's name. */
16
+ end: number;
17
+ mention: Mention;
18
+ }
19
+ export interface ComposerState {
20
+ text: string;
21
+ /** In text order, never overlapping. */
22
+ mentions: ComposerMention[];
23
+ }
24
+ export type ComposerSegment = {
25
+ kind: "text";
26
+ text: string;
27
+ } | {
28
+ kind: "mention";
29
+ text: string;
30
+ mention: Mention;
31
+ };
32
+ export declare const EMPTY_COMPOSER: ComposerState;
33
+ /**
34
+ * The text after an edit, with its mentions moved. `caret` is where the caret is after the edit:
35
+ * what follows it is taken as unchanged, which places a typed or pasted run, or a deletion, exactly
36
+ * (a plain diff can't tell which of two equal letters was typed).
37
+ */
38
+ export declare const applyEdit: (state: ComposerState, text: string, caret?: number) => ComposerState;
39
+ /** Picks a mention for the `@query` typed at `[queryStart, caret)`: `@Name ` in its place. */
40
+ export declare const insertMention: (state: ComposerState, queryStart: number, caret: number, mention: Mention) => {
41
+ state: ComposerState;
42
+ caret: number;
43
+ };
44
+ /** Backspace with the caret right after a mention: the whole mention goes. Null when it isn't one. */
45
+ export declare const deleteMentionBefore: (state: ComposerState, caret: number) => {
46
+ state: ComposerState;
47
+ caret: number;
48
+ } | null;
49
+ /** Whether the text mentions this agent. */
50
+ export declare const mentionsAgent: (state: ComposerState, agentId: string) => boolean;
51
+ /** Mentions an agent at the start of the text (the "Send to" line), unless the text already does. */
52
+ export declare const addAgentMention: (state: ComposerState, agent: {
53
+ agentId: string;
54
+ name: string;
55
+ }) => ComposerState;
56
+ /** Removes every mention of an agent, with the space after each. */
57
+ export declare const removeAgentMention: (state: ComposerState, agentId: string) => ComposerState;
58
+ /** The text in runs — plain text, and mentions — for the highlight drawn behind the text. */
59
+ export declare const composerSegments: (state: ComposerState) => ComposerSegment[];
60
+ /** The body to store: each mention as its token (`@[42]`, `@[agent:Name]`), the rest as typed. */
61
+ export declare const composerBody: (state: ComposerState) => string;
62
+ /** An agent the composer can mention: one of the app's floating-chat agents. */
63
+ export interface ComposerAgent {
64
+ id: string;
65
+ name: string;
66
+ photoUrl?: string | null;
67
+ }
68
+ export type ComposerOption = Mention & {
69
+ photoUrl?: string | null;
70
+ };
71
+ /**
72
+ * How well a name matches what was typed after `@`: 0 when the name starts with it, 1 when one of
73
+ * its words does, 2 for any other match (inside a word, an email). The menu lists better matches
74
+ * first, so "@ol" puts Olive before Marina Sokolov.
75
+ */
76
+ export declare const mentionRank: (name: string, query: string) => number;
77
+ /**
78
+ * The `@` menu for what was typed: the app's agents first, then people (never yourself), each group
79
+ * ranked by `mentionRank` and capped (3 agents, 5 people). `separatorBefore` is the index of the
80
+ * first person when both groups have matches, where the menu draws its divider.
81
+ */
82
+ export declare const mentionOptions: ({ people, agents, currentUserId, query, }: {
83
+ people: readonly MentionableUserDto[];
84
+ agents: readonly ComposerAgent[];
85
+ currentUserId: number | null;
86
+ query: string;
87
+ }) => {
88
+ options: ComposerOption[];
89
+ separatorBefore: number | null;
90
+ };
91
+ export interface UseMentionComposerOptions {
92
+ people: readonly MentionableUserDto[];
93
+ agents: readonly ComposerAgent[];
94
+ /** Left out of the `@` menu: nobody mentions themselves. */
95
+ currentUserId: number | null;
96
+ textareaRef: RefObject<HTMLTextAreaElement | null>;
97
+ }
98
+ export interface MentionComposer {
99
+ text: string;
100
+ segments: ComposerSegment[];
101
+ /** What to store; empty when there's nothing to send. */
102
+ body: string;
103
+ /** The `@` menu's options (agents, then people), while an `@query` is being typed. */
104
+ options: ComposerOption[];
105
+ /** The index of the first person when agents and people both match: draw a divider before it. */
106
+ separatorBefore: number | null;
107
+ highlighted: number;
108
+ setHighlighted: (index: number) => void;
109
+ /** Arrow keys in the menu. */
110
+ moveHighlight: (step: 1 | -1) => void;
111
+ pick: (option: ComposerOption) => void;
112
+ /** Esc in the menu. */
113
+ closeMenu: () => void;
114
+ isMenuOpen: boolean;
115
+ /** The textarea's change: its new value and caret. */
116
+ onChange: (text: string, caret: number) => void;
117
+ /**
118
+ * Backspace, before the browser acts on it: right after a mention, selects the whole mention so
119
+ * the key deletes it (undoably). The caller lets the key go on.
120
+ */
121
+ onBackspace: (selectionStart: number, selectionEnd: number) => void;
122
+ /** The "Send to" line: the agent it shows — the first one mentioned, else the first agent. */
123
+ sendToAgent: ComposerAgent | null;
124
+ isSendingToAgent: boolean;
125
+ /** Checks or unchecks "Send to": mentions the agent, or removes its mentions. */
126
+ setSendingToAgent: (isOn: boolean) => void;
127
+ /** Swaps the "Send to" agent; a mention of the old one becomes one of the new one. */
128
+ chooseSendToAgent: (agentId: string) => void;
129
+ reset: () => void;
130
+ }
131
+ /** The composer's state and actions; see the module comment. */
132
+ export declare const useMentionComposer: ({ people, agents, currentUserId, textareaRef, }: UseMentionComposerOptions) => MentionComposer;
133
+ //# sourceMappingURL=composer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"composer.d.ts","sourceRoot":"","sources":["../../src/comments/composer.ts"],"names":[],"mappings":"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"}