@rapidmx/meet-plugin 0.15.0 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/README.md +35 -55
  2. package/apps/meet/[token].tsx +341 -341
  3. package/apps/meet/_CallControls.tsx +508 -483
  4. package/apps/meet/_CallView.tsx +1060 -924
  5. package/apps/meet/_ChatPanel.tsx +194 -0
  6. package/apps/meet/_ColorWheel.tsx +136 -0
  7. package/apps/meet/_MeetLobby.tsx +224 -224
  8. package/apps/meet/_ParticipantTile.tsx +243 -242
  9. package/apps/meet/_Whiteboard.tsx +577 -0
  10. package/apps/meet/_icons.tsx +135 -90
  11. package/apps/meet/_meetApi.ts +213 -212
  12. package/apps/shared/collab/CollabSession.ts +675 -0
  13. package/apps/shared/collab/WhiteboardModel.ts +216 -0
  14. package/apps/shared/collab/chatMarkdown.tsx +375 -0
  15. package/apps/shared/collab/color.ts +67 -0
  16. package/apps/shared/collab/protocol.ts +321 -0
  17. package/apps/shared/collab/whiteboardRender.ts +311 -0
  18. package/apps/shared/media/filters/VideoFilterProcessor.ts +408 -408
  19. package/apps/shared/media/filters/mlModels.ts +174 -174
  20. package/apps/shared/relay/RelayClient.ts +2 -1
  21. package/apps/shared/relay/RelayTransport.ts +2 -1
  22. package/apps/shared/relay/relayEnv.ts +1 -1
  23. package/apps/shared/signaling/SignalingClient.ts +335 -0
  24. package/apps/shared/webrtc/MeshConnectionManager.ts +971 -971
  25. package/apps/shared/webrtc/types.ts +335 -316
  26. package/dist/apps/meet/[token].js +1 -1
  27. package/dist/apps/meet/_CallControls.d.ts +12 -4
  28. package/dist/apps/meet/_CallControls.js +5 -5
  29. package/dist/apps/meet/_CallView.d.ts +19 -4
  30. package/dist/apps/meet/_CallView.js +100 -11
  31. package/dist/apps/meet/_ChatPanel.d.ts +24 -0
  32. package/dist/apps/meet/_ChatPanel.js +116 -0
  33. package/dist/apps/meet/_ColorWheel.d.ts +14 -0
  34. package/dist/apps/meet/_ColorWheel.js +82 -0
  35. package/dist/apps/meet/_ParticipantTile.d.ts +2 -1
  36. package/dist/apps/meet/_ParticipantTile.js +1 -1
  37. package/dist/apps/meet/_Whiteboard.d.ts +64 -0
  38. package/dist/apps/meet/_Whiteboard.js +312 -0
  39. package/dist/apps/meet/_icons.d.ts +12 -0
  40. package/dist/apps/meet/_icons.js +12 -0
  41. package/dist/apps/meet/_meetApi.d.ts +8 -7
  42. package/dist/apps/meet/_meetApi.js +2 -2
  43. package/dist/apps/shared/collab/CollabSession.d.ts +208 -0
  44. package/dist/apps/shared/collab/CollabSession.js +559 -0
  45. package/dist/apps/shared/collab/WhiteboardModel.d.ts +54 -0
  46. package/dist/apps/shared/collab/WhiteboardModel.js +197 -0
  47. package/dist/apps/shared/collab/chatMarkdown.d.ts +65 -0
  48. package/dist/apps/shared/collab/chatMarkdown.js +311 -0
  49. package/dist/apps/shared/collab/color.d.ts +18 -0
  50. package/dist/apps/shared/collab/color.js +59 -0
  51. package/dist/apps/shared/collab/protocol.d.ts +157 -0
  52. package/dist/apps/shared/collab/protocol.js +222 -0
  53. package/dist/apps/shared/collab/whiteboardRender.d.ts +31 -0
  54. package/dist/apps/shared/collab/whiteboardRender.js +268 -0
  55. package/dist/apps/shared/relay/RelayClient.js +2 -1
  56. package/dist/apps/shared/relay/RelayTransport.d.ts +2 -1
  57. package/dist/apps/shared/relay/RelayTransport.js +2 -1
  58. package/dist/apps/shared/relay/relayEnv.d.ts +1 -1
  59. package/dist/apps/shared/signaling/SignalingClient.d.ts +95 -0
  60. package/dist/apps/shared/signaling/SignalingClient.js +278 -0
  61. package/dist/apps/shared/webrtc/MeshConnectionManager.d.ts +7 -7
  62. package/dist/apps/shared/webrtc/MeshConnectionManager.js +8 -8
  63. package/dist/apps/shared/webrtc/types.d.ts +38 -24
  64. package/dist/apps/shared/webrtc/types.js +3 -4
  65. package/dist/lib/routes/BaseVideoMeetingRoute.js +171 -69
  66. package/dist/lib/routes/BaseVideoMeetingRoute.js.map +1 -1
  67. package/dist/lib/util/SignalingHub.js +339 -0
  68. package/dist/lib/util/SignalingHub.js.map +1 -0
  69. package/dist/types/routes/BaseVideoMeetingRoute.d.ts +68 -46
  70. package/dist/types/util/SignalingHub.d.ts +112 -0
  71. package/package.json +208 -208
  72. package/apps/shared/push/GuestSignalingClient.ts +0 -359
  73. package/dist/apps/shared/push/GuestSignalingClient.d.ts +0 -98
  74. package/dist/apps/shared/push/GuestSignalingClient.js +0 -312
@@ -1,90 +1,135 @@
1
- ///////////////////////////////////////////////////////////////////////////////
2
- // Copyright (C) 2026 Jean-Philippe Steinmetz. All rights reserved.
3
- // SPDX-License-Identifier: MPL-2.0
4
- ///////////////////////////////////////////////////////////////////////////////
5
- /** The in-call control icons, as inline SVG (24x24, Material Design paths, `currentColor`) so this plugin needs no
6
- * icon package of its own. Decorative - every button that uses one carries its own `aria-label`. */
7
- import React from "react";
8
-
9
- function Icon({ path, className = "w-6 h-6" }: { path: string; className?: string }) {
10
- return (
11
- <svg viewBox="0 0 24 24" className={className} fill="currentColor" aria-hidden="true" focusable="false">
12
- <path d={path} />
13
- </svg>
14
- );
15
- }
16
-
17
- export const MicIcon = () => (
18
- <Icon path="M12 14c1.66 0 3-1.34 3-3V5c0-1.66-1.34-3-3-3S9 3.34 9 5v6c0 1.66 1.34 3 3 3zm5.3-3c0 3-2.54 5.1-5.3 5.1S6.7 14 6.7 11H5c0 3.41 2.72 6.23 6 6.72V21h2v-3.28c3.28-.48 6-3.3 6-6.72h-1.7z" />
19
- );
20
-
21
- export const MicOffIcon = () => (
22
- <Icon path="M19 11h-1.7c0 .74-.16 1.43-.43 2.05l1.23 1.23c.56-.98.9-2.09.9-3.28zm-4.02.17c0-.06.02-.11.02-.17V5c0-1.66-1.34-3-3-3S9 3.34 9 5v.18l5.98 5.99zM4.27 3L3 4.27l6.01 6.01V11c0 1.66 1.33 3 2.99 3 .22 0 .44-.03.65-.08l1.66 1.66c-.71.33-1.5.52-2.31.52-2.76 0-5.3-2.1-5.3-5.1H5c0 3.41 2.72 6.23 6 6.72V21h2v-3.28c.91-.13 1.77-.45 2.54-.9L19.73 21 21 19.73 4.27 3z" />
23
- );
24
-
25
- export const VideoIcon = () => <Icon path="M17 10.5V7c0-.55-.45-1-1-1H4c-.55 0-1 .45-1 1v10c0 .55.45 1 1 1h12c.55 0 1-.45 1-1v-3.5l4 4v-11l-4 4z" />;
26
-
27
- export const VideoOffIcon = () => (
28
- <Icon path="M21 6.5l-4 4V7c0-.55-.45-1-1-1H9.82L21 17.18V6.5zM3.27 2L2 3.27 4.73 6H4c-.55 0-1 .45-1 1v10c0 .55.45 1 1 1h12c.21 0 .39-.08.54-.18L19.73 21 21 19.73 3.27 2z" />
29
- );
30
-
31
- export const ScreenShareIcon = () => (
32
- <Icon path="M21 3H3c-1.11 0-2 .89-2 2v14c0 1.11.89 2 2 2h18c1.1 0 2-.9 2-2V5c0-1.11-.9-2-2-2zm0 16.02H3V4.98h18v14.04zM10 12H8l4-4 4 4h-2v4h-4v-4z" />
33
- );
34
-
35
- export const EmojiIcon = () => (
36
- <Icon path="M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8zm3.5-9c.83 0 1.5-.67 1.5-1.5S16.33 8 15.5 8 14 8.67 14 9.5s.67 1.5 1.5 1.5zm-7 0c.83 0 1.5-.67 1.5-1.5S9.33 8 8.5 8 7 8.67 7 9.5 7.67 11 8.5 11zm3.5 6.5c2.33 0 4.31-1.46 5.11-3.5H6.89c.8 2.04 2.78 3.5 5.11 3.5z" />
37
- );
38
-
39
- export const GridIcon = () => <Icon path="M3 3h8v8H3V3zm10 0h8v8h-8V3zM3 13h8v8H3v-8zm10 0h8v8h-8v-8z" />;
40
-
41
- export const FocusIcon = () => <Icon path="M3 3h18v12H3V3zm0 14h5v4H3v-4zm6.5 0h5v4h-5v-4zM16 17h5v4h-5v-4z" />;
42
-
43
- export const LeaveIcon = () => (
44
- <Icon path="M12 9c-1.6 0-3.15.25-4.6.72v3.1c0 .39-.23.74-.56.9-.98.49-1.87 1.12-2.66 1.85-.18.18-.43.28-.7.28-.28 0-.53-.11-.71-.29L.29 13.08c-.18-.17-.29-.42-.29-.7 0-.28.11-.53.29-.71C3.34 8.78 7.46 7 12 7s8.66 1.78 11.71 4.67c.18.18.29.43.29.71 0 .28-.11.53-.29.71l-2.48 2.48c-.18.18-.43.29-.71.29-.27 0-.52-.11-.7-.28-.79-.74-1.69-1.36-2.67-1.85-.33-.16-.56-.5-.56-.9v-3.1C15.15 9.25 13.6 9 12 9z" />
45
- );
46
-
47
- export const ChevronUpIcon = () => <Icon path="M12 8l-6 6 1.41 1.41L12 10.83l4.59 4.58L18 14z" className="w-5 h-5" />;
48
-
49
- /** A raised hand, as an emoji glyph - Material's own "back hand" path isn't among the ones bundled here. */
50
- export const HandIcon = () => (
51
- <span className="text-xl leading-none" aria-hidden="true">
52
- ✋
53
- </span>
54
- );
55
-
56
- export const EffectsIcon = () => (
57
- <Icon path="M19 9l1.25-2.75L23 5l-2.75-1.25L19 1l-1.25 2.75L15 5l2.75 1.25L19 9zm-7.5.5L9 4 6.5 9.5 1 12l5.5 2.5L9 20l2.5-5.5L17 12l-5.5-2.5zM19 15l-1.25 2.75L15 19l2.75 1.25L19 23l1.25-2.75L23 19l-2.75-1.25L19 15z" />
58
- );
59
-
60
- export const RotateIcon = () => (
61
- <Icon path="M12 6V2L7 7l5 5V8c3.31 0 6 2.69 6 6s-2.69 6-6 6-6-2.69-6-6H4c0 4.42 3.58 8 8 8s8-3.58 8-8-3.58-8-8-8z" />
62
- );
63
-
64
- export const FlipIcon = () => (
65
- <Icon path="M15 21h2v-2h-2v2zm4-12h2V7h-2v2zM3 5v14c0 1.1.9 2 2 2h4v-2H5V5h4V3H5c-1.1 0-2 .9-2 2zm16-2v2h2c0-1.1-.9-2-2-2zm-8 20h2V1h-2v22zm8-6h2v-2h-2v2zM15 5h2V3h-2v2zm4 8h2v-2h-2v2zm0 8c1.1 0 2-.9 2-2h-2v2z" />
66
- );
67
-
68
- /** A talking stick: a rounded shaft with a grip knob at one end, drawn (not a Material path - there isn't one for
69
- * this) as a diagonal bar with a circle, rather than reusing `HandIcon`'s emoji-glyph fallback, since this one
70
- * needs to light up (`currentColor`) the same way every other control icon here does. */
71
- export const BatonIcon = () => (
72
- <svg viewBox="0 0 24 24" className="w-6 h-6" fill="currentColor" aria-hidden="true" focusable="false">
73
- <g transform="rotate(45 12 12)">
74
- <rect x="5" y="10.5" width="13" height="3" rx="1.5" />
75
- <circle cx="19" cy="12" r="3" />
76
- </g>
77
- </svg>
78
- );
79
-
80
- export const OverflowIcon = () => <Icon path="M6 10c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm12 0c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm-6 0c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z" />;
81
-
82
- /** A plain "X" - removing a participant from the call (the participants drawer's "Remove" row button). Drawn with
83
- * strokes rather than a filled Material path, same "simple primitives, not a recalled path" reasoning as
84
- * `BatonIcon`. */
85
- export const RemoveIcon = () => (
86
- <svg viewBox="0 0 24 24" className="w-6 h-6" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" aria-hidden="true" focusable="false">
87
- <line x1="6" y1="6" x2="18" y2="18" />
88
- <line x1="18" y1="6" x2="6" y2="18" />
89
- </svg>
90
- );
1
+ ///////////////////////////////////////////////////////////////////////////////
2
+ // Copyright (C) 2026 Jean-Philippe Steinmetz. All rights reserved.
3
+ // SPDX-License-Identifier: MPL-2.0
4
+ ///////////////////////////////////////////////////////////////////////////////
5
+ /** The in-call control icons, as inline SVG (24x24, Material Design paths, `currentColor`) so this plugin needs no
6
+ * icon package of its own. Decorative - every button that uses one carries its own `aria-label`. */
7
+ import React from "react";
8
+
9
+ function Icon({ path, className = "w-6 h-6" }: { path: string; className?: string }) {
10
+ return (
11
+ <svg viewBox="0 0 24 24" className={className} fill="currentColor" aria-hidden="true" focusable="false">
12
+ <path d={path} />
13
+ </svg>
14
+ );
15
+ }
16
+
17
+ export const MicIcon = () => (
18
+ <Icon path="M12 14c1.66 0 3-1.34 3-3V5c0-1.66-1.34-3-3-3S9 3.34 9 5v6c0 1.66 1.34 3 3 3zm5.3-3c0 3-2.54 5.1-5.3 5.1S6.7 14 6.7 11H5c0 3.41 2.72 6.23 6 6.72V21h2v-3.28c3.28-.48 6-3.3 6-6.72h-1.7z" />
19
+ );
20
+
21
+ export const MicOffIcon = () => (
22
+ <Icon path="M19 11h-1.7c0 .74-.16 1.43-.43 2.05l1.23 1.23c.56-.98.9-2.09.9-3.28zm-4.02.17c0-.06.02-.11.02-.17V5c0-1.66-1.34-3-3-3S9 3.34 9 5v.18l5.98 5.99zM4.27 3L3 4.27l6.01 6.01V11c0 1.66 1.33 3 2.99 3 .22 0 .44-.03.65-.08l1.66 1.66c-.71.33-1.5.52-2.31.52-2.76 0-5.3-2.1-5.3-5.1H5c0 3.41 2.72 6.23 6 6.72V21h2v-3.28c.91-.13 1.77-.45 2.54-.9L19.73 21 21 19.73 4.27 3z" />
23
+ );
24
+
25
+ export const VideoIcon = () => <Icon path="M17 10.5V7c0-.55-.45-1-1-1H4c-.55 0-1 .45-1 1v10c0 .55.45 1 1 1h12c.55 0 1-.45 1-1v-3.5l4 4v-11l-4 4z" />;
26
+
27
+ export const VideoOffIcon = () => (
28
+ <Icon path="M21 6.5l-4 4V7c0-.55-.45-1-1-1H9.82L21 17.18V6.5zM3.27 2L2 3.27 4.73 6H4c-.55 0-1 .45-1 1v10c0 .55.45 1 1 1h12c.21 0 .39-.08.54-.18L19.73 21 21 19.73 3.27 2z" />
29
+ );
30
+
31
+ export const ScreenShareIcon = () => (
32
+ <Icon path="M21 3H3c-1.11 0-2 .89-2 2v14c0 1.11.89 2 2 2h18c1.1 0 2-.9 2-2V5c0-1.11-.9-2-2-2zm0 16.02H3V4.98h18v14.04zM10 12H8l4-4 4 4h-2v4h-4v-4z" />
33
+ );
34
+
35
+ export const EmojiIcon = () => (
36
+ <Icon path="M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8zm3.5-9c.83 0 1.5-.67 1.5-1.5S16.33 8 15.5 8 14 8.67 14 9.5s.67 1.5 1.5 1.5zm-7 0c.83 0 1.5-.67 1.5-1.5S9.33 8 8.5 8 7 8.67 7 9.5 7.67 11 8.5 11zm3.5 6.5c2.33 0 4.31-1.46 5.11-3.5H6.89c.8 2.04 2.78 3.5 5.11 3.5z" />
37
+ );
38
+
39
+ export const GridIcon = () => <Icon path="M3 3h8v8H3V3zm10 0h8v8h-8V3zM3 13h8v8H3v-8zm10 0h8v8h-8v-8z" />;
40
+
41
+ export const FocusIcon = () => <Icon path="M3 3h18v12H3V3zm0 14h5v4H3v-4zm6.5 0h5v4h-5v-4zM16 17h5v4h-5v-4z" />;
42
+
43
+ export const LeaveIcon = () => (
44
+ <Icon path="M12 9c-1.6 0-3.15.25-4.6.72v3.1c0 .39-.23.74-.56.9-.98.49-1.87 1.12-2.66 1.85-.18.18-.43.28-.7.28-.28 0-.53-.11-.71-.29L.29 13.08c-.18-.17-.29-.42-.29-.7 0-.28.11-.53.29-.71C3.34 8.78 7.46 7 12 7s8.66 1.78 11.71 4.67c.18.18.29.43.29.71 0 .28-.11.53-.29.71l-2.48 2.48c-.18.18-.43.29-.71.29-.27 0-.52-.11-.7-.28-.79-.74-1.69-1.36-2.67-1.85-.33-.16-.56-.5-.56-.9v-3.1C15.15 9.25 13.6 9 12 9z" />
45
+ );
46
+
47
+ export const ChevronUpIcon = () => <Icon path="M12 8l-6 6 1.41 1.41L12 10.83l4.59 4.58L18 14z" className="w-5 h-5" />;
48
+
49
+ /** A raised hand, as an emoji glyph - Material's own "back hand" path isn't among the ones bundled here. */
50
+ export const HandIcon = () => (
51
+ <span className="text-xl leading-none" aria-hidden="true">
52
+ ✋
53
+ </span>
54
+ );
55
+
56
+ export const EffectsIcon = () => (
57
+ <Icon path="M19 9l1.25-2.75L23 5l-2.75-1.25L19 1l-1.25 2.75L15 5l2.75 1.25L19 9zm-7.5.5L9 4 6.5 9.5 1 12l5.5 2.5L9 20l2.5-5.5L17 12l-5.5-2.5zM19 15l-1.25 2.75L15 19l2.75 1.25L19 23l1.25-2.75L23 19l-2.75-1.25L19 15z" />
58
+ );
59
+
60
+ export const RotateIcon = () => (
61
+ <Icon path="M12 6V2L7 7l5 5V8c3.31 0 6 2.69 6 6s-2.69 6-6 6-6-2.69-6-6H4c0 4.42 3.58 8 8 8s8-3.58 8-8-3.58-8-8-8z" />
62
+ );
63
+
64
+ export const FlipIcon = () => (
65
+ <Icon path="M15 21h2v-2h-2v2zm4-12h2V7h-2v2zM3 5v14c0 1.1.9 2 2 2h4v-2H5V5h4V3H5c-1.1 0-2 .9-2 2zm16-2v2h2c0-1.1-.9-2-2-2zm-8 20h2V1h-2v22zm8-6h2v-2h-2v2zM15 5h2V3h-2v2zm4 8h2v-2h-2v2zm0 8c1.1 0 2-.9 2-2h-2v2z" />
66
+ );
67
+
68
+ /** A talking stick: a rounded shaft with a grip knob at one end, drawn (not a Material path - there isn't one for
69
+ * this) as a diagonal bar with a circle, rather than reusing `HandIcon`'s emoji-glyph fallback, since this one
70
+ * needs to light up (`currentColor`) the same way every other control icon here does. */
71
+ export const BatonIcon = () => (
72
+ <svg viewBox="0 0 24 24" className="w-6 h-6" fill="currentColor" aria-hidden="true" focusable="false">
73
+ <g transform="rotate(45 12 12)">
74
+ <rect x="5" y="10.5" width="13" height="3" rx="1.5" />
75
+ <circle cx="19" cy="12" r="3" />
76
+ </g>
77
+ </svg>
78
+ );
79
+
80
+ export const OverflowIcon = () => <Icon path="M6 10c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm12 0c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm-6 0c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z" />;
81
+
82
+ /** A plain "X" - removing a participant from the call (the participants drawer's "Remove" row button). Drawn with
83
+ * strokes rather than a filled Material path, same "simple primitives, not a recalled path" reasoning as
84
+ * `BatonIcon`. */
85
+ export const RemoveIcon = () => (
86
+ <svg viewBox="0 0 24 24" className="w-6 h-6" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" aria-hidden="true" focusable="false">
87
+ <line x1="6" y1="6" x2="18" y2="18" />
88
+ <line x1="18" y1="6" x2="6" y2="18" />
89
+ </svg>
90
+ );
91
+
92
+ /** A speech bubble - the in-call chat. Drawn from primitives, same reasoning as `BatonIcon`. */
93
+ export const ChatIcon = () => (
94
+ <svg viewBox="0 0 24 24" className="w-6 h-6" fill="none" stroke="currentColor" strokeWidth="2" strokeLinejoin="round" aria-hidden="true" focusable="false">
95
+ <path d="M4 5h16v11H9l-5 4V5z" />
96
+ <line x1="8" y1="9.5" x2="16" y2="9.5" strokeLinecap="round" />
97
+ <line x1="8" y1="12.5" x2="13" y2="12.5" strokeLinecap="round" />
98
+ </svg>
99
+ );
100
+
101
+ /** An easel board with a squiggle on it - the shared whiteboard. */
102
+ export const WhiteboardIcon = () => (
103
+ <svg viewBox="0 0 24 24" className="w-6 h-6" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true" focusable="false">
104
+ <rect x="3" y="4" width="18" height="12" rx="1" />
105
+ <path d="M7 12c1.5-3 3-3 4 0s2.5 3 4-1" />
106
+ <line x1="12" y1="16" x2="9" y2="21" />
107
+ <line x1="12" y1="16" x2="15" y2="21" />
108
+ </svg>
109
+ );
110
+
111
+ /** The letters "GIF" in a rounded box - the chat's GIF picker. */
112
+ export const GifIcon = () => (
113
+ <svg viewBox="0 0 24 24" className="w-6 h-6" aria-hidden="true" focusable="false">
114
+ <rect x="2.5" y="5.5" width="19" height="13" rx="2.5" fill="none" stroke="currentColor" strokeWidth="1.8" />
115
+ <text x="12" y="15.2" textAnchor="middle" fontSize="7.5" fontWeight="700" fontFamily="system-ui, sans-serif" fill="currentColor">
116
+ GIF
117
+ </text>
118
+ </svg>
119
+ );
120
+
121
+ export const SendIcon = () => <Icon path="M2.01 21L23 12 2.01 3 2 10l15 2-15 2z" className="w-5 h-5" />;
122
+
123
+ export const UndoIcon = () => <Icon path="M12.5 8c-2.65 0-5.05.99-6.9 2.6L2 7v9h9l-3.62-3.62c1.39-1.16 3.16-1.88 5.12-1.88 3.54 0 6.55 2.31 7.6 5.5l2.37-.78C21.08 11.03 17.15 8 12.5 8z" className="w-5 h-5" />;
124
+
125
+ /** A slanted eraser block. */
126
+ export const EraserIcon = () => (
127
+ <svg viewBox="0 0 24 24" className="w-5 h-5" fill="none" stroke="currentColor" strokeWidth="2" strokeLinejoin="round" strokeLinecap="round" aria-hidden="true" focusable="false">
128
+ <path d="M14 4l6 6-9 9H6l-3-3z" />
129
+ <line x1="9" y1="9" x2="15" y2="15" />
130
+ <line x1="11" y1="19" x2="21" y2="19" />
131
+ </svg>
132
+ );
133
+
134
+ /** A trash can - clearing the whole whiteboard. */
135
+ export const ClearIcon = () => <Icon path="M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM19 4h-3.5l-1-1h-5l-1 1H5v2h14V4z" className="w-5 h-5" />;