@rapidmx/meet-plugin 0.13.1 → 0.14.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.
- package/README.md +55 -55
- package/apps/meet/[token].tsx +341 -341
- package/apps/meet/_CallControls.tsx +366 -394
- package/apps/meet/_CallView.tsx +987 -848
- package/apps/meet/_DiagnosticsWindow.tsx +80 -0
- package/apps/meet/_MeetLobby.tsx +224 -224
- package/apps/meet/_ParticipantTile.tsx +169 -169
- package/apps/meet/_ParticipantsDrawer.tsx +10 -104
- package/apps/meet/_SettingsModal.tsx +152 -0
- package/apps/meet/_icons.tsx +80 -82
- package/apps/meet/_meetApi.ts +212 -212
- package/apps/shared/media/filters/VideoFilterProcessor.ts +408 -408
- package/apps/shared/media/filters/mlModels.ts +174 -174
- package/apps/shared/push/GuestSignalingClient.ts +359 -359
- package/apps/shared/relay/RelayClient.ts +7 -2
- package/apps/shared/webrtc/MeshConnectionManager.ts +949 -795
- package/apps/shared/webrtc/types.ts +247 -222
- package/dist/apps/meet/_CallControls.d.ts +3 -8
- package/dist/apps/meet/_CallControls.js +3 -10
- package/dist/apps/meet/_CallView.d.ts +15 -2
- package/dist/apps/meet/_CallView.js +70 -4
- package/dist/apps/meet/_DiagnosticsWindow.d.ts +25 -0
- package/dist/apps/meet/_DiagnosticsWindow.js +45 -0
- package/dist/apps/meet/_ParticipantsDrawer.d.ts +10 -21
- package/dist/apps/meet/_ParticipantsDrawer.js +9 -39
- package/dist/apps/meet/_SettingsModal.d.ts +28 -0
- package/dist/apps/meet/_SettingsModal.js +59 -0
- package/dist/apps/meet/_icons.d.ts +1 -1
- package/dist/apps/meet/_icons.js +1 -1
- package/dist/apps/shared/relay/RelayClient.d.ts +6 -1
- package/dist/apps/shared/relay/RelayClient.js +7 -2
- package/dist/apps/shared/webrtc/MeshConnectionManager.d.ts +55 -2
- package/dist/apps/shared/webrtc/MeshConnectionManager.js +153 -9
- package/dist/apps/shared/webrtc/types.d.ts +25 -1
- package/dist/lib/util/RedisRelayBus.js +61 -5
- package/dist/lib/util/RedisRelayBus.js.map +1 -1
- package/dist/lib/util/RelayHub.js +10 -3
- package/dist/lib/util/RelayHub.js.map +1 -1
- package/dist/types/util/RedisRelayBus.d.ts +45 -6
- package/dist/types/util/RelayHub.d.ts +9 -2
- package/package.json +208 -208
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
2
|
+
// Copyright (C) 2026 Jean-Philippe Steinmetz. All rights reserved.
|
|
3
|
+
// SPDX-License-Identifier: MPL-2.0
|
|
4
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
5
|
+
/**
|
|
6
|
+
* The host-only meeting settings modal, opened from the navbar's "…" menu (`_CallView.tsx`) - host-only because
|
|
7
|
+
* `_CallView.tsx` never renders this for anyone else. Holds the settings that used to live in the participants
|
|
8
|
+
* drawer's header: "Mute new participants on join" (`forceMuteOnJoin`), the join password
|
|
9
|
+
* (`hasPassword`/`onSetPassword`), and "Require the host to admit participants" (`waitingRoomEnabled`) - each a
|
|
10
|
+
* setting for whoever joins *next*, not an action on anyone already here (muting someone already in the call is
|
|
11
|
+
* the participants drawer's own "Mute" button; the drawer also keeps the live waiting-room list, since admitting
|
|
12
|
+
* someone is closer to "who's here" than a setting).
|
|
13
|
+
*
|
|
14
|
+
* An ordinary modal, not a docked panel like the drawer or a persistent window like diagnostics: closes on Escape,
|
|
15
|
+
* a click on the backdrop, or its own close button, matching `_CallControls.tsx`'s menus and this app's general
|
|
16
|
+
* "settings is a focused, modal task" convention.
|
|
17
|
+
*/
|
|
18
|
+
import React, { useEffect, useState } from "react";
|
|
19
|
+
|
|
20
|
+
export interface SettingsModalProps {
|
|
21
|
+
forceMuteOnJoin: boolean;
|
|
22
|
+
onToggleForceMuteOnJoin: () => void;
|
|
23
|
+
/** Whether this meeting currently requires a password - never the password itself (see
|
|
24
|
+
* `PublicVideoMeeting.hasPassword`'s own doc comment). */
|
|
25
|
+
hasPassword: boolean;
|
|
26
|
+
/** Sets (a non-empty string), replaces, or removes (`null`) the join password. Rejecting lets `PasswordSection`
|
|
27
|
+
* show its own inline error without this modal needing to know anything about it. */
|
|
28
|
+
onSetPassword: (password: string | null) => Promise<void>;
|
|
29
|
+
waitingRoomEnabled: boolean;
|
|
30
|
+
onToggleWaitingRoomEnabled: () => void;
|
|
31
|
+
onClose: () => void;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** The password form: one text input plus a Set/Change button, and a Remove button once one is set. Owns its own
|
|
35
|
+
* input/error/busy state locally - the modal only needs to know whether a password exists, not what a half-typed
|
|
36
|
+
* one currently says. */
|
|
37
|
+
function PasswordSection({ hasPassword, onSetPassword }: { hasPassword: boolean; onSetPassword: (password: string | null) => Promise<void> }) {
|
|
38
|
+
const [input, setInput] = useState("");
|
|
39
|
+
const [error, setError] = useState<string | null>(null);
|
|
40
|
+
const [busy, setBusy] = useState(false);
|
|
41
|
+
|
|
42
|
+
async function submit(password: string | null) {
|
|
43
|
+
setBusy(true);
|
|
44
|
+
setError(null);
|
|
45
|
+
try {
|
|
46
|
+
await onSetPassword(password);
|
|
47
|
+
setInput("");
|
|
48
|
+
} catch {
|
|
49
|
+
setError("Could not save - try again.");
|
|
50
|
+
} finally {
|
|
51
|
+
setBusy(false);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return (
|
|
56
|
+
<div className="py-3 border-b border-white/10 text-sm">
|
|
57
|
+
<p className="text-white/70 mb-1.5">{hasPassword ? "Password protection is on." : "No password required to join."}</p>
|
|
58
|
+
<form
|
|
59
|
+
className="flex gap-2"
|
|
60
|
+
onSubmit={(event) => {
|
|
61
|
+
event.preventDefault();
|
|
62
|
+
if (input) {
|
|
63
|
+
void submit(input);
|
|
64
|
+
}
|
|
65
|
+
}}
|
|
66
|
+
>
|
|
67
|
+
<input
|
|
68
|
+
type="password"
|
|
69
|
+
aria-label={hasPassword ? "New password" : "Set a password"}
|
|
70
|
+
placeholder={hasPassword ? "New password" : "Set a password"}
|
|
71
|
+
className="flex-1 min-w-0 px-2 py-1 rounded bg-white/10 text-white placeholder:text-white/50 focus:outline-none focus-visible:ring-2 focus-visible:ring-white/80"
|
|
72
|
+
value={input}
|
|
73
|
+
onChange={(event) => setInput(event.target.value)}
|
|
74
|
+
disabled={busy}
|
|
75
|
+
/>
|
|
76
|
+
<button
|
|
77
|
+
type="submit"
|
|
78
|
+
className="px-2 py-1 rounded text-xs whitespace-nowrap bg-white/10 hover:bg-white/20 focus:outline-none focus-visible:ring-2 focus-visible:ring-white/80 disabled:opacity-50"
|
|
79
|
+
disabled={busy || !input}
|
|
80
|
+
>
|
|
81
|
+
{hasPassword ? "Change" : "Set"}
|
|
82
|
+
</button>
|
|
83
|
+
</form>
|
|
84
|
+
{hasPassword && (
|
|
85
|
+
<button
|
|
86
|
+
type="button"
|
|
87
|
+
className="mt-1.5 text-xs text-[#f2b8b5] underline focus:outline-none focus-visible:ring-2 focus-visible:ring-white/80 disabled:opacity-50"
|
|
88
|
+
disabled={busy}
|
|
89
|
+
onClick={() => void submit(null)}
|
|
90
|
+
>
|
|
91
|
+
Remove password
|
|
92
|
+
</button>
|
|
93
|
+
)}
|
|
94
|
+
{error && <p className="text-[#f2b8b5] text-xs mt-1.5">{error}</p>}
|
|
95
|
+
</div>
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export default function SettingsModal({
|
|
100
|
+
forceMuteOnJoin,
|
|
101
|
+
onToggleForceMuteOnJoin,
|
|
102
|
+
hasPassword,
|
|
103
|
+
onSetPassword,
|
|
104
|
+
waitingRoomEnabled,
|
|
105
|
+
onToggleWaitingRoomEnabled,
|
|
106
|
+
onClose,
|
|
107
|
+
}: SettingsModalProps) {
|
|
108
|
+
useEffect(() => {
|
|
109
|
+
const onKeyDown = (event: KeyboardEvent) => {
|
|
110
|
+
if (event.key === "Escape") {
|
|
111
|
+
onClose();
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
document.addEventListener("keydown", onKeyDown);
|
|
115
|
+
return () => document.removeEventListener("keydown", onKeyDown);
|
|
116
|
+
}, [onClose]);
|
|
117
|
+
|
|
118
|
+
return (
|
|
119
|
+
<>
|
|
120
|
+
<div className="fixed inset-0 z-30 bg-black/40" onClick={onClose} aria-hidden="true" />
|
|
121
|
+
<div
|
|
122
|
+
role="dialog"
|
|
123
|
+
aria-label="Meeting settings"
|
|
124
|
+
className="fixed z-30 top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-80 max-w-[calc(100vw-1.5rem)] max-h-[80vh] overflow-y-auto flex flex-col rounded-2xl bg-[#2b2d30] text-white shadow-xl"
|
|
125
|
+
onClick={(event) => event.stopPropagation()}
|
|
126
|
+
>
|
|
127
|
+
<div className="flex items-center justify-between gap-2 px-4 py-3 border-b border-white/10">
|
|
128
|
+
<h2 className="text-sm font-medium">Meeting settings</h2>
|
|
129
|
+
<button
|
|
130
|
+
type="button"
|
|
131
|
+
className="w-8 h-8 flex items-center justify-center rounded-full hover:bg-white/10 focus:outline-none focus-visible:ring-2 focus-visible:ring-white/80"
|
|
132
|
+
aria-label="Close meeting settings"
|
|
133
|
+
onClick={onClose}
|
|
134
|
+
>
|
|
135
|
+
✕
|
|
136
|
+
</button>
|
|
137
|
+
</div>
|
|
138
|
+
<div className="px-4">
|
|
139
|
+
<label className="flex items-center gap-2 py-3 border-b border-white/10 text-sm">
|
|
140
|
+
<input type="checkbox" checked={forceMuteOnJoin} onChange={onToggleForceMuteOnJoin} className="w-4 h-4" />
|
|
141
|
+
Mute new participants on join
|
|
142
|
+
</label>
|
|
143
|
+
<PasswordSection hasPassword={hasPassword} onSetPassword={onSetPassword} />
|
|
144
|
+
<label className="flex items-center gap-2 py-3 text-sm">
|
|
145
|
+
<input type="checkbox" checked={waitingRoomEnabled} onChange={onToggleWaitingRoomEnabled} className="w-4 h-4" />
|
|
146
|
+
Require the host to admit participants
|
|
147
|
+
</label>
|
|
148
|
+
</div>
|
|
149
|
+
</div>
|
|
150
|
+
</>
|
|
151
|
+
);
|
|
152
|
+
}
|
package/apps/meet/_icons.tsx
CHANGED
|
@@ -1,82 +1,80 @@
|
|
|
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
|
-
|
|
69
|
-
|
|
70
|
-
)
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
</svg>
|
|
82
|
-
);
|
|
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" />;
|