@elizaos/plugin-messages 2.0.3-beta.6 → 2.0.3-beta.7

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 (57) hide show
  1. package/dist/components/MessagesAppView.d.ts +3 -0
  2. package/dist/components/MessagesAppView.d.ts.map +1 -0
  3. package/dist/components/MessagesAppView.helpers.d.ts +20 -0
  4. package/dist/components/MessagesAppView.helpers.d.ts.map +1 -0
  5. package/dist/components/MessagesAppView.helpers.js +56 -0
  6. package/dist/components/MessagesAppView.helpers.js.map +1 -0
  7. package/dist/components/MessagesAppView.interact.d.ts +2 -0
  8. package/dist/components/MessagesAppView.interact.d.ts.map +1 -0
  9. package/dist/components/MessagesAppView.interact.js +49 -0
  10. package/dist/components/MessagesAppView.interact.js.map +1 -0
  11. package/dist/components/MessagesAppView.js +642 -0
  12. package/dist/components/MessagesAppView.js.map +1 -0
  13. package/dist/components/MessagesSpatialView.d.ts +47 -0
  14. package/dist/components/MessagesSpatialView.d.ts.map +1 -0
  15. package/dist/components/MessagesSpatialView.js +152 -0
  16. package/dist/components/MessagesSpatialView.js.map +1 -0
  17. package/dist/components/MessagesView.d.ts +13 -0
  18. package/dist/components/MessagesView.d.ts.map +1 -0
  19. package/dist/components/MessagesView.js +144 -0
  20. package/dist/components/MessagesView.js.map +1 -0
  21. package/dist/components/messages-app.d.ts +5 -0
  22. package/dist/components/messages-app.d.ts.map +1 -0
  23. package/dist/components/messages-app.js +20 -0
  24. package/dist/components/messages-app.js.map +1 -0
  25. package/dist/components/messages-view-bundle.d.ts +3 -0
  26. package/dist/components/messages-view-bundle.d.ts.map +1 -0
  27. package/dist/components/messages-view-bundle.js +7 -0
  28. package/dist/components/messages-view-bundle.js.map +1 -0
  29. package/dist/index.d.ts +7 -0
  30. package/dist/index.d.ts.map +1 -0
  31. package/dist/index.js +20 -0
  32. package/dist/index.js.map +1 -0
  33. package/dist/plugin.d.ts +4 -0
  34. package/dist/plugin.d.ts.map +1 -0
  35. package/dist/plugin.js +29 -0
  36. package/dist/plugin.js.map +1 -0
  37. package/dist/register-terminal-view.d.ts +15 -0
  38. package/dist/register-terminal-view.d.ts.map +1 -0
  39. package/dist/register-terminal-view.js +28 -0
  40. package/dist/register-terminal-view.js.map +1 -0
  41. package/dist/register.d.ts +2 -0
  42. package/dist/register.d.ts.map +1 -0
  43. package/dist/register.js +10 -0
  44. package/dist/register.js.map +1 -0
  45. package/dist/ui.d.ts +4 -0
  46. package/dist/ui.d.ts.map +1 -0
  47. package/dist/ui.js +15 -0
  48. package/dist/ui.js.map +1 -0
  49. package/dist/views/bundle.js +338 -0
  50. package/dist/views/bundle.js.map +1 -0
  51. package/dist/views/dist-Cd2YtKy4.js +270 -0
  52. package/dist/views/dist-Cd2YtKy4.js.map +1 -0
  53. package/dist/views/web-BNoqOavR.js +90 -0
  54. package/dist/views/web-BNoqOavR.js.map +1 -0
  55. package/dist/views/web-DeLuzxfk.js +32 -0
  56. package/dist/views/web-DeLuzxfk.js.map +1 -0
  57. package/package.json +8 -8
@@ -0,0 +1,642 @@
1
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
2
+ import { Messages } from "@elizaos/capacitor-messages";
3
+ import { System } from "@elizaos/capacitor-system";
4
+ import { useAgentElement } from "@elizaos/ui/agent-surface";
5
+ import { consumePendingMessageRecipient } from "@elizaos/ui/app-navigate-view";
6
+ import { PermissionRecoveryCallout } from "@elizaos/ui/components";
7
+ import { Button } from "@elizaos/ui/components/ui/button";
8
+ import { Input } from "@elizaos/ui/components/ui/input";
9
+ import { Textarea } from "@elizaos/ui/components/ui/textarea";
10
+ import {
11
+ ArrowLeft,
12
+ ChevronLeft,
13
+ MessageSquareText,
14
+ Plus,
15
+ Send,
16
+ ShieldCheck
17
+ } from "lucide-react";
18
+ import {
19
+ memo,
20
+ useCallback,
21
+ useEffect,
22
+ useMemo,
23
+ useState
24
+ } from "react";
25
+ import {
26
+ buildThreads,
27
+ smsRole
28
+ } from "./MessagesAppView.helpers.js";
29
+ const SENT_SMS_TYPE = 2;
30
+ function formatTime(epochMs) {
31
+ const date = new Date(epochMs);
32
+ if (Number.isNaN(date.getTime())) return "";
33
+ const now = /* @__PURE__ */ new Date();
34
+ const sameDay = date.getFullYear() === now.getFullYear() && date.getMonth() === now.getMonth() && date.getDate() === now.getDate();
35
+ if (sameDay) {
36
+ return date.toLocaleTimeString(void 0, {
37
+ hour: "2-digit",
38
+ minute: "2-digit"
39
+ });
40
+ }
41
+ return date.toLocaleDateString(void 0, {
42
+ month: "short",
43
+ day: "numeric"
44
+ });
45
+ }
46
+ function threadInitial(address) {
47
+ const trimmed = address.trim();
48
+ if (trimmed.length === 0) return "#";
49
+ const firstLetter = trimmed.split("").find((ch) => /[a-z]/i.test(ch));
50
+ if (firstLetter) return firstLetter.toUpperCase();
51
+ const firstDigit = trimmed.replace(/[^0-9]/g, "").slice(-1);
52
+ return firstDigit || "#";
53
+ }
54
+ function isMessagesPermissionError(message) {
55
+ const normalized = message.toLowerCase();
56
+ return normalized.includes("permission") || normalized.includes("denied") || normalized.includes("access is needed") || normalized.includes("read_sms") || normalized.includes("send_sms");
57
+ }
58
+ function StatChip({
59
+ icon,
60
+ label,
61
+ accent = false
62
+ }) {
63
+ return /* @__PURE__ */ jsxs(
64
+ "span",
65
+ {
66
+ className: "inline-flex items-center gap-1.5 px-1 py-1 text-xs font-medium",
67
+ style: {
68
+ color: accent ? "var(--accent)" : "var(--muted)"
69
+ },
70
+ children: [
71
+ icon ? /* @__PURE__ */ jsx(
72
+ "span",
73
+ {
74
+ "aria-hidden": true,
75
+ className: "flex h-3.5 w-3.5 items-center justify-center",
76
+ children: icon
77
+ }
78
+ ) : null,
79
+ label
80
+ ]
81
+ }
82
+ );
83
+ }
84
+ function ChatBubblesMotif() {
85
+ return /* @__PURE__ */ jsxs("svg", { width: "96", height: "96", viewBox: "0 0 96 96", fill: "none", role: "img", children: [
86
+ /* @__PURE__ */ jsx("title", { children: "Chat bubbles" }),
87
+ /* @__PURE__ */ jsx(
88
+ "rect",
89
+ {
90
+ x: "10",
91
+ y: "20",
92
+ width: "56",
93
+ height: "34",
94
+ rx: "12",
95
+ fill: "var(--accent-subtle)",
96
+ stroke: "var(--accent)",
97
+ strokeWidth: "2"
98
+ }
99
+ ),
100
+ /* @__PURE__ */ jsx("path", { d: "M24 54 L24 64 L36 54 Z", fill: "var(--accent-subtle)" }),
101
+ /* @__PURE__ */ jsx(
102
+ "rect",
103
+ {
104
+ x: "38",
105
+ y: "44",
106
+ width: "48",
107
+ height: "30",
108
+ rx: "11",
109
+ fill: "var(--surface)",
110
+ stroke: "var(--border)",
111
+ strokeWidth: "2"
112
+ }
113
+ ),
114
+ /* @__PURE__ */ jsx("path", { d: "M72 74 L72 82 L62 74 Z", fill: "var(--surface)" }),
115
+ /* @__PURE__ */ jsx("circle", { cx: "24", cy: "37", r: "2.5", fill: "var(--accent)" }),
116
+ /* @__PURE__ */ jsx("circle", { cx: "34", cy: "37", r: "2.5", fill: "var(--accent)" }),
117
+ /* @__PURE__ */ jsx("circle", { cx: "44", cy: "37", r: "2.5", fill: "var(--accent)" }),
118
+ /* @__PURE__ */ jsx("circle", { cx: "56", cy: "59", r: "2.5", fill: "var(--muted)" }),
119
+ /* @__PURE__ */ jsx("circle", { cx: "66", cy: "59", r: "2.5", fill: "var(--muted)" }),
120
+ /* @__PURE__ */ jsx("circle", { cx: "76", cy: "59", r: "2.5", fill: "var(--muted)" })
121
+ ] });
122
+ }
123
+ const MessagesThreadButton = memo(function MessagesThreadButton2({
124
+ thread,
125
+ selected,
126
+ onOpen
127
+ }) {
128
+ const { ref, agentProps } = useAgentElement({
129
+ id: `thread-${thread.id}`,
130
+ role: "list-item",
131
+ label: thread.address || "Unknown",
132
+ group: "messages-threads",
133
+ status: selected ? "active" : void 0,
134
+ description: `Open the SMS thread with ${thread.address || "Unknown"}`
135
+ });
136
+ return /* @__PURE__ */ jsxs(
137
+ "button",
138
+ {
139
+ ref,
140
+ ...agentProps,
141
+ type: "button",
142
+ onClick: () => onOpen(thread),
143
+ "aria-current": selected ? "true" : void 0,
144
+ className: "flex w-full items-start gap-3 px-3 py-2.5 text-left transition-colors focus:outline-none",
145
+ style: selected ? { background: "var(--accent-subtle)" } : void 0,
146
+ "data-testid": `messages-thread-${thread.id}`,
147
+ children: [
148
+ /* @__PURE__ */ jsx(
149
+ "span",
150
+ {
151
+ className: "flex h-10 w-10 shrink-0 items-center justify-center rounded-full text-sm font-semibold",
152
+ style: { background: "var(--accent-subtle)", color: "var(--accent)" },
153
+ children: threadInitial(thread.address)
154
+ }
155
+ ),
156
+ /* @__PURE__ */ jsxs("span", { className: "min-w-0 flex-1", children: [
157
+ /* @__PURE__ */ jsxs("span", { className: "flex items-center justify-between gap-2", children: [
158
+ /* @__PURE__ */ jsx("span", { className: "truncate text-sm font-semibold text-txt", children: thread.address || "Unknown" }),
159
+ /* @__PURE__ */ jsx("span", { className: "shrink-0 px-1 py-0.5 text-2xs text-muted", children: formatTime(thread.lastMessage.date) })
160
+ ] }),
161
+ /* @__PURE__ */ jsx("span", { className: "mt-1 line-clamp-2 text-xs text-muted", children: thread.lastMessage.body })
162
+ ] }),
163
+ thread.unreadCount > 0 ? /* @__PURE__ */ jsx(
164
+ "span",
165
+ {
166
+ className: "rounded-full px-1.5 py-0.5 text-2xs font-semibold",
167
+ style: {
168
+ background: "var(--accent)",
169
+ color: "var(--accent-foreground)"
170
+ },
171
+ children: thread.unreadCount
172
+ }
173
+ ) : null
174
+ ]
175
+ }
176
+ );
177
+ });
178
+ function MessagesAppView({ exitToApps, t }) {
179
+ const [messages, setMessages] = useState([]);
180
+ const [systemStatus, setSystemStatus] = useState(null);
181
+ const [selectedThreadId, setSelectedThreadId] = useState(null);
182
+ const [composeAddress, setComposeAddress] = useState("");
183
+ const [composeBody, setComposeBody] = useState("");
184
+ const [loading, setLoading] = useState(true);
185
+ const [sending, setSending] = useState(false);
186
+ const [requestingRole, setRequestingRole] = useState(false);
187
+ const [error, setError] = useState(null);
188
+ const [notice, setNotice] = useState(null);
189
+ const [showComposer, setShowComposer] = useState(false);
190
+ const refresh = useCallback(async () => {
191
+ setLoading(true);
192
+ setError(null);
193
+ try {
194
+ const statusResult = await System.getStatus().catch(() => null);
195
+ setSystemStatus(statusResult);
196
+ const perm = await Messages.requestPermissions().catch(() => null);
197
+ if (perm && perm.sms !== "granted") {
198
+ setMessages([]);
199
+ setError(
200
+ "SMS access is needed to read and send messages. Grant it in your device settings, then retry."
201
+ );
202
+ return;
203
+ }
204
+ const messageResult = await Messages.listMessages({ limit: 200 });
205
+ setMessages(messageResult.messages);
206
+ } catch (err) {
207
+ setError(err instanceof Error ? err.message : String(err));
208
+ setMessages([]);
209
+ } finally {
210
+ setLoading(false);
211
+ }
212
+ }, []);
213
+ useEffect(() => {
214
+ void refresh();
215
+ const interval = setInterval(() => void refresh(), 2e4);
216
+ return () => clearInterval(interval);
217
+ }, [refresh]);
218
+ useEffect(() => {
219
+ const pending = consumePendingMessageRecipient();
220
+ if (pending) {
221
+ setSelectedThreadId(null);
222
+ setComposeAddress(pending);
223
+ setComposeBody("");
224
+ setShowComposer(true);
225
+ setNotice(null);
226
+ setError(null);
227
+ }
228
+ }, []);
229
+ const threads = useMemo(() => buildThreads(messages), [messages]);
230
+ const selectedThread = useMemo(
231
+ () => threads.find((thread) => thread.id === selectedThreadId) ?? null,
232
+ [selectedThreadId, threads]
233
+ );
234
+ const currentSmsRole = smsRole(systemStatus);
235
+ const ownsSmsRole = currentSmsRole?.held === true;
236
+ const unreadTotal = threads.reduce(
237
+ (total, thread) => total + thread.unreadCount,
238
+ 0
239
+ );
240
+ const canSend = composeAddress.trim().length > 0 && composeBody.trim().length > 0 && !sending;
241
+ const openThread = useCallback((thread) => {
242
+ setSelectedThreadId(thread.id);
243
+ setComposeAddress(thread.address);
244
+ setShowComposer(true);
245
+ setNotice(null);
246
+ setError(null);
247
+ }, []);
248
+ const openNewComposer = useCallback(() => {
249
+ setSelectedThreadId(null);
250
+ setComposeAddress("");
251
+ setComposeBody("");
252
+ setShowComposer(true);
253
+ setNotice(null);
254
+ setError(null);
255
+ }, []);
256
+ const backToThreads = useCallback(() => {
257
+ setShowComposer(false);
258
+ setSelectedThreadId(null);
259
+ }, []);
260
+ const requestSmsRole = useCallback(async () => {
261
+ setRequestingRole(true);
262
+ setError(null);
263
+ try {
264
+ await System.requestRole({ role: "sms" });
265
+ const next = await System.getStatus();
266
+ setSystemStatus(next);
267
+ } catch (err) {
268
+ setError(err instanceof Error ? err.message : String(err));
269
+ } finally {
270
+ setRequestingRole(false);
271
+ }
272
+ }, []);
273
+ const send = useCallback(async () => {
274
+ if (!canSend) return;
275
+ setSending(true);
276
+ setError(null);
277
+ setNotice(null);
278
+ try {
279
+ await Messages.sendSms({
280
+ address: composeAddress.trim(),
281
+ body: composeBody.trim()
282
+ });
283
+ setComposeBody("");
284
+ setNotice("Message sent.");
285
+ await refresh();
286
+ } catch (err) {
287
+ setError(err instanceof Error ? err.message : String(err));
288
+ } finally {
289
+ setSending(false);
290
+ }
291
+ }, [canSend, composeAddress, composeBody, refresh]);
292
+ const title = showComposer ? selectedThread?.address || t("messages.new", { defaultValue: "New message" }) : t("messages.title", { defaultValue: "Messages" });
293
+ const backLabel = showComposer ? t("messages.backToThreads", { defaultValue: "Back to threads" }) : t("nav.back", { defaultValue: "Back" });
294
+ const back = useAgentElement({
295
+ id: "action-back",
296
+ role: "button",
297
+ label: backLabel,
298
+ group: "messages-header",
299
+ description: showComposer ? "Return from the composer to the thread list" : "Leave Messages and return to the apps grid"
300
+ });
301
+ const newMessage = useAgentElement({
302
+ id: "action-new-message",
303
+ role: "button",
304
+ label: t("messages.newShort", { defaultValue: "New" }),
305
+ group: "messages-header",
306
+ description: "Open the composer to start a new text message"
307
+ });
308
+ const emptyNewMessage = useAgentElement({
309
+ id: "action-empty-new-message",
310
+ role: "button",
311
+ label: t("messages.new", { defaultValue: "New message" }),
312
+ group: "messages-threads",
313
+ description: "Start a new text message from the empty thread list"
314
+ });
315
+ const requestRole = useAgentElement({
316
+ id: "action-set-default-sms",
317
+ role: "button",
318
+ label: t("messages.setDefaultSms", { defaultValue: "Set default SMS" }),
319
+ group: "messages-sms-role",
320
+ description: "Request the Android default SMS role for this app"
321
+ });
322
+ const addressInput = useAgentElement({
323
+ id: "input-recipient",
324
+ role: "text-input",
325
+ label: t("messages.to", { defaultValue: "To" }),
326
+ group: "messages-composer",
327
+ description: "Recipient phone number for the outgoing text message",
328
+ getValue: () => composeAddress,
329
+ onFill: setComposeAddress
330
+ });
331
+ const bodyInput = useAgentElement({
332
+ id: "input-message-body",
333
+ role: "textarea",
334
+ label: t("messages.placeholder", { defaultValue: "Message" }),
335
+ group: "messages-composer",
336
+ description: "Body text of the outgoing message",
337
+ getValue: () => composeBody,
338
+ onFill: setComposeBody
339
+ });
340
+ const sendButton = useAgentElement({
341
+ id: "action-send",
342
+ role: "button",
343
+ label: t("messages.send", { defaultValue: "Send" }),
344
+ group: "messages-composer",
345
+ description: "Send the composed text message"
346
+ });
347
+ return /* @__PURE__ */ jsxs(
348
+ "div",
349
+ {
350
+ "data-testid": "messages-shell",
351
+ className: "fixed inset-0 z-50 flex h-[100vh] flex-col overflow-hidden bg-bg pb-[var(--safe-area-bottom,0px)] pl-[var(--safe-area-left,0px)] pr-[var(--safe-area-right,0px)] pt-[var(--safe-area-top,0px)] supports-[height:100dvh]:h-[100dvh]",
352
+ children: [
353
+ /* @__PURE__ */ jsxs("header", { className: "flex shrink-0 items-center justify-between gap-3 px-3 py-2", children: [
354
+ /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 items-center gap-3", children: [
355
+ /* @__PURE__ */ jsx(
356
+ Button,
357
+ {
358
+ ref: back.ref,
359
+ ...back.agentProps,
360
+ variant: "ghost",
361
+ size: "icon",
362
+ className: "h-9 w-9 shrink-0 text-muted hover:text-txt",
363
+ onClick: showComposer ? backToThreads : exitToApps,
364
+ "aria-label": backLabel,
365
+ children: showComposer ? /* @__PURE__ */ jsx(ChevronLeft, { className: "h-4 w-4" }) : /* @__PURE__ */ jsx(ArrowLeft, { className: "h-4 w-4" })
366
+ }
367
+ ),
368
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
369
+ /* @__PURE__ */ jsx("h1", { className: "truncate text-base font-semibold text-txt", children: title }),
370
+ /* @__PURE__ */ jsx("p", { className: "sr-only truncate text-xs text-muted", children: ownsSmsRole ? t("messages.smsReady", { defaultValue: "Default SMS app" }) : t("messages.smsBridge", {
371
+ defaultValue: "Android SMS bridge"
372
+ }) })
373
+ ] })
374
+ ] }),
375
+ /* @__PURE__ */ jsx("div", { className: "flex shrink-0 items-center gap-2", children: /* @__PURE__ */ jsxs(
376
+ Button,
377
+ {
378
+ ref: newMessage.ref,
379
+ ...newMessage.agentProps,
380
+ variant: "default",
381
+ size: "sm",
382
+ className: "px-3",
383
+ onClick: openNewComposer,
384
+ "data-testid": "messages-new",
385
+ children: [
386
+ /* @__PURE__ */ jsx(Plus, { className: "mr-1.5 h-4 w-4" }),
387
+ t("messages.newShort", { defaultValue: "New" })
388
+ ]
389
+ }
390
+ ) })
391
+ ] }),
392
+ currentSmsRole && !ownsSmsRole ? /* @__PURE__ */ jsx("div", { className: "shrink-0 px-3 py-2", children: /* @__PURE__ */ jsxs("div", { className: "mx-auto flex max-w-5xl flex-col gap-3 md:flex-row md:items-center md:justify-between", children: [
393
+ /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-3 text-sm", children: [
394
+ /* @__PURE__ */ jsx(ShieldCheck, { className: "mt-0.5 h-4 w-4 shrink-0 text-muted" }),
395
+ /* @__PURE__ */ jsxs("div", { children: [
396
+ /* @__PURE__ */ jsx("div", { className: "font-medium text-txt", children: t("messages.smsRoleTitle", {
397
+ defaultValue: "SMS role"
398
+ }) }),
399
+ /* @__PURE__ */ jsx("div", { className: "sr-only text-xs text-muted", children: t("messages.smsRoleBody", {
400
+ defaultValue: "Reading and sending real SMS requires Android to grant the default SMS role."
401
+ }) })
402
+ ] })
403
+ ] }),
404
+ /* @__PURE__ */ jsx(
405
+ Button,
406
+ {
407
+ ref: requestRole.ref,
408
+ ...requestRole.agentProps,
409
+ variant: "outline",
410
+ size: "sm",
411
+ onClick: requestSmsRole,
412
+ disabled: requestingRole,
413
+ "data-testid": "messages-request-sms-role",
414
+ children: requestingRole ? t("messages.requesting", { defaultValue: "Requesting\u2026" }) : t("messages.setDefaultSms", {
415
+ defaultValue: "Set default SMS"
416
+ })
417
+ }
418
+ )
419
+ ] }) }) : null,
420
+ error && isMessagesPermissionError(error) ? /* @__PURE__ */ jsx("div", { className: "shrink-0 px-4 pt-3", children: /* @__PURE__ */ jsx(
421
+ PermissionRecoveryCallout,
422
+ {
423
+ permission: "messages",
424
+ title: t("messages.permissionTitle", {
425
+ defaultValue: "SMS access is off"
426
+ }),
427
+ description: error,
428
+ onRetry: refresh,
429
+ retryLabel: t("actions.retry", { defaultValue: "Try again" }),
430
+ className: "mx-auto max-w-5xl",
431
+ testId: "messages-permission-callout"
432
+ }
433
+ ) }) : error || notice ? /* @__PURE__ */ jsx("div", { className: "shrink-0 px-4 pt-3", children: /* @__PURE__ */ jsx(
434
+ "div",
435
+ {
436
+ role: error ? "alert" : "status",
437
+ className: `mx-auto max-w-5xl px-1 py-2 text-sm ${error ? "text-danger" : "text-muted"}`,
438
+ children: error ?? notice
439
+ }
440
+ ) }) : null,
441
+ /* @__PURE__ */ jsxs("main", { className: "flex min-h-0 flex-1 flex-col", children: [
442
+ /* @__PURE__ */ jsx(
443
+ "section",
444
+ {
445
+ className: `min-h-0 flex-1 flex-col ${showComposer ? "hidden" : "flex"}`,
446
+ "data-testid": "messages-thread-list",
447
+ children: loading && threads.length === 0 ? /* @__PURE__ */ jsx("div", { className: "flex flex-1 items-center justify-center px-4 text-sm text-muted", children: t("messages.loading", { defaultValue: "Loading" }) }) : threads.length === 0 ? /* @__PURE__ */ jsxs("div", { className: "flex flex-1 flex-col items-center justify-center px-6 pb-32 text-center", children: [
448
+ /* @__PURE__ */ jsx("span", { className: "flex h-20 w-20 items-center justify-center", children: /* @__PURE__ */ jsx(ChatBubblesMotif, {}) }),
449
+ /* @__PURE__ */ jsx("h2", { className: "mt-5 text-base font-semibold text-txt", children: t("messages.empty.title", { defaultValue: "None" }) }),
450
+ /* @__PURE__ */ jsx("p", { className: "sr-only mt-1 max-w-xs text-sm text-muted", children: t("messages.empty.body", {
451
+ defaultValue: "Start a conversation \u2014 texts you send and receive show up here."
452
+ }) }),
453
+ /* @__PURE__ */ jsx("div", { className: "sr-only mt-4 flex flex-wrap items-center justify-center gap-2", children: /* @__PURE__ */ jsx(
454
+ StatChip,
455
+ {
456
+ label: t("messages.threadCount", {
457
+ defaultValue: "0 threads"
458
+ })
459
+ }
460
+ ) }),
461
+ /* @__PURE__ */ jsxs(
462
+ "button",
463
+ {
464
+ ref: emptyNewMessage.ref,
465
+ ...emptyNewMessage.agentProps,
466
+ type: "button",
467
+ onClick: openNewComposer,
468
+ className: "mt-6 inline-flex items-center gap-2 px-5 py-2.5 text-sm font-semibold transition-colors",
469
+ style: {
470
+ background: "var(--accent)",
471
+ color: "var(--accent-foreground)"
472
+ },
473
+ onMouseEnter: (e) => {
474
+ e.currentTarget.style.background = "var(--accent-hover)";
475
+ },
476
+ onMouseLeave: (e) => {
477
+ e.currentTarget.style.background = "var(--accent)";
478
+ },
479
+ children: [
480
+ /* @__PURE__ */ jsx(Plus, { className: "h-4 w-4" }),
481
+ t("messages.new", { defaultValue: "New message" })
482
+ ]
483
+ }
484
+ )
485
+ ] }) : /* @__PURE__ */ jsxs("div", { className: "chat-native-scrollbar min-h-0 flex-1 overflow-y-auto pb-32", children: [
486
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-3 px-3 py-2", children: [
487
+ /* @__PURE__ */ jsx(
488
+ StatChip,
489
+ {
490
+ label: t("messages.threadCountN", {
491
+ defaultValue: `${threads.length} threads`
492
+ })
493
+ }
494
+ ),
495
+ /* @__PURE__ */ jsx(
496
+ StatChip,
497
+ {
498
+ label: t("messages.unreadCountN", {
499
+ defaultValue: `${unreadTotal} unread`
500
+ }),
501
+ accent: unreadTotal > 0
502
+ }
503
+ )
504
+ ] }),
505
+ /* @__PURE__ */ jsx("div", { children: threads.map((thread) => /* @__PURE__ */ jsx(
506
+ MessagesThreadButton,
507
+ {
508
+ thread,
509
+ selected: thread.id === selectedThreadId,
510
+ onOpen: openThread
511
+ },
512
+ thread.id
513
+ )) })
514
+ ] })
515
+ }
516
+ ),
517
+ /* @__PURE__ */ jsx(
518
+ "section",
519
+ {
520
+ className: `min-h-0 flex-1 flex-col ${showComposer ? "flex" : "hidden"}`,
521
+ "data-testid": "messages-composer-panel",
522
+ children: showComposer ? /* @__PURE__ */ jsxs(Fragment, { children: [
523
+ /* @__PURE__ */ jsxs("div", { className: "shrink-0 px-3 py-2", children: [
524
+ /* @__PURE__ */ jsx(
525
+ "label",
526
+ {
527
+ htmlFor: "messages-compose-address",
528
+ className: "text-xs text-muted",
529
+ children: t("messages.to", { defaultValue: "To" })
530
+ }
531
+ ),
532
+ /* @__PURE__ */ jsx(
533
+ Input,
534
+ {
535
+ ref: addressInput.ref,
536
+ ...addressInput.agentProps,
537
+ id: "messages-compose-address",
538
+ value: composeAddress,
539
+ onChange: (event) => setComposeAddress(event.target.value),
540
+ placeholder: "+1 555 123 4567",
541
+ inputMode: "tel",
542
+ className: "mt-1",
543
+ "data-testid": "messages-compose-address"
544
+ }
545
+ )
546
+ ] }),
547
+ /* @__PURE__ */ jsx("div", { className: "chat-native-scrollbar flex-1 overflow-y-auto px-4 py-4", children: selectedThread ? /* @__PURE__ */ jsx("div", { className: "mx-auto flex max-w-2xl flex-col gap-2", children: selectedThread.messages.map((message) => {
548
+ const sent = message.type === SENT_SMS_TYPE;
549
+ return /* @__PURE__ */ jsx(
550
+ "div",
551
+ {
552
+ className: `flex ${sent ? "justify-end" : "justify-start"}`,
553
+ children: /* @__PURE__ */ jsxs(
554
+ "div",
555
+ {
556
+ className: "max-w-[78%] rounded-2xl px-3 py-2",
557
+ style: sent ? {
558
+ background: "var(--accent)",
559
+ color: "var(--accent-foreground)"
560
+ } : {
561
+ background: "var(--surface)",
562
+ color: "var(--text)"
563
+ },
564
+ children: [
565
+ /* @__PURE__ */ jsx("div", { className: "whitespace-pre-wrap break-words text-sm", children: message.body }),
566
+ /* @__PURE__ */ jsx(
567
+ "div",
568
+ {
569
+ className: "mt-1 text-right text-2xs",
570
+ style: {
571
+ opacity: 0.7,
572
+ color: sent ? "var(--accent-foreground)" : "var(--muted)"
573
+ },
574
+ children: formatTime(message.date)
575
+ }
576
+ )
577
+ ]
578
+ }
579
+ )
580
+ },
581
+ message.id
582
+ );
583
+ }) }) : /* @__PURE__ */ jsx("div", { className: "flex h-full items-center justify-center text-center", children: /* @__PURE__ */ jsxs("div", { className: "max-w-sm", children: [
584
+ /* @__PURE__ */ jsx(MessageSquareText, { className: "mx-auto h-12 w-12 text-muted" }),
585
+ /* @__PURE__ */ jsx("div", { className: "mt-3 text-sm font-medium text-txt", children: t("messages.composeTitle", {
586
+ defaultValue: "Start a text message"
587
+ }) }),
588
+ /* @__PURE__ */ jsx("p", { className: "sr-only mt-1 text-xs text-muted", children: t("messages.composeBody", {
589
+ defaultValue: "Enter a phone number and message body. Android handles carrier delivery through the SMS bridge."
590
+ }) })
591
+ ] }) }) }),
592
+ /* @__PURE__ */ jsx("div", { className: "shrink-0 bg-bg/95 px-3 py-2", children: /* @__PURE__ */ jsxs("div", { className: "mx-auto flex max-w-2xl items-end gap-2", children: [
593
+ /* @__PURE__ */ jsx(
594
+ Textarea,
595
+ {
596
+ ref: bodyInput.ref,
597
+ ...bodyInput.agentProps,
598
+ value: composeBody,
599
+ onChange: (event) => setComposeBody(event.target.value),
600
+ placeholder: t("messages.placeholder", {
601
+ defaultValue: "Message"
602
+ }),
603
+ className: "min-h-[44px] resize-none",
604
+ rows: 2,
605
+ "data-testid": "messages-compose-body"
606
+ }
607
+ ),
608
+ /* @__PURE__ */ jsx(
609
+ Button,
610
+ {
611
+ ref: sendButton.ref,
612
+ ...sendButton.agentProps,
613
+ size: "icon",
614
+ className: "h-11 w-11 shrink-0",
615
+ onClick: () => void send(),
616
+ disabled: !canSend,
617
+ "aria-label": t("messages.send", { defaultValue: "Send" }),
618
+ "data-testid": "messages-send",
619
+ children: /* @__PURE__ */ jsx(Send, { className: "h-4 w-4" })
620
+ }
621
+ )
622
+ ] }) })
623
+ ] }) : /* @__PURE__ */ jsx("div", { className: "flex flex-1 items-center justify-center px-6 text-center", children: /* @__PURE__ */ jsxs("div", { className: "max-w-sm", children: [
624
+ /* @__PURE__ */ jsx(MessageSquareText, { className: "mx-auto h-12 w-12 text-muted" }),
625
+ /* @__PURE__ */ jsx("div", { className: "mt-3 text-sm font-medium text-txt", children: t("messages.selectTitle", {
626
+ defaultValue: "Select a conversation"
627
+ }) }),
628
+ /* @__PURE__ */ jsx("p", { className: "sr-only mt-1 text-xs text-muted", children: t("messages.selectBody", {
629
+ defaultValue: "Review existing SMS threads or start a new text message."
630
+ }) })
631
+ ] }) })
632
+ }
633
+ )
634
+ ] })
635
+ ]
636
+ }
637
+ );
638
+ }
639
+ export {
640
+ MessagesAppView
641
+ };
642
+ //# sourceMappingURL=MessagesAppView.js.map