feat(MediaController): controls for media devices

This commit is contained in:
Slowlife01
2025-04-13 14:47:56 +07:00
parent f13537e812
commit 82b94db408
10 changed files with 236 additions and 32 deletions

View File

@@ -36,6 +36,12 @@
class="toolbarbutton-1" />
<toolbarbutton id="zen-media-mute-button"
class="toolbarbutton-1" />
<hbox id="media-device-buttons">
<toolbarbutton id="zen-media-mute-mic-button"
class="toolbarbutton-1" />
<toolbarbutton id="zen-media-mute-camera-button"
class="toolbarbutton-1" />
</hbox>
</hbox>
</vbox>
</toolbaritem>

View File

@@ -1,4 +1,4 @@
#include zen-media-player.inc.xhtml
#include zen-media-controls.inc.xhtml
<toolbar brighttext="true"
id="zen-sidebar-bottom-buttons"
fullscreentoolbar="true"

View File

@@ -19,6 +19,20 @@
--toolbarbutton-outer-padding: 2px;
}
&:not([media-sharing]) {
#media-device-buttons {
display: none;
}
}
&[media-sharing] #zen-media-controls-hbox > toolbarbutton:not(:first-child) {
display: none;
#media-device-buttons {
display: flex;
}
}
&:not([can-pip]) {
#zen-media-info-vbox {
width: calc(100% - 26px);
@@ -206,7 +220,7 @@
}
#zen-media-info-vbox {
#zen-media-controls-toolbar:not([media-position-hidden='true']) & {
#zen-media-controls-toolbar:not([media-position-hidden]) & {
transition-delay: 0.01s !important;
}
overflow-x: hidden;
@@ -251,7 +265,7 @@
align-items: center;
padding-top: 0px !important;
#zen-media-controls-toolbar[media-position-hidden='true'] & {
#zen-media-controls-toolbar[media-position-hidden] & {
display: none;
}
}

View File

@@ -27,6 +27,8 @@
_tabTimeout = null;
_controllerSwitchTimeout = null;
#isSeeking = false;
init() {
if (!Services.prefs.getBoolPref('zen.mediacontrols.enabled', true)) return;
@@ -50,6 +52,11 @@
}
#initEventListeners() {
this.mediaControlBar.addEventListener('mousedown', (event) => {
if (event.target.closest(':is(toolbarbutton,#zen-media-progress-hbox)')) return;
else this.onMediaFocus();
});
this.mediaControlBar.addEventListener('command', (event) => {
const button = event.target.closest('toolbarbutton');
if (!button) return;
@@ -75,6 +82,12 @@
case 'zen-media-playpause-button':
this.onMediaToggle();
break;
case 'zen-media-mute-mic-button':
this.onMicrophoneMuteToggle();
break;
case 'zen-media-mute-camera-button':
this.onCameraMuteToggle();
break;
}
});
@@ -123,25 +136,29 @@
});
window.addEventListener('DOMAudioPlaybackStopped', () => this.updateMuteState());
window.webrtcUI.on('peer-request-allowed', this._onMediaShareStart.bind(this));
}
onTabDiscardedOrClosed(event) {
const linkedBrowser = event.target.linkedBrowser;
if (!linkedBrowser?.browsingContext?.mediaController) return;
this.deinitMediaController(
linkedBrowser.browsingContext.mediaController,
true,
linkedBrowser.browserId === this._currentBrowser?.browserId,
true
);
const { linkedBrowser } = event.target;
if (linkedBrowser?.browserId === this._currentBrowser?.browserId) {
this.deinitMediaSharingControls(linkedBrowser);
this.hideMediaControls();
}
if (linkedBrowser?.browsingContext.mediaController) {
this.deinitMediaController(
linkedBrowser.browsingContext.mediaController,
true,
linkedBrowser.browserId === this._currentBrowser?.browserId,
true
);
}
}
async deinitMediaController(mediaController, shouldForget = true, shouldOverride = true, shouldHide = true) {
if (!mediaController) return;
const retrievedMediaController = this.mediaControllersMap.get(mediaController.id);
if (shouldForget) {
if (shouldForget && mediaController) {
mediaController.removeEventListener('pictureinpicturemodechange', this.onPipModeChange);
mediaController.removeEventListener('positionstatechange', this.onPositionstateChange);
mediaController.removeEventListener('playbackstatechange', this.onPlaybackstateChange);
@@ -167,6 +184,26 @@
}
}
get isSharing() {
return this.mediaControlBar.hasAttribute('media-sharing');
}
set isSharing(value) {
if (this._currentBrowser && !value) {
const webRTC = this._currentBrowser.browsingContext.currentWindowGlobal.getActor('WebRTC');
webRTC.sendAsyncMessage('webrtc:UnmuteMicrophone');
webRTC.sendAsyncMessage('webrtc:UnmuteCamera');
}
if (!value) {
this.mediaControlBar.removeAttribute('mic-muted');
this.mediaControlBar.removeAttribute('camera-muted');
} else {
this.mediaControlBar.setAttribute('media-position-hidden', '');
this.mediaControlBar.setAttribute('media-sharing', '');
}
}
hideMediaControls() {
if (this.mediaControlBar.hasAttribute('hidden')) return;
@@ -189,12 +226,16 @@
}
showMediaControls() {
if (!this._currentMediaController) return;
if (this._currentMediaController.isBeingUsedInPIPModeOrFullscreen) return this.hideMediaControls();
if (!this.mediaControlBar.hasAttribute('hidden')) return;
this.updatePipButton();
if (!this.isSharing) {
if (!this._currentMediaController) return;
if (this._currentMediaController.isBeingUsedInPIPModeOrFullscreen) return this.hideMediaControls();
this.updatePipButton();
this.mediaControlBar.removeAttribute('media-sharing');
}
const mediaInfoElements = [this.mediaTitle, this.mediaArtist];
for (const element of mediaInfoElements) {
element.removeAttribute('overflow'); // So we can properly recalculate the overflow
@@ -238,6 +279,7 @@
}
setupMediaControlUI(metadata, positionState) {
if (this.isSharing) return;
this.updatePipButton();
if (!this.mediaControlBar.classList.contains('playing') && this._currentMediaController.isPlaying) {
@@ -295,6 +337,70 @@
mediaController.addEventListener('deactivated', this.onDeactivated);
}
activateMediaDeviceControls(browser) {
if (browser?.browsingContext.currentWindowGlobal.hasActivePeerConnections()) {
this.mediaControlBar.removeAttribute('can-pip');
this._currentBrowser = browser;
const tab = window.gBrowser.getTabForBrowser(browser);
const iconURL = browser.mIconURL || `page-icon:${browser.currentURI.spec}`;
this.isSharing = true;
this.mediaFocusButton.style.listStyleImage = `url(${iconURL})`;
this.mediaTitle.textContent = tab.label;
this.mediaArtist.textContent = '';
this.showMediaControls();
tab.addEventListener('TabAttrModified', this._onTabAttrModified.bind(this));
}
}
deinitMediaSharingControls(browser) {
const tab = window.gBrowser.getTabForBrowser(browser);
if (tab) tab.removeEventListener('TabAttrModified', this._onTabAttrModified.bind(this));
this.isSharing = false;
this._currentBrowser = null;
}
_onTabAttrModified(event) {
const { changed } = event.detail;
const { linkedBrowser } = event.target;
if (changed.includes('sharing') && !linkedBrowser.browsingContext.currentWindowGlobal.hasActivePeerConnections()) {
if (this._currentBrowser?.browserId === linkedBrowser.browserId) {
event.target.removeEventListener('TabAttrModified', this._onTabAttrModified.bind(this));
this.deinitMediaSharingControls(linkedBrowser);
this.hideMediaControls();
this.switchController(true);
}
}
}
_onMediaShareStart(event) {
const { innerWindowID } = event;
for (const browser of window.gBrowser.browsers) {
if (browser.innerWindowID === innerWindowID) {
const webRTC = browser.browsingContext.currentWindowGlobal.getActor('WebRTC');
webRTC.sendAsyncMessage('webrtc:UnmuteMicrophone');
webRTC.sendAsyncMessage('webrtc:UnmuteCamera');
if (this._currentBrowser) this.deinitMediaSharingControls(this._currentBrowser);
if (this._currentMediaController) {
this._currentMediaController.pause();
this.deinitMediaController(this._currentMediaController, true, true).then(() =>
this.activateMediaDeviceControls(browser)
);
} else this.activateMediaDeviceControls(browser);
break;
}
}
}
_onDeactivated(event) {
this.deinitMediaController(event.target, true, event.target.id === this._currentMediaController.id, true);
this.switchController();
@@ -339,6 +445,9 @@
switchController(force = false) {
let timeout = 3000;
if (this.isSharing) return;
if (this.#isSeeking) return;
if (this._controllerSwitchTimeout) {
clearTimeout(this._controllerSwitchTimeout);
this._controllerSwitchTimeout = null;
@@ -466,6 +575,8 @@
}
onMediaSeekDrag(event) {
this.#isSeeking = true;
this._currentMediaController?.pause();
const newTime = (event.target.value / 100) * this._currentDuration;
this.mediaCurrentTime.textContent = this.formatSecondsToTime(newTime);
@@ -477,11 +588,18 @@
this._currentMediaController.seekTo(newPosition);
this._currentMediaController.play();
}
this.#isSeeking = false;
}
onMediaFocus() {
if (!this._currentBrowser) return;
this._currentMediaController?.focus();
if (this._currentMediaController) this._currentMediaController.focus();
else if (this._currentBrowser) {
const tab = window.gBrowser.getTabForBrowser(this._currentBrowser);
if (tab) window.ZenWorkspaces.switchTabIfNeeded(tab);
}
}
onMediaMute() {
@@ -503,9 +621,13 @@
}
onControllerClose() {
this._currentMediaController?.pause();
if (this._currentMediaController) {
this._currentMediaController.pause();
this.deinitMediaController(this._currentMediaController);
} else if (this.isSharing) this.deinitMediaSharingControls(this._currentBrowser);
this.hideMediaControls();
this.switchController(true);
this.deinitMediaController(this._currentMediaController);
}
onMediaPip() {
@@ -514,22 +636,37 @@
.sendAsyncMessage('PictureInPicture:KeyToggle');
}
onMicrophoneMuteToggle() {
if (this._currentBrowser) {
const shouldMute = this.mediaControlBar.hasAttribute('mic-muted') ? 'webrtc:UnmuteMicrophone' : 'webrtc:MuteMicrophone';
this._currentBrowser.browsingContext.currentWindowGlobal.getActor('WebRTC').sendAsyncMessage(shouldMute);
this.mediaControlBar.toggleAttribute('mic-muted');
}
}
onCameraMuteToggle() {
if (this._currentBrowser) {
const shouldMute = this.mediaControlBar.hasAttribute('camera-muted') ? 'webrtc:UnmuteCamera' : 'webrtc:MuteCamera';
this._currentBrowser.browsingContext.currentWindowGlobal.getActor('WebRTC').sendAsyncMessage(shouldMute);
this.mediaControlBar.toggleAttribute('camera-muted');
}
}
updateMuteState() {
if (!this._currentBrowser) return;
if (this._currentBrowser._audioMuted) {
this.mediaControlBar.setAttribute('muted', '');
} else {
this.mediaControlBar.removeAttribute('muted');
}
this.mediaControlBar.toggleAttribute('muted', this._currentBrowser._audioMuted);
}
updatePipButton() {
if (!this._currentBrowser) return;
const { totalPipCount, totalPipDisabled } = PictureInPicture.getEligiblePipVideoCount(this._currentBrowser);
if (this.isSharing) return;
if (totalPipCount === 1 || (totalPipDisabled > 0 && lazy.RESPECT_PIP_DISABLED))
this.mediaControlBar.setAttribute('can-pip', '');
else this.mediaControlBar.removeAttribute('can-pip');
const { totalPipCount, totalPipDisabled } = PictureInPicture.getEligiblePipVideoCount(this._currentBrowser);
const canPip = totalPipCount === 1 || (totalPipDisabled > 0 && lazy.RESPECT_PIP_DISABLED);
this.mediaControlBar.toggleAttribute('can-pip', canPip);
}
}

View File

@@ -1181,6 +1181,24 @@ menupopup > menuitem:is([type='checkbox']) .menu-iconic-left {
list-style-image: url('close.svg') !important;
}
#zen-media-mute-mic-button {
list-style-image: url('microphone.fill.svg') !important;
}
#zen-media-controls-toolbar[mic-muted] #zen-media-mute-mic-button {
list-style-image: url('microphone-blocked.fill.svg') !important;
fill: rgb(224, 41, 29);
}
#zen-media-mute-camera-button {
list-style-image: url('video.fill.svg') !important;
}
#zen-media-controls-toolbar[camera-muted] #zen-media-mute-camera-button {
list-style-image: url('video-blocked.fill.svg') !important;
fill: rgb(224, 41, 29);
}
#zen-media-pip-button {
list-style-image: url('chrome://global/skin/media/picture-in-picture-open.svg') !important;
}

View File

@@ -75,7 +75,9 @@
skin/classic/browser/zen-icons/media-unmute.svg (../shared/zen-icons/lin/media-unmute.svg)
skin/classic/browser/zen-icons/menu-bar.svg (../shared/zen-icons/lin/menu-bar.svg)
skin/classic/browser/zen-icons/menu.svg (../shared/zen-icons/lin/menu.svg)
skin/classic/browser/zen-icons/microphone-blocked.fill.svg (../shared/zen-icons/lin/microphone-blocked.fill.svg)
skin/classic/browser/zen-icons/microphone-blocked.svg (../shared/zen-icons/lin/microphone-blocked.svg)
skin/classic/browser/zen-icons/microphone.fill.svg (../shared/zen-icons/lin/microphone.fill.svg)
skin/classic/browser/zen-icons/microphone.svg (../shared/zen-icons/lin/microphone.svg)
skin/classic/browser/zen-icons/midi.svg (../shared/zen-icons/lin/midi.svg)
skin/classic/browser/zen-icons/move-tab.svg (../shared/zen-icons/lin/move-tab.svg)
@@ -125,8 +127,10 @@
skin/classic/browser/zen-icons/tracking-protection.svg (../shared/zen-icons/lin/tracking-protection.svg)
skin/classic/browser/zen-icons/translations.svg (../shared/zen-icons/lin/translations.svg)
skin/classic/browser/zen-icons/unpin.svg (../shared/zen-icons/lin/unpin.svg)
skin/classic/browser/zen-icons/video-blocked.fill.svg (../shared/zen-icons/lin/video-blocked.fill.svg)
skin/classic/browser/zen-icons/video-open.svg (../shared/zen-icons/lin/video-open.svg)
skin/classic/browser/zen-icons/video-save.svg (../shared/zen-icons/lin/video-save.svg)
skin/classic/browser/zen-icons/video.fill.svg (../shared/zen-icons/lin/video.fill.svg)
skin/classic/browser/zen-icons/window.svg (../shared/zen-icons/lin/window.svg)
skin/classic/browser/zen-icons/xr-blocked.svg (../shared/zen-icons/lin/xr-blocked.svg)
skin/classic/browser/zen-icons/xr.svg (../shared/zen-icons/lin/xr.svg)
@@ -209,7 +213,9 @@
skin/classic/browser/zen-icons/media-unmute.svg (../shared/zen-icons/lin/media-unmute.svg)
skin/classic/browser/zen-icons/menu-bar.svg (../shared/zen-icons/lin/menu-bar.svg)
skin/classic/browser/zen-icons/menu.svg (../shared/zen-icons/lin/menu.svg)
skin/classic/browser/zen-icons/microphone-blocked.fill.svg (../shared/zen-icons/lin/microphone-blocked.fill.svg)
skin/classic/browser/zen-icons/microphone-blocked.svg (../shared/zen-icons/lin/microphone-blocked.svg)
skin/classic/browser/zen-icons/microphone.fill.svg (../shared/zen-icons/lin/microphone.fill.svg)
skin/classic/browser/zen-icons/microphone.svg (../shared/zen-icons/lin/microphone.svg)
skin/classic/browser/zen-icons/midi.svg (../shared/zen-icons/lin/midi.svg)
skin/classic/browser/zen-icons/move-tab.svg (../shared/zen-icons/lin/move-tab.svg)
@@ -259,8 +265,10 @@
skin/classic/browser/zen-icons/tracking-protection.svg (../shared/zen-icons/lin/tracking-protection.svg)
skin/classic/browser/zen-icons/translations.svg (../shared/zen-icons/lin/translations.svg)
skin/classic/browser/zen-icons/unpin.svg (../shared/zen-icons/lin/unpin.svg)
skin/classic/browser/zen-icons/video-blocked.fill.svg (../shared/zen-icons/lin/video-blocked.fill.svg)
skin/classic/browser/zen-icons/video-open.svg (../shared/zen-icons/lin/video-open.svg)
skin/classic/browser/zen-icons/video-save.svg (../shared/zen-icons/lin/video-save.svg)
skin/classic/browser/zen-icons/video.fill.svg (../shared/zen-icons/lin/video.fill.svg)
skin/classic/browser/zen-icons/window.svg (../shared/zen-icons/lin/window.svg)
skin/classic/browser/zen-icons/xr-blocked.svg (../shared/zen-icons/lin/xr-blocked.svg)
skin/classic/browser/zen-icons/xr.svg (../shared/zen-icons/lin/xr.svg)
@@ -343,7 +351,9 @@
skin/classic/browser/zen-icons/media-unmute.svg (../shared/zen-icons/lin/media-unmute.svg)
skin/classic/browser/zen-icons/menu-bar.svg (../shared/zen-icons/lin/menu-bar.svg)
skin/classic/browser/zen-icons/menu.svg (../shared/zen-icons/lin/menu.svg)
skin/classic/browser/zen-icons/microphone-blocked.fill.svg (../shared/zen-icons/lin/microphone-blocked.fill.svg)
skin/classic/browser/zen-icons/microphone-blocked.svg (../shared/zen-icons/lin/microphone-blocked.svg)
skin/classic/browser/zen-icons/microphone.fill.svg (../shared/zen-icons/lin/microphone.fill.svg)
skin/classic/browser/zen-icons/microphone.svg (../shared/zen-icons/lin/microphone.svg)
skin/classic/browser/zen-icons/midi.svg (../shared/zen-icons/lin/midi.svg)
skin/classic/browser/zen-icons/move-tab.svg (../shared/zen-icons/lin/move-tab.svg)
@@ -393,8 +403,10 @@
skin/classic/browser/zen-icons/tracking-protection.svg (../shared/zen-icons/lin/tracking-protection.svg)
skin/classic/browser/zen-icons/translations.svg (../shared/zen-icons/lin/translations.svg)
skin/classic/browser/zen-icons/unpin.svg (../shared/zen-icons/lin/unpin.svg)
skin/classic/browser/zen-icons/video-blocked.fill.svg (../shared/zen-icons/lin/video-blocked.fill.svg)
skin/classic/browser/zen-icons/video-open.svg (../shared/zen-icons/lin/video-open.svg)
skin/classic/browser/zen-icons/video-save.svg (../shared/zen-icons/lin/video-save.svg)
skin/classic/browser/zen-icons/video.fill.svg (../shared/zen-icons/lin/video.fill.svg)
skin/classic/browser/zen-icons/window.svg (../shared/zen-icons/lin/window.svg)
skin/classic/browser/zen-icons/xr-blocked.svg (../shared/zen-icons/lin/xr-blocked.svg)
skin/classic/browser/zen-icons/xr.svg (../shared/zen-icons/lin/xr.svg)

View File

@@ -0,0 +1,7 @@
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 18.96 22.0996">
<g>
<rect height="22.0996" opacity="0" width="18.96" x="0" y="0"/>
<path d="M3.7915 8.24219L3.7915 10.166C3.7915 13.4961 5.95947 15.7031 9.29932 15.7031C10.305 15.7031 11.2053 15.503 11.97 15.1291L13.0109 16.1679C12.1527 16.654 11.1499 16.9577 10.0415 17.0508L10.0415 19.3262L13.6646 19.3262C14.0747 19.3262 14.397 19.6582 14.397 20.0684C14.397 20.4785 14.0747 20.8008 13.6646 20.8008L4.93408 20.8008C4.52393 20.8008 4.20166 20.4785 4.20166 20.0684C4.20166 19.6582 4.52393 19.3262 4.93408 19.3262L8.56689 19.3262L8.56689 17.0508C4.83643 16.7383 2.31689 14.0527 2.31689 10.2246L2.31689 8.24219C2.31689 7.83203 2.63916 7.50977 3.04932 7.50977C3.45947 7.50977 3.7915 7.83203 3.7915 8.24219ZM10.2997 13.462C9.99086 13.555 9.65583 13.6035 9.29932 13.6035C7.35596 13.6035 6.0376 12.1484 6.0376 10.0684L6.0376 9.20811ZM16.2915 8.24219L16.2915 10.2246C16.2915 11.4076 16.05 12.4814 15.6051 13.4089L14.475 12.2803C14.7003 11.6468 14.8169 10.9365 14.8169 10.166L14.8169 8.24219C14.8169 7.83203 15.1392 7.50977 15.5493 7.50977C15.9595 7.50977 16.2915 7.83203 16.2915 8.24219ZM12.561 3.53516L12.561 10.0684C12.561 10.1666 12.5581 10.2634 12.5496 10.3574L6.0376 3.8538L6.0376 3.53516C6.0376 1.44531 7.35596 0 9.29932 0C11.2524 0 12.561 1.44531 12.561 3.53516Z" fill="context-fill" fill-opacity="context-fill-opacity"/>
<path d="M16.4771 18.0176C16.77 18.3105 17.2485 18.3105 17.5415 18.0176C17.8247 17.7148 17.8345 17.2461 17.5415 16.9531L2.43408 1.85547C2.14111 1.57227 1.65283 1.5625 1.35986 1.85547C1.07666 2.14844 1.07666 2.63672 1.35986 2.91992Z" fill="context-fill" fill-opacity="context-fill-opacity"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@@ -0,0 +1,6 @@
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 14.3359 22.0996">
<g>
<rect height="22.0996" opacity="0" width="14.3359" x="0" y="0"/>
<path d="M0 10.2246C0 14.0527 2.5293 16.7383 6.25 17.0508L6.25 19.3262L2.62695 19.3262C2.2168 19.3262 1.88477 19.6582 1.88477 20.0684C1.88477 20.4785 2.2168 20.8008 2.62695 20.8008L11.3477 20.8008C11.7578 20.8008 12.0898 20.4785 12.0898 20.0684C12.0898 19.6582 11.7578 19.3262 11.3477 19.3262L7.72461 19.3262L7.72461 17.0508C11.4551 16.7383 13.9746 14.0527 13.9746 10.2246L13.9746 8.24219C13.9746 7.83203 13.6523 7.50977 13.2422 7.50977C12.832 7.50977 12.5 7.83203 12.5 8.24219L12.5 10.166C12.5 13.4961 10.332 15.7031 6.99219 15.7031C3.64258 15.7031 1.47461 13.4961 1.47461 10.166L1.47461 8.24219C1.47461 7.83203 1.15234 7.50977 0.732422 7.50977C0.322266 7.50977 0 7.83203 0 8.24219ZM6.99219 13.6035C8.93555 13.6035 10.2441 12.1484 10.2441 10.0684L10.2441 3.53516C10.2441 1.44531 8.93555 0 6.99219 0C5.03906 0 3.73047 1.44531 3.73047 3.53516L3.73047 10.0684C3.73047 12.1484 5.03906 13.6035 6.99219 13.6035Z" fill="context-fill" fill-opacity="context-fill-opacity"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -0,0 +1 @@
<svg fill="context-fill" fill-opacity="context-fill-opacity" width="800px" height="800px" viewBox="0 -64 640 640" xmlns="http://www.w3.org/2000/svg"><path d="M633.8 458.1l-55-42.5c15.4-1.4 29.2-13.7 29.2-31.1v-257c0-25.5-29.1-40.4-50.4-25.8L448 177.3v137.2l-32-24.7v-178c0-26.4-21.4-47.8-47.8-47.8H123.9L45.5 3.4C38.5-2 28.5-.8 23 6.2L3.4 31.4c-5.4 7-4.2 17 2.8 22.4L42.7 82 416 370.6l178.5 138c7 5.4 17 4.2 22.5-2.8l19.6-25.3c5.5-6.9 4.2-17-2.8-22.4zM32 400.2c0 26.4 21.4 47.8 47.8 47.8h288.4c11.2 0 21.4-4 29.6-10.5L32 154.7v245.5z"/></svg>

After

Width:  |  Height:  |  Size: 542 B

View File

@@ -0,0 +1,3 @@
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="context-fill" fill-opacity="context-fill-opacity" xmlns="http://www.w3.org/2000/svg">
<path d="M3 4C1.34315 4 0 5.34315 0 7V17C0 18.6569 1.34315 20 3 20H13C14.6569 20 16 18.6569 16 17V14.5307L20.7286 18.4249C22.0334 19.4994 24.0001 18.5713 24.0001 16.8811V7.28972C24.0001 5.54447 21.9211 4.63648 20.6408 5.8226L16 10.1222V7C16 5.34315 14.6569 4 13 4H3Z"/>
</svg>

After

Width:  |  Height:  |  Size: 422 B