diff --git a/src/browser/base/content/ZenUIManager.mjs b/src/browser/base/content/ZenUIManager.mjs
index 1f84425ae..185fe8cc3 100644
--- a/src/browser/base/content/ZenUIManager.mjs
+++ b/src/browser/base/content/ZenUIManager.mjs
@@ -770,252 +770,3 @@ var gZenVerticalTabsManager = {
this._tabEdited = null;
},
};
-
-var gZenMediaController = {
- _currentMediaController: null,
- _currentBrowser: null,
- _mediaUpdateInterval: null,
-
- mediaTitle: null,
- mediaArtist: null,
- mediaControlBar: null,
- mediaServiceIcon: null,
- mediaServiceTitle: null,
- mediaProgressBar: null,
- mediaCurrentTime: null,
- mediaDuration: null,
- mediaFocusButton: null,
-
- supportedKeys: ['playpause', 'previoustrack', 'nexttrack'],
-
- init() {
- this.mediaTitle = document.querySelector('#zen-media-title');
- this.mediaArtist = document.querySelector('#zen-media-artist');
- this.mediaControlBar = document.querySelector('#zen-media-controls-toolbar');
- this.mediaServiceIcon = document.querySelector('#zen-media-service-button > image');
- this.mediaServiceTitle = document.querySelector('#zen-media-service-title');
- this.mediaProgressBar = document.querySelector('#zen-media-progress-bar');
- this.mediaCurrentTime = document.querySelector('#zen-media-current-time');
- this.mediaDuration = document.querySelector('#zen-media-duration');
- this.mediaFocusButton = document.querySelector('#zen-media-focus-button');
- },
-
- /**
- * Deinitializes a media controller, removing all event listeners and resetting state.
- * @param {Object} mediaController - The media controller to deinitialize.
- */
- deinitMediaController(mediaController) {
- if (!mediaController) return;
-
- mediaController.onpositionstatechange = null;
- mediaController.onplaybackstatechange = null;
- mediaController.onsupportedkeyschange = null;
- mediaController.onmetadatachange = null;
- mediaController.ondeactivated = null;
-
- this._currentMediaController = null;
- this._currentBrowser = null;
-
- if (this._mediaUpdateInterval) {
- clearInterval(this._mediaUpdateInterval);
- this._mediaUpdateInterval = null;
- }
-
- this.mediaControlBar.setAttribute('hidden', 'true');
- this.mediaControlBar.removeAttribute('muted');
- this.mediaControlBar.classList.remove('playing');
- },
-
- /**
- * Sets up the media control UI with metadata and position state.
- * @param {Object} metadata - The media metadata (title, artist, etc.).
- * @param {Object} positionState - The position state (position, duration).
- */
- setupMediaControl(metadata, positionState) {
- if (!this.mediaControlBar.classList.contains('playing')) {
- this.mediaControlBar.classList.add('playing');
- }
-
- this.mediaServiceTitle.textContent = this._currentBrowser._originalURI.displayHost;
- this.mediaServiceIcon.src = this._currentBrowser.mIconURL;
- this.mediaFocusButton.style.listStyleImage = `url(${this._currentBrowser.mIconURL})`;
-
- this.mediaControlBar.removeAttribute('hidden');
- this.mediaTitle.textContent = metadata.title || '';
- this.mediaArtist.textContent = metadata.artist || '';
-
- this._currentPosition = positionState.position;
- this._currentDuration = positionState.duration;
- this.updateMediaPosition();
-
- for (const key of this.supportedKeys) {
- const button = this.mediaControlBar.querySelector(`#zen-media-${key}-button`);
- button.disabled = !this._currentMediaController.supportedKeys.includes(key);
- }
- },
-
- /**
- * @param {Object} mediaController - The media controller to activate.
- * @param {Object} browser - The browser associated with the media controller.
- */
- activateMediaControls(mediaController, browser) {
- if (this._currentBrowser?.browserId === browser.browserId) return;
- else this._currentBrowser = browser;
-
- mediaController.onpositionstatechange = this.onPositionstateChange.bind(this);
- mediaController.onplaybackstatechange = this.onPlaybackstateChange.bind(this);
- mediaController.onsupportedkeyschange = this.onSupportedKeysChange.bind(this);
- mediaController.onmetadatachange = this.onMetadataChange.bind(this);
- mediaController.ondeactivated = this.onDeactivated.bind(this);
-
- if (this._currentMediaController === mediaController) return;
- else this.deinitMediaController(this._currentMediaController);
-
- this._currentMediaController = mediaController;
-
- const metadata = mediaController.getMetadata();
- const positionState = mediaController.getPositionState();
-
- this.setupMediaControl(metadata, positionState);
- },
-
- /**
- * @param {Event} event - The deactivation event.
- */
- onDeactivated(event) {
- if (event.target === this._currentMediaController) {
- this.deinitMediaController(event.target);
- }
- },
-
- /**
- * Updates playback state and UI based on changes.
- * @param {Event} event - The playback state change event.
- */
- onPlaybackstateChange(event) {
- this.mediaControlBar.classList.toggle('playing', event.target.isPlaying);
- },
-
- /**
- * Updates supported keys in the UI.
- * @param {Event} event - The supported keys change event.
- */
- onSupportedKeysChange(event) {
- for (const key of this.supportedKeys) {
- const button = this.mediaControlBar.querySelector(`#zen-media-${key}-button`);
- button.disabled = !event.target.supportedKeys.includes(key);
- }
- },
-
- /**
- * Updates position state and UI when the media position changes.
- * @param {Event} event - The position state change event.
- */
- onPositionstateChange(event) {
- if (event.target !== this._currentMediaController) return;
-
- this._currentPosition = event.position;
- this._currentDuration = event.duration;
- this.updateMediaPosition();
- },
-
- /**
- * Updates the media progress bar and time display.
- */
- updateMediaPosition() {
- if (this._mediaUpdateInterval) {
- clearInterval(this._mediaUpdateInterval);
- this._mediaUpdateInterval = null;
- }
-
- this.mediaCurrentTime.textContent = this.formatSecondsToMinutes(this._currentPosition);
- this.mediaDuration.textContent = this.formatSecondsToMinutes(this._currentDuration);
- this.mediaProgressBar.value = (this._currentPosition / this._currentDuration) * 100;
-
- if (this._currentMediaController?.isPlaying) {
- this._mediaUpdateInterval = setInterval(() => {
- if (this._currentMediaController?.isPlaying) {
- this._currentPosition += 1;
- if (this._currentPosition > this._currentDuration) {
- this._currentPosition = this._currentDuration;
- }
- this.mediaCurrentTime.textContent = this.formatSecondsToMinutes(this._currentPosition);
- this.mediaProgressBar.value = (this._currentPosition / this._currentDuration) * 100;
- } else {
- clearInterval(this._mediaUpdateInterval);
- this._mediaUpdateInterval = null;
- }
- }, 1000);
- }
- },
-
- /**
- * Formats seconds into a minutes:seconds string.
- * @param {number} seconds - The time in seconds.
- * @returns {string} Formatted time string.
- */
- formatSecondsToMinutes(seconds) {
- const totalSeconds = Math.ceil(seconds);
- const minutes = Math.floor(totalSeconds / 60);
- const remainingSeconds = totalSeconds % 60;
- return `${minutes}:${remainingSeconds.toString().padStart(2, '0')}`;
- },
-
- /**
- * Updates metadata in the UI.
- * @param {Event} event - The metadata change event.
- */
- onMetadataChange(event) {
- const metadata = event.target.getMetadata();
- this.mediaTitle.textContent = metadata.title || '';
- this.mediaArtist.textContent = metadata.artist || '';
- },
-
- onMediaPlayPrev() {
- if (this._currentMediaController?.supportedKeys.includes('previoustrack')) {
- this._currentMediaController.prevTrack();
- }
- },
-
- onMediaPlayNext() {
- if (this._currentMediaController?.supportedKeys.includes('nexttrack')) {
- this._currentMediaController.nextTrack();
- }
- },
-
- onMediaSeekDrag(event) {
- this._currentMediaController?.pause();
- const newTime = (event.target.value / 100) * this._currentDuration;
- this.mediaCurrentTime.textContent = this.formatSecondsToMinutes(newTime);
- },
-
- onMediaSeekComplete(event) {
- const newPosition = (event.target.value / 100) * this._currentDuration;
- if (this._currentMediaController?.supportedKeys.includes('seekto')) {
- this._currentMediaController.seekTo(newPosition);
- this._currentMediaController.play();
- }
- },
-
- onMediaFocus() {
- this._currentMediaController?.focus();
- },
-
- onMediaMute() {
- if (!this.mediaControlBar.hasAttribute('muted')) {
- this._currentBrowser.mute();
- this.mediaControlBar.setAttribute('muted', '');
- } else {
- this._currentBrowser.unmute();
- this.mediaControlBar.removeAttribute('muted');
- }
- },
-
- onMediaToggle() {
- if (this.mediaControlBar.classList.contains('playing')) {
- this._currentMediaController?.pause();
- } else {
- this._currentMediaController?.play();
- }
- },
-};
diff --git a/src/browser/base/content/navigator-toolbox-inc-xhtml.patch b/src/browser/base/content/navigator-toolbox-inc-xhtml.patch
index b2f5e92ae..104f2a9d1 100644
--- a/src/browser/base/content/navigator-toolbox-inc-xhtml.patch
+++ b/src/browser/base/content/navigator-toolbox-inc-xhtml.patch
@@ -1,5 +1,5 @@
diff --git a/browser/base/content/navigator-toolbox.inc.xhtml b/browser/base/content/navigator-toolbox.inc.xhtml
-index 2a57e254c876589bf64bfbd5df188a2b435d8482..c4aecd9738baed13b8dd5687edc95695b0d5af37 100644
+index 2a57e254c876589bf64bfbd5df188a2b435d8482..fcb6958cd1379bb5f2a7f63d5c7d8145726001cd 100644
--- a/browser/base/content/navigator-toolbox.inc.xhtml
+++ b/browser/base/content/navigator-toolbox.inc.xhtml
@@ -2,7 +2,7 @@
@@ -73,7 +73,7 @@ index 2a57e254c876589bf64bfbd5df188a2b435d8482..c4aecd9738baed13b8dd5687edc95695
@@ -82,7 +82,6 @@ index 2a57e254c876589bf64bfbd5df188a2b435d8482..c4aecd9738baed13b8dd5687edc95695
#include titlebar-items.inc.xhtml
-
+#endif
-+#include zen-media-controller.inc.xhtml
+#include zen-sidebar-icons.inc.xhtml
-
@@ -90,7 +89,7 @@ index 2a57e254c876589bf64bfbd5df188a2b435d8482..c4aecd9738baed13b8dd5687edc95695
diff --git a/src/browser/base/content/zen-assets.inc.xhtml b/src/browser/base/content/zen-assets.inc.xhtml
index c5f6d0b17..9895f4093 100644
--- a/src/browser/base/content/zen-assets.inc.xhtml
+++ b/src/browser/base/content/zen-assets.inc.xhtml
@@ -43,6 +43,7 @@
+
# Unimportant scripts
diff --git a/src/browser/base/content/zen-assets.jar.inc.mn b/src/browser/base/content/zen-assets.jar.inc.mn
index e024f82b9..e13214714 100644
--- a/src/browser/base/content/zen-assets.jar.inc.mn
+++ b/src/browser/base/content/zen-assets.jar.inc.mn
@@ -25,6 +25,7 @@
content/browser/zen-components/ZenRices.mjs (zen-components/ZenRices.mjs)
content/browser/zen-components/ZenEmojies.mjs (zen-components/ZenEmojies.mjs)
content/browser/zen-components/ZenWelcome.mjs (zen-components/ZenWelcome.mjs)
+ content/browser/zen-components/ZenMediaController.mjs (zen-components/ZenMediaController.mjs)
content/browser/zen-styles/zen-theme.css (content/zen-styles/zen-theme.css)
content/browser/zen-styles/zen-buttons.css (content/zen-styles/zen-buttons.css)
diff --git a/src/browser/base/content/zen-media-controller.inc.xhtml b/src/browser/base/content/zen-media-controller.inc.xhtml
deleted file mode 100644
index 488aa44d4..000000000
--- a/src/browser/base/content/zen-media-controller.inc.xhtml
+++ /dev/null
@@ -1,48 +0,0 @@
-
\ No newline at end of file
diff --git a/src/browser/base/content/zen-sidebar-icons.inc.xhtml b/src/browser/base/content/zen-sidebar-icons.inc.xhtml
index a85aaf520..cc06d5e20 100644
--- a/src/browser/base/content/zen-sidebar-icons.inc.xhtml
+++ b/src/browser/base/content/zen-sidebar-icons.inc.xhtml
@@ -1,3 +1,52 @@
+
+