-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathindex.html
More file actions
1023 lines (960 loc) · 72.5 KB
/
Copy pathindex.html
File metadata and controls
1023 lines (960 loc) · 72.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Beeper — Chat App Support</title>
<meta name="description" content="See which features work with each chat app and connection type in Beeper.">
<meta name="robots" content="noindex">
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>💬</text></svg>">
<style>
:root {
color-scheme: light;
--bg-primary: #ffffff;
--bg-secondary: #f7f7f5;
--text-primary: #1a1a1a;
--text-secondary: #6b6b6b;
--text-tertiary: #999999;
--border: rgba(0,0,0,0.1);
--border-hover: rgba(0,0,0,0.2);
--active-border: #534AB7;
--active-bg: #EEEDFE;
--active-text: #3C3489;
--radius-md: 8px;
--radius-lg: 12px;
--font: -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;
}
* { box-sizing: border-box; margin: 0; padding: 0; }
body { font-family: var(--font); color: var(--text-primary); background: transparent; padding: 1rem 0; margin: 0; }
.section-label { font-size: 12px; color: var(--text-secondary); font-weight: 500; margin: 0 0 8px; text-transform: uppercase; letter-spacing: 0.5px; }
.network-grid { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 24px; }
.net-btn, .mode-btn { font-family: var(--font); cursor: pointer; transition: all 0.15s; }
.net-btn { font-size: 13px; padding: 6px 14px; border-radius: var(--radius-md); border: 0.5px solid var(--border); background: var(--bg-primary); color: var(--text-primary); }
.net-btn:hover, .mode-btn:hover { border-color: var(--border-hover); background: var(--bg-secondary); }
.net-btn.active { border-color: var(--active-border); background: var(--active-bg); color: var(--active-text); }
.connection-intro { font-size: 13px; color: var(--text-secondary); line-height: 1.45; margin: -12px 0 20px; }
.connection-intro strong { color: var(--text-primary); font-weight: 500; }
.audit-date { white-space: nowrap; }
.label-card { background: var(--bg-primary); border: 0.5px solid var(--border); border-radius: var(--radius-lg); padding: 18px 20px; margin-bottom: 8px; }
.label-header { display: flex; align-items: flex-start; flex-wrap: wrap; gap: 10px 16px; margin-bottom: 14px; padding-bottom: 12px; border-bottom: 2px solid var(--text-primary); }
.label-main { min-width: 0; }
.label-title { display: flex; align-items: center; flex-wrap: wrap; gap: 7px; }
.label-title h2 { font-size: 18px; font-weight: 500; }
.availability-badge { font-size: 11px; padding: 3px 8px; border-radius: var(--radius-md); background: #FAEEDA; color: #854F0B; font-weight: 500; white-space: nowrap; }
.platform-badge { font-size: 11px; padding: 3px 8px; border-radius: var(--radius-md); background: #E6F1FB; color: #185FA5; font-weight: 500; white-space: nowrap; }
.connection-badge { display: inline-block; margin-top: 7px; font-size: 11px; padding: 4px 9px; border-radius: var(--radius-md); background: #EEEDFE; color: #3C3489; font-weight: 500; white-space: nowrap; }
.mode-switch { display: flex; align-items: center; gap: 4px; margin-top: 7px; }
.mode-btn { font-size: 11px; padding: 4px 9px; border-radius: var(--radius-md); border: 0.5px solid var(--border); background: var(--bg-primary); color: var(--text-secondary); }
.mode-btn.active { border-color: var(--active-border); background: var(--active-bg); color: var(--active-text); }
.support-summary { margin-left: auto; max-width: 330px; font-size: 12px; line-height: 1.45; color: var(--text-secondary); text-align: right; }
.connection-note { margin: -2px 0 14px; padding: 9px 11px; border-radius: var(--radius-md); background: var(--bg-secondary); color: var(--text-secondary); font-size: 12px; line-height: 1.4; }
.feature-row, .feature-row-stacked { display: flex; justify-content: space-between; gap: 10px; border-bottom: 0.5px solid var(--border); font-size: 14px; }
.feature-row { align-items: center; padding: 5px 0; }
.feature-row-stacked { align-items: flex-start; padding: 6px 0; }
.feature-name { flex: 1; min-width: 0; }
.feature-detail { font-size: 11px; color: var(--text-secondary); margin-top: 2px; line-height: 1.35; }
.mode-diff-tag { display: inline-block; font-size: 10px; padding: 2px 6px; border-radius: var(--radius-md); background: #E6F1FB; color: #185FA5; margin-left: 6px; vertical-align: 1px; white-space: nowrap; }
.badge { flex: 0 0 auto; font-size: 11px; padding: 2px 9px; border-radius: var(--radius-md); font-weight: 500; white-space: nowrap; }
.badge-yes { background: #EAF3DE; color: #3B6D11; }
.badge-no { background: #FAC775; color: #633806; }
.badge-partial { background: #FAEEDA; color: #854F0B; }
.badge-oneway { background: #E6F1FB; color: #185FA5; }
.badge-na { background: var(--bg-secondary); color: var(--text-tertiary); }
.divider { height: 1px; background: var(--border); margin: 17px 0; }
.data-error { padding: 16px; border-radius: var(--radius-md); background: #FCEBEB; color: #A32D2D; font-size: 14px; }
.visually-hidden { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; }
@media (max-width: 480px) {
body { padding-top: 12px; padding-bottom: 12px; }
.network-grid { flex-wrap: nowrap; overflow-x: auto; padding-bottom: 4px; -webkit-overflow-scrolling: touch; }
.net-btn { flex: 0 0 auto; }
.connection-intro { font-size: 12px; line-height: 1.35; margin-bottom: 14px; }
.label-card { padding: 14px; }
.label-header { margin-bottom: 11px; padding-bottom: 10px; }
.support-summary { width: 100%; max-width: none; margin-left: 0; text-align: left; }
.feature-row { padding: 4px 0; }
.feature-row-stacked { padding: 5px 0; }
.divider { margin: 14px 0; }
}
@media (max-width: 375px) {
body { padding-top: 8px; padding-bottom: 8px; }
.network-grid { margin-bottom: 16px; }
.connection-intro { font-size: 11px; line-height: 1.3; margin-top: -9px; margin-bottom: 10px; }
.label-card { padding: 10px; }
.label-header { gap: 7px 10px; margin-bottom: 8px; padding-bottom: 8px; }
.label-title h2 { font-size: 17px; }
.support-summary { font-size: 11px; line-height: 1.35; }
.section-label { font-size: 11px; margin-bottom: 4px; }
.feature-row, .feature-row-stacked { font-size: 13px; gap: 7px; }
.feature-row { padding: 1px 0; }
.feature-row-stacked { padding: 2px 0; }
.feature-detail { font-size: 10px; line-height: 1.2; }
.mode-diff-tag { font-size: 9px; padding: 1px 4px; margin-left: 4px; }
.divider { margin: 8px 0; }
.badge { font-size: 10px; padding: 2px 7px; }
}
</style>
</head>
<body>
<main>
<p class="section-label">Choose a chat app</p>
<div class="network-grid" id="netGrid"></div>
<p class="connection-intro">Choose a chat app, then choose how Beeper connects to it. <strong>Beeper Cloud</strong> keeps the connection running for you. <strong>On-Device</strong> connects through your device. <strong>Limited</strong> means the feature works with the restrictions described below. <strong>Not offered</strong> means the chat app itself does not have that feature. <strong>Not supported</strong> means the chat app has it, but this Beeper connection does not. <span class="audit-date">Information last checked August 26, 2026.</span></p>
<div id="labelOutput"></div>
<p id="statusOutput" class="visually-hidden" aria-live="polite"></p>
</main>
<script>
const modeLabels = {
cloud: 'Beeper Cloud',
onDevice: 'On-Device',
};
const featureGroups = [
{
label: 'Messaging basics',
features: [
{ key: 'reactions', label: 'Emoji reactions' },
{ key: 'receipts', label: 'Read receipts' },
{ key: 'push', label: 'Push notifications' },
{ key: 'replies', label: 'Replies' },
{ key: 'files', label: 'Send files' },
{ key: 'voice', label: 'Voice messages' },
{ key: 'history', label: 'Import historical messages' },
{ key: 'redactions', label: 'Delete messages' },
],
},
{
label: 'More chat features',
features: [
{ key: 'createChat', label: 'Start a new chat' },
{ key: 'edits', label: 'Edit messages' },
{ key: 'typing', label: 'Typing indicators' },
{ key: 'captions', label: 'Media captions' },
{ key: 'formatting', label: 'Text formatting' },
{ key: 'disappearing', label: 'Disappearing messages' },
],
},
{
label: 'Other features',
features: [
{ key: 'timeBasedMedia', label: 'Time-based media' },
{ key: 'online', label: 'Online status' },
{ key: 'polls', label: 'Polls' },
{ key: 'threads', label: 'Threads' },
],
},
];
function cell(status, detail) {
return detail ? { status: status, detail: detail } : { status: status };
}
const networks = [
'WhatsApp',
'FB Messenger',
'Instagram',
'Telegram',
'Signal',
'iMessage',
'Google Messages',
'Google Voice',
'Google Chat',
'Twitter/X',
'Slack',
'Discord',
'LinkedIn',
'LINE',
'Tumblr',
];
function nk(networkLabel) {
return networkLabel.replace(/[\s/]/g, '');
}
const networkLabels = Object.fromEntries(networks.map(function(networkLabel) {
return [nk(networkLabel), networkLabel];
}));
const networkData = {
WhatsApp: {
modes: ['cloud', 'onDevice'],
availability: 'public',
support: {
cloud: {
reactions: cell('yes', 'Reactions work both ways. Each person can use one reaction per message; adding another replaces it.'),
receipts: cell('yes', 'Read receipts work both ways. Reading a message in WhatsApp also updates Beeper.'),
push: cell('yes', 'Notifications can arrive while Beeper is closed.'),
replies: cell('yes', 'Replies work both ways in direct messages and regular groups. Community Announcement Groups use threads instead.'),
files: cell('yes', 'Photos, videos, audio, GIFs, stickers, and documents work. View-once media appears as a note telling you to open it in WhatsApp.'),
voice: cell('yes', 'Voice messages work both ways. View-once voice messages must be opened in WhatsApp.'),
history: cell('yes', 'When WhatsApp provides it, the import covers about one year across eligible chats and can sometimes reach roughly three years. Beeper starts with up to 50 messages per chat, then imports older transferred history automatically in batches of up to 100. If more must be fetched from your phone, scroll up to request about 50 at a time. Status updates, system chats, and groups you have left are not imported.'),
redactions: cell('yes', 'You can delete a message for everyone for up to two days. Beeper cannot start a “delete for me,” but one made in WhatsApp syncs to Beeper.'),
createChat: cell('yes', 'You can start a DM by phone number or contact and create regular groups. Beeper cannot create WhatsApp Communities or Channels.'),
edits: cell('yes', 'You can edit a message for up to 15 minutes. Imported history shows only the latest version.'),
typing: cell('oneway-out', 'Beeper sends your text typing indicator to WhatsApp, but does not show when someone is typing in WhatsApp. Voice-recording and uploading indicators do not sync.'),
captions: cell('yes', 'Captions work on photos, videos, GIFs, and documents. Audio, voice messages, and stickers do not carry captions.'),
formatting: cell('yes', 'Bold, italics, strikethrough, code, lists, quotes, and mentions work. Linked text and headings may look simpler in Beeper.'),
disappearing: cell('yes', '24-hour, 7-day, and 90-day timers work both ways. View-once media must be opened in WhatsApp, and keeping a disappearing message appears only as a notice.'),
timeBasedMedia: cell('no', 'Beeper does not support View Once or Allow Replay photos and videos. You may see a notice to open them in the original app, or receive them as a regular file instead.'),
online: cell('no', 'Beeper does not show a contact’s online or last-seen status.'),
polls: cell('yes', 'You can create polls and vote with up to 12 options of up to 100 characters each. Ending polls, hiding votes, and duplicate options are not supported. A poll sent in a Community Announcement thread may lose its thread placement.'),
threads: cell('partial', 'Comments in Community Announcement Groups work both ways as threads. Regular WhatsApp chats use replies instead.'),
},
onDevice: {
reactions: cell('yes', 'Reactions work both ways. Each person can use one reaction per message; adding another replaces it.'),
receipts: cell('yes', 'Read receipts work both ways. Reading a message in WhatsApp also updates Beeper.'),
push: cell('yes', 'Notifications can arrive while Beeper is closed.'),
replies: cell('yes', 'Replies work both ways in direct messages and regular groups. Community Announcement Groups use threads instead.'),
files: cell('yes', 'Photos, videos, audio, GIFs, stickers, and documents work. View-once media appears as a note telling you to open it in WhatsApp.'),
voice: cell('yes', 'Voice messages work both ways. View-once voice messages must be opened in WhatsApp.'),
history: cell('yes', 'When WhatsApp provides it, the import covers about one year across eligible chats and can sometimes reach roughly three years. Beeper starts with up to 50 messages per chat; scroll up to load 50 more at a time. Status updates, system chats, and groups you have left are not imported. WhatsApp Channels are not available On-Device; use Beeper Cloud for Channels.'),
redactions: cell('yes', 'You can delete a message for everyone for up to two days. Beeper cannot start a “delete for me,” but one made in WhatsApp syncs to Beeper.'),
createChat: cell('yes', 'You can start a DM by phone number or contact and create regular groups. Beeper cannot create WhatsApp Communities or Channels.'),
edits: cell('yes', 'You can edit a message for up to 15 minutes. Imported history shows only the latest version.'),
typing: cell('yes', 'Text typing indicators work both ways while the chat is open. Voice-recording and uploading indicators do not sync.'),
captions: cell('yes', 'Captions work on photos, videos, GIFs, and documents. Audio, voice messages, and stickers do not carry captions.'),
formatting: cell('yes', 'Bold, italics, strikethrough, code, lists, quotes, and mentions work. Linked text and headings may look simpler in Beeper.'),
disappearing: cell('yes', '24-hour, 7-day, and 90-day timers work both ways. View-once media must be opened in WhatsApp, and keeping a disappearing message appears only as a notice.'),
timeBasedMedia: cell('no', 'Beeper does not support View Once or Allow Replay photos and videos. You may see a notice to open them in the original app, or receive them as a regular file instead.'),
online: cell('no', 'Beeper does not show a contact’s online or last-seen status.'),
polls: cell('yes', 'You can create polls and vote with up to 12 options of up to 100 characters each. Ending polls, hiding votes, and duplicate options are not supported. A poll sent in a Community Announcement thread may lose its thread placement.'),
threads: cell('partial', 'Comments in Community Announcement Groups work both ways as threads. Regular WhatsApp chats use replies instead.'),
},
},
},
FBMessenger: {
modes: ['cloud', 'onDevice'],
availability: 'public',
support: {
cloud: {
reactions: cell('yes', 'Each person can add one reaction to a message.'),
receipts: cell('partial', 'Read receipts work in regular and encrypted DMs and groups. Sending a read receipt from Beeper in a Community group has not been confirmed.'),
push: cell('yes', 'Notifications can arrive while Beeper is closed.'),
replies: cell('yes'),
files: cell('yes', 'Photos can be up to 8 MB. Videos, audio, and other files can be up to 25 MB. Encrypted chats allow up to 100 MB, but WebM and Ogg videos do not work.'),
voice: cell('yes'),
history: cell('partial', 'Beeper adds regular Messenger DMs and groups with up to 50 messages in each. Scroll up to load 50 more at a time, as far back as Messenger makes available. Earlier encrypted history is unavailable, and older Community chat or thread history may be incomplete.'),
redactions: cell('yes'),
createChat: cell('yes', 'You can start direct messages and group chats from Beeper.'),
edits: cell('yes', 'You can edit a message up to five times within 15 minutes.'),
typing: cell('partial', 'Typing indicators work both ways in regular DMs and groups. In encrypted chats, people can see when you type, but Beeper Cloud cannot show when they type. Community threads do not support typing indicators. Voice-recording and uploading indicators do not sync.'),
captions: cell('partial', 'Captions work on photos, videos, and files in chats without end-to-end encryption. They do not carry over on audio, voice messages, GIFs, stickers, or encrypted chats.'),
formatting: cell('yes', 'Bold, italics, strikethrough, quotes, and code work both ways.'),
disappearing: cell('partial', 'Message timers work in end-to-end encrypted chats. Regular chats do not offer a timer, and view-once media must be opened in Messenger.'),
timeBasedMedia: cell('no', 'Beeper does not support View Once or Allow Replay photos and videos. You may see a notice to open them in the original app, or receive them as a regular file instead.'),
online: cell('no'),
polls: cell('no'),
threads: cell('partial', 'Only Messenger Community group threads are supported. New replies work, but older thread history may be incomplete.'),
},
onDevice: {
reactions: cell('yes', 'Each person can add one reaction to a message.'),
receipts: cell('partial', 'Read receipts work in regular and encrypted DMs and groups. Sending a read receipt from Beeper in a Community group has not been confirmed.'),
push: cell('no', 'Background notifications do not currently work. Open Beeper to check for new messages.'),
replies: cell('yes'),
files: cell('yes', 'Photos can be up to 8 MB. Videos, audio, and other files can be up to 25 MB. Encrypted chats allow up to 100 MB, but WebM and Ogg videos do not work.'),
voice: cell('yes'),
history: cell('partial', 'Beeper adds regular Messenger DMs and groups with up to 20 messages in each. Scroll up to load 20 more at a time, as far back as Messenger makes available. Earlier encrypted history is unavailable, and older Community chat or thread history may be incomplete.'),
redactions: cell('yes'),
createChat: cell('yes', 'You can start direct messages and group chats from Beeper.'),
edits: cell('yes', 'You can edit a message up to five times within 15 minutes.'),
typing: cell('partial', 'Text typing indicators work both ways in regular and encrypted chats while the chat is open. Community threads do not support them. Voice-recording and uploading indicators do not sync.'),
captions: cell('partial', 'Captions work on photos, videos, and files in chats without end-to-end encryption. They do not carry over on audio, voice messages, GIFs, stickers, or encrypted chats.'),
formatting: cell('yes', 'Bold, italics, strikethrough, quotes, and code work both ways.'),
disappearing: cell('partial', 'Message timers work in end-to-end encrypted chats. Regular chats do not offer a timer, and view-once media must be opened in Messenger.'),
timeBasedMedia: cell('no', 'Beeper does not support View Once or Allow Replay photos and videos. You may see a notice to open them in the original app, or receive them as a regular file instead.'),
online: cell('no'),
polls: cell('no'),
threads: cell('partial', 'Only Messenger Community group threads are supported. New replies work, but older thread history may be incomplete.'),
},
},
},
Instagram: {
modes: ['cloud', 'onDevice'],
availability: 'public',
support: {
cloud: {
reactions: cell('yes', 'Each person can add one reaction to a message.'),
receipts: cell('yes', 'Read receipts work both ways in direct messages and group chats.'),
push: cell('yes', 'Notifications can arrive while Beeper is closed.'),
replies: cell('yes'),
files: cell('partial', 'Photos and GIFs can be up to 8 MB; videos and audio can be up to 25 MB. Shared posts, reels, and stories received from Instagram show their preview, Instagram text, and link. Beeper cannot send or share an Instagram Reel. View-once photos and videos must be opened in Instagram.'),
voice: cell('yes'),
history: cell('partial', 'Only chats on Instagram\'s first inbox page—usually around 20—are added automatically. Each starts with up to 20 messages; scroll up to load 20 more at a time, and keep loading as far back as Instagram makes history available in those chats. Older chats are not added automatically, and reels, stories, or shared posts may be incomplete.'),
redactions: cell('yes'),
createChat: cell('yes', 'You can start direct messages and group chats, and accept message requests in Beeper.'),
edits: cell('yes', 'You can edit a message up to five times within 15 minutes.'),
typing: cell('yes', 'Text typing indicators work both ways in direct messages and groups.'),
captions: cell('yes', 'Instagram sends caption text as a separate message instead of attaching it to the photo, video, GIF, or audio. Beeper does the same, so the text still arrives. Shared posts and reels you receive can include their Instagram text and link.'),
formatting: cell('yes', 'Bold, italics, strikethrough, quotes, and code work both ways.'),
disappearing: cell('no', 'Disappearing-message timers do not sync. View-once photos and videos must be opened in Instagram; Beeper shows a notice instead.'),
timeBasedMedia: cell('no', 'Beeper does not support View Once or Allow Replay photos and videos. You may see a notice to open them in the original app, or receive them as a regular file instead.'),
online: cell('no'),
polls: cell('no'),
threads: cell('na'),
},
onDevice: {
reactions: cell('yes', 'Each person can add one reaction to a message.'),
receipts: cell('yes', 'Read receipts work both ways in direct messages and group chats.'),
push: cell('yes', 'Notifications can arrive while Beeper is closed.'),
replies: cell('yes'),
files: cell('partial', 'Photos and GIFs can be up to 8 MB; videos and audio can be up to 25 MB. Other file types are not supported. Reels, shared posts, and stories may be incomplete. View-once photos and videos appear as a notice and must be opened in Instagram.'),
voice: cell('yes'),
history: cell('partial', 'Only chats on Instagram\'s first inbox page—usually around 20—are added automatically. Each starts with up to 20 messages; scroll up to load 20 more at a time, and keep loading as far back as Instagram makes history available in those chats. Older chats are not added automatically, and reels, stories, or shared posts may be incomplete.'),
redactions: cell('yes'),
createChat: cell('yes', 'You can start direct messages and group chats, and accept message requests in Beeper.'),
edits: cell('yes', 'You can edit a message up to five times within 15 minutes.'),
typing: cell('yes', 'Text typing indicators work both ways in direct messages and groups.'),
captions: cell('no', 'Captions on photos, videos, audio, GIFs, and other media do not appear in Beeper.'),
formatting: cell('yes', 'Bold, italics, strikethrough, quotes, and code work both ways.'),
disappearing: cell('no', 'Disappearing-message timers do not sync. View-once photos and videos must be opened in Instagram; Beeper shows a notice instead.'),
timeBasedMedia: cell('no', 'Beeper does not support View Once or Allow Replay photos and videos. You may see a notice to open them in the original app, or receive them as a regular file instead.'),
online: cell('no'),
polls: cell('no'),
threads: cell('na'),
},
},
},
Telegram: {
modes: ['cloud', 'onDevice'],
availability: 'public',
support: {
cloud: {
reactions: cell('yes'),
receipts: cell('partial', 'Read receipts are complete in direct messages. In groups and channels, Beeper syncs your own read position but does not show who else has read a message.'),
push: cell('yes', 'Notifications can arrive while Beeper is closed.'),
replies: cell('partial', 'Replies in the same chat work, but replies that point to a message in another chat lose that link.'),
files: cell('yes'),
voice: cell('yes'),
history: cell('yes', 'Every supported Telegram chat is added; groups with more than 10,000 members are excluded. Beeper starts with 50 recent messages in each. Scroll up to load older messages 50 at a time. Beeper does not limit how far you can keep loading.'),
redactions: cell('yes'),
createChat: cell('yes', 'You can start direct messages and regular groups, but not channels or supergroups.'),
edits: cell('yes'),
typing: cell('yes', 'Text typing indicators work both ways in DMs, groups, channels, and forum topics. Telegram\'s separate recording and uploading indicators are not shown.'),
captions: cell('yes'),
formatting: cell('partial', 'Most formatting works. Some lists, headings, and custom emoji may look simpler.'),
disappearing: cell('yes', 'Disappearing-message timers work. Telegram forum topics share their parent chat\'s timer. View-once photos and videos can only be opened in Telegram.'),
timeBasedMedia: cell('no', 'Beeper does not support View Once or Allow Replay photos and videos. You may see a notice to open them in the original app, or receive them as a regular file instead.'),
online: cell('no'),
polls: cell('partial', 'Polls appear as plain text with the question and choices. You cannot create or vote in them from Beeper.'),
threads: cell('partial', 'Forum topics appear as separate chats. You cannot create a topic or use Beeper\'s own thread view.'),
},
onDevice: {
reactions: cell('yes'),
receipts: cell('partial', 'Read receipts are complete in direct messages. In groups and channels, Beeper syncs your own read position but does not show who else has read a message.'),
push: cell('yes', 'Notifications are sent directly from Telegram to your phone, even when Beeper is closed.'),
replies: cell('yes', 'Replies work, including replies that link to a message in another chat.'),
files: cell('yes'),
voice: cell('yes'),
history: cell('yes', 'Every supported Telegram chat is added; groups with more than 10,000 members are excluded. Beeper starts with 50 recent messages in each. Scroll up to load older messages 50 at a time. Beeper does not limit how far you can keep loading.'),
redactions: cell('yes'),
createChat: cell('yes', 'You can start direct messages and regular groups, but not channels or supergroups.'),
edits: cell('yes'),
typing: cell('yes', 'Text typing indicators work both ways in DMs, groups, channels, and forum topics. Telegram\'s separate recording and uploading indicators are not shown.'),
captions: cell('yes'),
formatting: cell('partial', 'Most formatting works. Some lists, headings, and custom emoji may look simpler.'),
disappearing: cell('yes', 'Disappearing-message timers work. Telegram forum topics share their parent chat\'s timer. View-once photos and videos can only be opened in Telegram.'),
timeBasedMedia: cell('no', 'Beeper does not support View Once or Allow Replay photos and videos. You may see a notice to open them in the original app, or receive them as a regular file instead.'),
online: cell('no'),
polls: cell('partial', 'Polls appear as plain text with the question and choices. You cannot create or vote in them from Beeper.'),
threads: cell('partial', 'Forum topics appear as separate chats. You cannot create a topic or use Beeper\'s own thread view.'),
},
},
},
Signal: {
modes: ['cloud', 'onDevice'],
availability: 'public',
support: {
cloud: {
reactions: cell('yes'),
receipts: cell('yes', 'Read receipts work both ways when they are enabled in Signal. Message requests do not send them until accepted.'),
push: cell('yes', 'Notifications can arrive while Beeper is closed.'),
replies: cell('yes'),
files: cell('yes', 'Supported photos, videos, audio, GIFs, and documents up to 100 MB work. Photos are limited to 4,096 × 4,096 pixels.'),
voice: cell('partial', 'Most voice messages work both ways. Beeper automatically converts some audio formats when needed.'),
history: cell('yes', 'If you choose “Import all history” while linking Signal, Beeper adds every supported chat that contains messages and imports all supported history Signal provides in the transfer. There is no fixed time cutoff. It starts with up to 50 messages in each chat, then imports older transferred history automatically, up to 100 at a time.'),
redactions: cell('yes', 'You can delete a message for everyone for up to 24 hours. Note to Self does not have the 24-hour limit.'),
createChat: cell('yes'),
edits: cell('yes', 'You can edit each message up to 10 times within 24 hours. Note to Self does not have the 24-hour limit.'),
typing: cell('yes', 'Typing indicators work both ways in direct messages and groups when they are enabled in Signal. Message requests do not send them until accepted.'),
captions: cell('yes', 'Captions work on photos, videos, files, and GIFs, but not on voice messages or stickers.'),
formatting: cell('partial', 'Signal’s bold, italics, strikethrough, spoilers, monospace, and mentions work both ways. Beeper-only formatting such as lists, linked text, headings, block quotes, underlining, and code blocks becomes simpler readable text in Signal.'),
disappearing: cell('yes', 'Timers that start after a message is read work. View-once media disappears five minutes after it is read.'),
timeBasedMedia: cell('no', 'Beeper does not support View Once or Allow Replay photos and videos. You may see a notice to open them in the original app, or receive them as a regular file instead.'),
online: cell('na'),
polls: cell('partial', 'You can create polls and vote. Polls ended in Signal also show as ended in Beeper, but you cannot end one from Beeper. Polls that hide who voted are not supported.'),
threads: cell('na'),
},
onDevice: {
reactions: cell('yes'),
receipts: cell('yes', 'Read receipts work both ways when they are enabled in Signal. Message requests do not send them until accepted.'),
push: cell('yes', 'Notifications can arrive while Beeper is closed.'),
replies: cell('yes'),
files: cell('yes', 'Supported photos, videos, audio, GIFs, and documents up to 100 MB work. Photos are limited to 4,096 × 4,096 pixels.'),
voice: cell('partial', 'Most voice messages work. Some may appear as regular audio instead.'),
history: cell('yes', 'If you choose “Import all history” while linking Signal, Beeper adds every supported chat that contains messages and imports all supported history Signal provides in the transfer. There is no fixed time cutoff. It starts with up to 50 messages in each chat; scroll up to load 50 more transferred messages at a time.'),
redactions: cell('yes', 'You can delete a message for everyone for up to 24 hours. Note to Self does not have the 24-hour limit.'),
createChat: cell('yes'),
edits: cell('yes', 'You can edit each message up to 10 times within 24 hours. Note to Self does not have the 24-hour limit.'),
typing: cell('yes', 'Typing indicators work both ways in direct messages and groups when they are enabled in Signal. Message requests do not send them until accepted.'),
captions: cell('yes', 'Captions work on photos, videos, files, and GIFs, but not on voice messages or stickers.'),
formatting: cell('partial', 'Signal’s bold, italics, strikethrough, spoilers, monospace, and mentions work both ways. Beeper-only formatting such as lists, linked text, headings, block quotes, underlining, and code blocks becomes simpler readable text in Signal.'),
disappearing: cell('yes', 'Timers that start after a message is read work. View-once messages are not removed automatically.'),
timeBasedMedia: cell('no', 'Beeper does not support View Once or Allow Replay photos and videos. You may see a notice to open them in the original app, or receive them as a regular file instead.'),
online: cell('na'),
polls: cell('partial', 'You can create polls and vote. Polls ended in Signal also show as ended in Beeper, but you cannot end one from Beeper. Polls that hide who voted are not supported.'),
threads: cell('na'),
},
},
},
iMessage: {
modes: ['onDevice'],
availability: 'public',
platformLabel: 'Beeper Desktop for Mac only',
connectionNote: 'Available in Beeper Desktop on a Mac running macOS Big Sur or later. Beeper uses the Messages app on that Mac, which must stay open. This connection stays on that Mac and does not sync to your other Beeper devices.',
support: {
onDevice: {
reactions: cell('partial', 'Apple’s six Tapbacks work both ways. On macOS Sequoia or later, any emoji reaction also works; older versions are limited to the six Tapbacks. Some attachments and replies cannot be reacted to, and sticker reactions can appear but cannot be added or removed from Beeper.'),
receipts: cell('partial', 'Reading in Beeper marks the conversation read in Messages, and reading in Messages updates Beeper. One-to-one iMessage chats show read receipts when they are enabled in Messages. Beeper does not show who has read a group message. Marking chats read may be less reliable on macOS Monterey and earlier.'),
push: cell('no', 'Beeper can show notifications while Beeper Desktop and Messages are running on this Mac, including when the Beeper window is closed. It does not receive iMessage push notifications, so it cannot notify after Beeper is quit or while the Mac is asleep. Messages may still show its own macOS notifications.'),
replies: cell('partial', 'Replies work both ways. On macOS Monterey or later, you can reply to ordinary text and most media. On Big Sur, only ordinary text messages can be replied to; some iMessage app messages and replies cannot be selected.'),
files: cell('yes', 'Photos, videos, GIFs, audio, and other files work both ways, up to 100 MB per outgoing attachment. Incoming stickers appear as stickers; a sticker sent from Beeper is sent as a regular image.'),
voice: cell('oneway-in', 'Voice messages received from iMessage play as voice messages in Beeper. You cannot record or send an iMessage voice message from Beeper, but you can send an audio file.'),
history: cell('yes', 'Beeper reads the iMessage, SMS, and RCS history stored in Messages on this Mac. It starts with 25 recent chats and automatically adds recent chats until it reaches about one year of activity or 1,000 chats. Scroll down the chat list to load older chats 25 at a time. Each chat starts with about 20 recent messages; scroll up to load 20 more at a time. Beeper does not set a time or message cutoff. Recently deleted items, pending scheduled messages, and a few unsupported Messages system items are not included.'),
redactions: cell('partial', 'Incoming unsends sync. On macOS Ventura or later, you can undo-send your own iMessages during Apple’s two-minute window. Earlier macOS versions cannot unsend from Beeper. This does not delete a message only from your own history.'),
createChat: cell('yes', 'You can start a direct message or group conversation by phone number or email. You must send the first message, and Beeper cannot set a group name while creating it.'),
edits: cell('partial', 'Incoming edits and edit history appear. On macOS Ventura or later, you can edit your own text iMessages within Apple’s 15-minute, five-edit limit. Earlier macOS versions, SMS messages, and attachment-only messages cannot be edited.'),
typing: cell('oneway-out', 'People can see your text typing indicator in one-to-one iMessage chats. Sending it in group chats requires macOS Tahoe or later. Typing indicators from other people do not appear with Beeper’s normal setup. Voice-recording and uploading indicators do not sync.'),
captions: cell('partial', 'Text sent with a photo or file is preserved both ways, but it appears as a separate message rather than attached to the media.'),
formatting: cell('oneway-in', 'Bold, italics, underlining, strikethrough, links, and @mentions received from iMessage appear in Beeper. Messages sent from Beeper are plain text. iMessage screen and bubble effects appear only as a short label.'),
disappearing: cell('no', 'iMessage does not offer chat-wide disappearing-message timers. Its expiring audio messages are not fully synchronized: Beeper does not show their timer or offer Keep, and an expiration may not appear until the chat is reloaded.'),
timeBasedMedia: cell('no', 'Beeper does not support View Once or Allow Replay photos and videos. You may see a notice to open them in the original app, or receive them as a regular file instead.'),
online: cell('na', 'iMessage does not show when someone is online or last active.'),
polls: cell('partial', 'Polls appear as plain text with the question and numbered choices. You cannot create a poll, vote, see vote totals, or receive updated results from Beeper.'),
threads: cell('na', 'iMessage uses inline replies rather than separate chat threads. Those are covered under Replies.'),
},
},
},
GoogleMessages: {
modes: ['cloud'],
availability: 'public',
support: {
cloud: {
reactions: cell('partial', 'Reactions work best in newer RCS chats and are more limited in regular text and picture messages.'),
receipts: cell('partial', 'Read receipts from other people work only in newer RCS chats. Beeper still keeps your own read position in sync for regular text and picture messages.'),
push: cell('yes', 'Notifications can arrive while Beeper is closed.'),
replies: cell('partial', 'Replies work only in newer RCS chats.'),
files: cell('yes', 'Photos, videos, audio, and documents work. Regular picture messages may be compressed by your phone or carrier.'),
voice: cell('yes', 'Voice messages work. Beeper changes the audio format before sending when needed.'),
history: cell('partial', 'Beeper imports up to 25 recent chats from your Google Messages inbox. It starts with up to 50 messages in each; scroll up to load up to two more batches of around 50 messages.'),
redactions: cell('partial', 'Deleting a message in Google Messages also removes it from Beeper. Deleting it in Beeper removes it from your Google Messages history, but not for other participants.'),
createChat: cell('yes'),
edits: cell('oneway-in', 'Eligible edits made in newer RCS chats appear in Beeper. Beeper cannot send edits back, and regular text or picture messages cannot be edited.'),
typing: cell('partial', 'Typing indicators work both ways in newer RCS chats, but not in regular text or picture messages. The indicator may linger briefly after you stop typing in Beeper.'),
captions: cell('partial', 'Captions work with photos, videos, audio, GIFs, and files in regular picture messages, but not in newer RCS chats.'),
formatting: cell('na'),
disappearing: cell('na'),
timeBasedMedia: cell('no', 'Beeper does not support View Once or Allow Replay photos and videos. You may see a notice to open them in the original app, or receive them as a regular file instead.'),
online: cell('na'),
polls: cell('na'),
threads: cell('na'),
},
},
},
GoogleVoice: {
modes: ['onDevice'],
availability: 'public',
support: {
onDevice: {
reactions: cell('na'),
receipts: cell('na', 'Google Voice does not show whether the other person read a message. Beeper still keeps your own place in the conversation in sync.'),
push: cell('no', 'Notifications cannot wake Beeper in the background. New messages appear live while Beeper is running, with a check about every 15 minutes as backup.'),
replies: cell('na'),
files: cell('partial', 'Photos and GIFs work up to 2 MB. Documents, video, and audio cannot be sent.'),
voice: cell('na'),
history: cell('partial', 'Beeper asks Google Voice for about 20 recent conversations and roughly 50 recent messages from each. Scroll up to request older messages in similar batches; Google Voice may return more than 50 messages at once.'),
redactions: cell('no', 'Google Voice can delete individual messages from your own history, but Beeper cannot. Beeper can only delete the whole conversation.'),
createChat: cell('no'),
edits: cell('na'),
typing: cell('na'),
captions: cell('yes', 'Photos and GIFs can be about 2 MB, with captions up to 160 characters.'),
formatting: cell('na'),
disappearing: cell('na'),
timeBasedMedia: cell('no', 'Beeper does not support View Once or Allow Replay photos and videos. You may see a notice to open them in the original app, or receive them as a regular file instead.'),
online: cell('na'),
polls: cell('na'),
threads: cell('na'),
},
},
},
GoogleChat: {
modes: ['cloud'],
availability: 'public',
support: {
cloud: {
reactions: cell('partial', 'Standard emoji reactions work both ways. Google Chat custom emoji reactions are not supported.'),
receipts: cell('partial', 'Read receipts work both ways in direct messages and group chats of up to 20 people. Google Chat Spaces use view counts instead, which Beeper does not show.'),
push: cell('yes', 'Notifications can arrive while Beeper is closed.'),
replies: cell('yes'),
files: cell('partial', 'Uploaded files work both ways. Google Drive files come into Beeper as links, and Beeper cannot send native Drive file cards.'),
voice: cell('partial', 'Audio messages appear as regular attachments in Beeper, without Google Chat\'s voice-message features or transcripts.'),
history: cell('partial', 'Beeper adds up to 10 recent chats. Direct messages and unthreaded chats start with at most one recent main message, and replies to that message may also appear. Threaded Spaces do not receive an initial thread-history import, and older history cannot be loaded by scrolling.'),
redactions: cell('yes'),
createChat: cell('no'),
edits: cell('yes'),
typing: cell('yes', 'Typing indicators work both ways for the conversation. Beeper does not preserve which thread someone is typing in.'),
captions: cell('oneway-in', 'Text sent with a file in Google Chat appears in Beeper as a separate message. A caption added in Beeper is not sent as a caption and may become only the uploaded filename.'),
formatting: cell('partial', 'Common formatting, links, mentions, code, colors, and bulleted lists work. Unsupported formatting is simplified or removed.'),
disappearing: cell('no', 'Google Chat can remove messages after 24 hours when history is turned off. Beeper does not support this setting.'),
timeBasedMedia: cell('no', 'Beeper does not support View Once or Allow Replay photos and videos. You may see a notice to open them in the original app, or receive them as a regular file instead.'),
online: cell('no'),
polls: cell('no', 'Google Chat\'s Poll app is not supported in Beeper.'),
threads: cell('partial', 'New thread messages work. Threaded Spaces do not import older thread roots during setup, and typing indicators are shown only for the overall conversation.'),
},
},
},
TwitterX: {
modes: ['cloud', 'onDevice'],
availability: 'public',
support: {
cloud: {
reactions: cell('yes'),
receipts: cell('partial', 'Reads sync both ways, but Beeper may not always show the read marker.'),
push: cell('yes', 'Notifications can arrive while Beeper is closed.'),
replies: cell('yes'),
files: cell('partial', 'Photos, videos, and GIFs work. X\'s newer document and file attachments do not.'),
voice: cell('oneway-in', 'Voice messages can come from X into Beeper, but you cannot send one back from Beeper.'),
history: cell('partial', 'All X Chat conversations are added. Beeper checks message requests separately using one response from X, so older requests may be missing. Each chat starts with up to 50 messages; scroll up to load 50 more at a time. Beeper does not limit how far you can keep loading.'),
redactions: cell('partial', 'Delete for everyone works in encrypted X Chat conversations. In unencrypted X Chat conversations, older chats, and message requests, you can delete messages only for yourself.'),
createChat: cell('partial', 'You can start a direct message by X username. Starting a new group chat in Beeper is not supported.'),
edits: cell('partial', 'Only encrypted X Chat messages can be edited in Beeper. You can make up to 10 edits within 15 minutes.'),
typing: cell('partial', 'Typing indicators work both ways in X Chat. In older chats and unencrypted message requests, Beeper can send your typing indicator but cannot show when the other person is typing.'),
captions: cell('yes'),
formatting: cell('na'),
disappearing: cell('no'),
timeBasedMedia: cell('no', 'Beeper does not support View Once or Allow Replay photos and videos. You may see a notice to open them in the original app, or receive them as a regular file instead.'),
online: cell('na'),
polls: cell('na'),
threads: cell('na'),
},
onDevice: {
reactions: cell('yes'),
receipts: cell('partial', 'Reads sync both ways, but Beeper may not always show the read marker.'),
push: cell('yes', 'Notifications can arrive while Beeper is closed.'),
replies: cell('yes'),
files: cell('partial', 'Photos, videos, and GIFs work. X\'s newer document and file attachments do not.'),
voice: cell('oneway-in', 'Voice messages can come from X into Beeper, but you cannot send one back from Beeper.'),
history: cell('partial', 'All X Chat conversations are added. Beeper checks message requests separately using one response from X, so older requests may be missing. Each chat starts with up to 50 messages; scroll up to load 50 more at a time. Beeper does not limit how far you can keep loading.'),
redactions: cell('partial', 'Delete for everyone works in encrypted X Chat conversations. In unencrypted X Chat conversations, older chats, and message requests, you can delete messages only for yourself.'),
createChat: cell('partial', 'You can start a direct message by X username. Starting a new group chat in Beeper is not supported.'),
edits: cell('partial', 'Only encrypted X Chat messages can be edited in Beeper. You can make up to 10 edits within 15 minutes.'),
typing: cell('yes', 'Typing indicators work both ways in X Chat. In older chats and message requests, you see the other person typing only while that chat is open.'),
captions: cell('yes'),
formatting: cell('na'),
disappearing: cell('no'),
timeBasedMedia: cell('no', 'Beeper does not support View Once or Allow Replay photos and videos. You may see a notice to open them in the original app, or receive them as a regular file instead.'),
online: cell('na'),
polls: cell('na'),
threads: cell('na'),
},
},
},
Slack: {
modes: ['cloud'],
availability: 'public',
support: {
cloud: {
reactions: cell('yes', 'Standard and custom workspace emoji reactions work both ways, including adding and removing them.'),
receipts: cell('na', 'Beeper keeps your place in Slack conversations, but Slack does not show other people whether you have read a message.'),
push: cell('yes', 'Notifications can arrive while Beeper is closed. New Slack channel rooms start muted in Beeper; unmute a channel to enable its notifications. Slack notification settings are not imported.'),
replies: cell('na', 'Slack does not offer quoted replies. It uses threads instead.'),
files: cell('yes', 'Photos, videos, GIFs, audio, and files up to Slack’s 1 GB limit work both ways, subject to workspace rules.'),
voice: cell('partial', 'Voice messages work, but some recordings need to be converted. The maximum length is five minutes.'),
history: cell('partial', 'Beeper does not bring in every Slack message at once. It starts with a recent batch in each conversation, and you can scroll up to load older DMs and group DMs. Channel history and thread replies are more limited. Slack retention and access rules still apply.'),
redactions: cell('yes', 'Deleting messages works when your Slack workspace allows members to delete them.'),
createChat: cell('partial', 'You can start direct messages and group DMs with 2–8 other people. Beeper cannot create Slack channels.'),
edits: cell('yes', 'Editing follows your Slack workspace rules, which may set a time limit or disable editing.'),
typing: cell('yes', 'Typing indicators work both ways in DMs, group DMs, and channels. Slack does not identify which thread someone is typing in.'),
captions: cell('yes', 'Captions work on media and files. With several files in one Slack message, the text may appear separately rather than on every file.'),
formatting: cell('partial', 'Bold, italics, strikethrough, code, quotes, lists, links, mentions, and custom emoji work. Underlining is removed, headings become bold text with # marks, and some Beeper room or message links remain Beeper links.'),
disappearing: cell('na'),
timeBasedMedia: cell('no', 'Beeper does not support View Once or Allow Replay photos and videos. You may see a notice to open them in the original app, or receive them as a regular file instead.'),
online: cell('no', 'Slack online and away status does not sync to Beeper.'),
polls: cell('na'),
threads: cell('yes', 'Threads work in DMs, group DMs, and channels. Thread history is brought in separately and may be more limited than the main conversation.'),
},
},
},
Discord: {
modes: ['cloud'],
availability: 'public',
support: {
cloud: {
reactions: cell('partial', 'Live reactions work both ways, including custom emoji. Reactions already present in imported history are not added, and removing all reactions at once may leave some visible in Beeper.'),
receipts: cell('na', 'Beeper keeps your place in Discord conversations, but Discord does not show other people whether you have read a message.'),
push: cell('yes', 'Notifications can arrive while Beeper is closed for bridged DMs, group DMs, and visible text or announcement channels in selected servers. Muted conversations and device notification settings still apply.'),
replies: cell('yes'),
files: cell('partial', 'Incoming Discord attachments work. Files, photos, GIFs, video, or audio sent from Beeper are limited to 10 MB, even if Nitro or a boosted server allows more.'),
voice: cell('yes', 'Voice messages work both ways. If you add a caption, Discord receives it as a regular audio attachment.'),
history: cell('partial', 'Beeper adds up to 10 recently active DMs and group DMs, with up to 50 recent messages in each. Older private history imports automatically, 100 at a time, with no Beeper-set total limit. Selecting a server adds its visible text and announcement channels with up to 50 recent messages. Older channel history imports automatically in up to 10 batches of 100, for up to 1,000 older messages. Threads found in those channels start with up to 50 replies; older replies are not loaded separately. Server history requires Discord\'s Read Message History permission. Forum, media, voice, and stage channels are not imported. If Discord cannot resume after a disconnect, Beeper recovers at most 100 missed messages per chat.'),
redactions: cell('partial', 'Individual message deletions work both ways. Moderator bulk deletions in server channels do not sync to Beeper.'),
createChat: cell('no', 'You cannot start a Discord DM or group DM from Beeper. New incoming DMs appear automatically. Beeper Cloud can send in a one-to-one chat only when that person is on your Discord friends list.'),
edits: cell('yes'),
typing: cell('partial', 'Typing indicators work both ways in DMs, group DMs, and server channels. In a thread, typing appears in the parent channel instead.'),
captions: cell('yes'),
formatting: cell('partial', 'Common formatting, links, mentions, lists, quotes, and code work. Some Beeper links, @room mentions, and custom-emoji formatting are not supported.'),
disappearing: cell('na'),
timeBasedMedia: cell('no', 'Beeper does not support View Once or Allow Replay photos and videos. You may see a notice to open them in the original app, or receive them as a regular file instead.'),
online: cell('no', 'Discord online and activity status does not appear in Beeper.'),
polls: cell('no', 'Discord polls do not appear in Beeper, and you cannot create or vote in them from Beeper.'),
threads: cell('partial', 'Public and announcement threads appear inside their parent channel, and you can create them from Beeper. Private threads cannot be created. Beeper imports up to 50 replies when it finds the thread starter. Archived threads are not fully discovered; private threads may lose their grouping or be missing. Forum and media posts are not supported.'),
},
},
},
LinkedIn: {
modes: ['cloud', 'onDevice'],
availability: 'public',
support: {
cloud: {
reactions: cell('yes'),
receipts: cell('yes'),
push: cell('yes', 'Notifications can arrive while Beeper is closed.'),
replies: cell('yes'),
files: cell('yes', 'Photos, GIFs, videos, audio, and documents up to 20 MB work.'),
voice: cell('yes', 'Voice messages can be up to one minute long.'),
history: cell('partial', 'Beeper checks your 20 most recent LinkedIn chats and automatically adds up to 11 that are eligible. Spam, archived chats, and chats you have left are skipped; other chats can appear later when they become active. Each added chat starts with up to 20 messages; scroll up to load 20 more at a time. Beeper does not limit how far you can keep loading.'),
redactions: cell('yes', 'You can delete messages within 60 minutes of sending them.'),
createChat: cell('yes'),
edits: cell('yes', 'You can edit text messages within 60 minutes of sending them.'),
typing: cell('yes'),
captions: cell('yes'),
formatting: cell('na', '@mentions work. Bold, italics, and other text styles are sent as plain text.'),
disappearing: cell('na'),
timeBasedMedia: cell('no', 'Beeper does not support View Once or Allow Replay photos and videos. You may see a notice to open them in the original app, or receive them as a regular file instead.'),
online: cell('no', 'LinkedIn shows active status, but Beeper does not.'),
polls: cell('na'),
threads: cell('na'),
},
onDevice: {
reactions: cell('yes'),
receipts: cell('yes'),
push: cell('yes', 'Notifications can arrive while Beeper is closed.'),
replies: cell('yes'),
files: cell('yes', 'Photos, GIFs, videos, audio, and documents up to 20 MB work.'),
voice: cell('yes', 'Voice messages can be up to one minute long.'),
history: cell('partial', 'Beeper checks your 20 most recent LinkedIn chats and automatically adds up to 11 that are eligible. Spam, archived chats, and chats you have left are skipped; other chats can appear later when they become active. Each added chat starts with up to 50 messages; scroll up to load 50 more at a time. Beeper does not limit how far you can keep loading.'),
redactions: cell('yes', 'You can delete messages within 60 minutes of sending them.'),
createChat: cell('yes'),
edits: cell('yes', 'You can edit text messages within 60 minutes of sending them.'),
typing: cell('yes'),
captions: cell('yes'),
formatting: cell('na', '@mentions work. Bold, italics, and other text styles are sent as plain text.'),
disappearing: cell('na'),
timeBasedMedia: cell('no', 'Beeper does not support View Once or Allow Replay photos and videos. You may see a notice to open them in the original app, or receive them as a regular file instead.'),
online: cell('no', 'LinkedIn shows active status, but Beeper does not.'),
polls: cell('na'),
threads: cell('na'),
},
},
},
LINE: {
modes: ['cloud'],
availability: 'public',
support: {
cloud: {
reactions: cell('partial', 'Reactions sent in LINE appear in Beeper. In Beeper, each person can add one reaction per message, using one of 122 supported emoji.'),
receipts: cell('yes'),
push: cell('yes', 'Notifications can arrive while Beeper is closed. Beeper also tries to keep notifications in the LINE app turned on.'),
replies: cell('yes'),
files: cell('yes', 'Photos, GIFs, videos, audio, voice messages, and files up to 100 MB work. If LINE rejects that kind of file, Beeper puts it inside a ZIP file and tries again.'),
voice: cell('yes'),
history: cell('partial', 'Every active chat and joined group is included. Group invitations appear as message requests. DMs with blocked contacts are skipped. Beeper imports one batch of up to 50 recent messages from each included chat. Older messages cannot be loaded by scrolling.'),
redactions: cell('partial', 'You can delete messages in LINE or Beeper. LINE\'s time limit is usually 1 or 24 hours, depending on your account and country. Some Premium accounts allow up to 7 days. Beeper currently shows a 24-hour limit.'),
createChat: cell('yes'),
edits: cell('no', 'LINE is rolling out message editing in some countries, but editing is not available in Beeper yet.'),
typing: cell('no', 'Typing indicators do not pass from Beeper to LINE or from LINE to Beeper.'),
captions: cell('na'),
formatting: cell('na'),
disappearing: cell('na'),
timeBasedMedia: cell('no', 'Beeper does not support View Once or Allow Replay photos and videos. You may see a notice to open them in the original app, or receive them as a regular file instead.'),
online: cell('na'),
polls: cell('no', 'LINE has polls, but Beeper cannot create one, vote in one, or show the results yet.'),
threads: cell('na'),
},
},
},
Tumblr: {
modes: ['cloud', 'onDevice'],
availability: 'internal',
support: {
cloud: {
reactions: cell('na'),
receipts: cell('na', 'Beeper keeps your place in Tumblr conversations, but it does not show whether the other person read a message.'),
push: cell('yes', 'Notifications can arrive while Beeper is closed.'),
replies: cell('na'),
files: cell('partial', 'Images and GIFs up to about 5 MB work. Documents and other files do not.'),
voice: cell('na'),
history: cell('yes', 'Beeper adds the conversations Tumblr makes available. Accounts with an extremely large conversation list may not finish importing every chat. Each starts with up to 50 recent messages, then loads older messages automatically in batches of up to 50. You can also scroll up to request more. Beeper does not set a history limit, but Tumblr may not provide every older message.'),
redactions: cell('na', 'Tumblr only lets you delete an entire conversation, not individual messages.'),
createChat: cell('no', 'You can start a direct message in Tumblr, but not from Beeper yet.'),
edits: cell('na'),
typing: cell('na'),
captions: cell('na'),
formatting: cell('na'),
disappearing: cell('na'),
timeBasedMedia: cell('no', 'Beeper does not support View Once or Allow Replay photos and videos. You may see a notice to open them in the original app, or receive them as a regular file instead.'),
online: cell('no', 'Tumblr has an activity status, but Beeper does not show it.'),
polls: cell('na'),
threads: cell('na'),
},
onDevice: {
reactions: cell('na'),
receipts: cell('na', 'Beeper keeps your place in Tumblr conversations, but it does not show whether the other person read a message.'),
push: cell('yes', 'Your phone can wake Beeper in the background when a new Tumblr message arrives.'),
replies: cell('na'),
files: cell('partial', 'Images and GIFs up to about 5 MB work. Documents and other files do not.'),
voice: cell('na'),
history: cell('yes', 'Beeper adds the conversations Tumblr makes available. Accounts with an extremely large conversation list may not finish importing every chat. Each starts with up to 50 recent messages; scroll up to load older messages, up to 50 at a time. Beeper does not set a history limit, but Tumblr may not provide every older message.'),
redactions: cell('na', 'Tumblr only lets you delete an entire conversation, not individual messages.'),
createChat: cell('no', 'You can start a direct message in Tumblr, but not from Beeper yet.'),
edits: cell('na'),
typing: cell('na'),
captions: cell('na'),
formatting: cell('na'),
disappearing: cell('na'),
timeBasedMedia: cell('no', 'Beeper does not support View Once or Allow Replay photos and videos. You may see a notice to open them in the original app, or receive them as a regular file instead.'),
online: cell('no', 'Tumblr has an activity status, but Beeper does not show it.'),
polls: cell('na'),
threads: cell('na'),
},
},
},
};
const networkOrder = networks.map(nk);
const publicNetworkOrder = networkOrder.filter(function(networkKey) {
return networkData[networkKey].availability === 'public';
});
const statusDefinitions = {
yes: { label: function() { return 'Supported'; }, className: 'badge-yes', summaryBucket: 'yes' },
no: { label: function() { return 'Not supported'; }, className: 'badge-no', summaryBucket: 'no' },
partial: { label: function() { return 'Limited'; }, className: 'badge-partial', summaryBucket: 'limited' },
'oneway-in': { label: function(networkLabel) { return 'From ' + networkLabel + ' only'; }, className: 'badge-oneway', summaryBucket: 'oneway' },
'oneway-out': { label: function() { return 'From Beeper only'; }, className: 'badge-oneway', summaryBucket: 'oneway' },
na: { label: function() { return 'Not offered'; }, className: 'badge-na', summaryBucket: 'na' },
};
const availabilityLabels = {
public: '',
internal: 'Beeper team only',
};
const allowedStatuses = new Set(Object.keys(statusDefinitions));
const allFeatures = featureGroups.flatMap(function(group) { return group.features; });
const selectedModes = {};
function validateData() {
const errors = [];
const seenFeatures = new Set();
const seenNetworks = new Set();
allFeatures.forEach(function(feature) {
if (seenFeatures.has(feature.key)) errors.push('Duplicate feature key: ' + feature.key);
seenFeatures.add(feature.key);
});
networkOrder.forEach(function(networkKey) {
if (seenNetworks.has(networkKey)) errors.push('Duplicate network in order: ' + networkKey);
seenNetworks.add(networkKey);
const network = networkData[networkKey];
if (!network) {
errors.push('Missing network: ' + networkKey);
return;
}
if (!network.modes || network.modes.length === 0) {
errors.push('No modes declared for ' + networkKey);
return;
}
if (!network.support) {
errors.push('No support data for ' + networkKey);
return;
}
if (!Object.prototype.hasOwnProperty.call(availabilityLabels, network.availability)) {
errors.push('Invalid availability ' + network.availability + ' for ' + networkKey);
}
if (network.availabilityByMode) {
network.modes.forEach(function(mode) {
const modeAvailability = network.availabilityByMode[mode];
if (!Object.prototype.hasOwnProperty.call(availabilityLabels, modeAvailability)) {
errors.push('Invalid availability ' + modeAvailability + ' for ' + networkKey + '/' + mode);
}
});
Object.keys(network.availabilityByMode).forEach(function(mode) {
if (network.modes.indexOf(mode) === -1) errors.push('Availability declared for unknown mode ' + mode + ' in ' + networkKey);
});
}
if (network.platformLabel && typeof network.platformLabel !== 'string') {
errors.push('Invalid platform label for ' + networkKey);
}
if (network.connectionNote && typeof network.connectionNote !== 'string') {
errors.push('Invalid connection note for ' + networkKey);
}
network.modes.forEach(function(mode) {
if (!Object.prototype.hasOwnProperty.call(modeLabels, mode)) {
errors.push('Unknown mode ' + mode + ' for ' + networkKey);
}
const modeData = network.support[mode];
if (!modeData) {
errors.push('Missing ' + mode + ' data for ' + networkKey);
return;
}
allFeatures.forEach(function(feature) {
const item = modeData[feature.key];
if (!item) {
errors.push('Missing ' + feature.key + ' for ' + networkKey + '/' + mode);
return;
}
if (!allowedStatuses.has(item.status)) {
errors.push('Invalid status ' + item.status + ' for ' + networkKey + '/' + mode + '/' + feature.key);
}
if (item.status === 'partial' && !item.detail) {
errors.push('Partial status requires detail for ' + networkKey + '/' + mode + '/' + feature.key);
}
});
Object.keys(modeData).forEach(function(featureKey) {
if (!seenFeatures.has(featureKey)) errors.push('Unknown feature ' + featureKey + ' for ' + networkKey + '/' + mode);
});
});
Object.keys(network.support).forEach(function(mode) {
if (network.modes.indexOf(mode) === -1) errors.push('Undeclared mode ' + mode + ' for ' + networkKey);
});
});
Object.keys(networkData).forEach(function(networkKey) {
if (networkOrder.indexOf(networkKey) === -1) errors.push('Network missing from order: ' + networkKey);
});
return errors;
}
function badgeHTML(status, networkLabel) {
const definition = statusDefinitions[status];
if (!definition) return '<span class="badge badge-na">Unknown</span>';
return '<span class="badge ' + definition.className + '">' + definition.label(networkLabel) + '</span>';
}
function modesDiffer(network, featureKey) {
if (network.modes.length < 2) return false;
const first = network.support[network.modes[0]][featureKey];
return network.modes.slice(1).some(function(mode) {
const current = network.support[mode][featureKey];
return current.status !== first.status || (current.detail || '') !== (first.detail || '');
});
}
function summaryFor(modeData) {
const counts = { yes: 0, limited: 0, oneway: 0, no: 0, na: 0 };
allFeatures.forEach(function(feature) {
const status = modeData[feature.key].status;
counts[statusDefinitions[status].summaryBucket]++;
});
const parts = [counts.yes + ' supported', counts.limited + ' limited'];
if (counts.oneway > 0) parts.push(counts.oneway + ' one-way');
parts.push(counts.no + ' not supported', counts.na + ' not offered');
return parts.join(' · ');
}
function renderFeatureRow(network, networkLabel, mode, feature) {
const item = network.support[mode][feature.key];
const differs = modesDiffer(network, feature.key);
const name = feature.label + (differs ? '<span class="mode-diff-tag">varies by connection</span>' : '');
if (item.detail) {
return '<div class="feature-row-stacked"><div class="feature-name">' + name + '<div class="feature-detail">' + item.detail + '</div></div>' + badgeHTML(item.status, networkLabel) + '</div>';
}
return '<div class="feature-row"><span class="feature-name">' + name + '</span>' + badgeHTML(item.status, networkLabel) + '</div>';
}
function render(networkKey, focusMode) {
const network = networkData[networkKey];
const networkLabel = networkLabels[networkKey];
const mode = selectedModes[networkKey] || network.modes[0];
selectedModes[networkKey] = mode;
const modeData = network.support[mode];
let html = '<div class="label-card">';
html += '<div class="label-header"><div class="label-main"><div class="label-title"><h2>' + networkLabel + ' on Beeper</h2>';
const availability = network.availabilityByMode ? network.availabilityByMode[mode] : network.availability;
const availabilityLabel = availabilityLabels[availability];
if (availabilityLabel) html += '<span class="availability-badge">' + availabilityLabel + '</span>';
if (network.platformLabel) html += '<span class="platform-badge">' + network.platformLabel + '</span>';
html += '</div>';
if (network.modes.length === 1) {
html += '<span class="connection-badge">' + modeLabels[mode] + ' only</span>';
} else {
html += '<div class="mode-switch" role="group" aria-label="How this chat app connects to Beeper">';
network.modes.forEach(function(option) {
const active = option === mode;
html += '<button type="button" class="mode-btn' + (active ? ' active' : '') + '" data-mode="' + option + '" aria-pressed="' + active + '">' + modeLabels[option] + '</button>';
});
html += '</div>';
}
html += '</div><div class="support-summary">' + summaryFor(modeData) + '</div></div>';
if (network.connectionNote) html += '<p class="connection-note">' + network.connectionNote + '</p>';
featureGroups.forEach(function(group, groupIndex) {
if (groupIndex > 0) html += '<div class="divider"></div>';
html += '<p class="section-label">' + group.label + '</p>';
group.features.forEach(function(feature) {
html += renderFeatureRow(network, networkLabel, mode, feature);
});
});
html += '</div>';
document.getElementById('labelOutput').innerHTML = html;
document.querySelectorAll('.mode-btn').forEach(function(button) {
button.onclick = function() {
selectedModes[networkKey] = button.dataset.mode;
render(networkKey, button.dataset.mode);
};
});
if (focusMode) {
const activeModeButton = document.querySelector('.mode-btn[data-mode="' + focusMode + '"]');
if (activeModeButton) activeModeButton.focus();
}
document.getElementById('statusOutput').textContent = networkLabel + ', ' + modeLabels[mode] + '. ' + summaryFor(modeData);
notifyHeight();
}
const validationErrors = validateData();
const grid = document.getElementById('netGrid');
if (validationErrors.length > 0) {
document.getElementById('labelOutput').innerHTML = '<div class="data-error" role="alert">We couldn\'t load this support information. Please try again later.</div>';
console.error('Support matrix data error:', validationErrors);
notifyHeight();
} else {
publicNetworkOrder.forEach(function(networkKey, index) {
const button = document.createElement('button');
button.type = 'button';
button.className = 'net-btn' + (index === 0 ? ' active' : '');
button.textContent = networkLabels[networkKey];
button.setAttribute('aria-pressed', index === 0 ? 'true' : 'false');
button.onclick = function() {
document.querySelectorAll('.net-btn').forEach(function(otherButton) {
otherButton.classList.remove('active');
otherButton.setAttribute('aria-pressed', 'false');
});
button.classList.add('active');
button.setAttribute('aria-pressed', 'true');
render(networkKey);
};
grid.appendChild(button);
});
render(publicNetworkOrder[0]);
}
function notifyHeight() {
window.parent.postMessage({
type: 'resize',
height: Math.ceil(document.body.scrollHeight),
}, '*');
}
window.addEventListener('message', function(event) {
const allowedParentOrigins = [
'https://help.beeper.com',
'https://beeper.helpjuice.com',
];
if (