Redesign chrome://media-internals with a modern look and feel

The new design features:
- A two-pane layout for players & information
- A dark theme when chrome://settings/appearance is set to dark
- Redesigned property and log views.

I gemini'd most of this, and then tweaked a few CSS bits.

Mobile screenshots:
https://files.tedm.io/media-internals/1.jpg
https://files.tedm.io/media-internals/2.jpg
https://files.tedm.io/media-internals/3.jpg
https://files.tedm.io/media-internals/4.jpg

Desktop screenshots:
https://files.tedm.io/media-internals/5.png
https://files.tedm.io/media-internals/6.png
https://files.tedm.io/media-internals/7.png

I've uploaded a video of the new UX: https://youtu.be/Xl7R085gtPU

Change-Id: Iedd7f1ac62d6922db50ad3f3ec11ee912312fe60
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6861607
Reviewed-by: Dale Curtis <dalecurtis@chromium.org>
Commit-Queue: Ted (Chromium) Meyer <tmathmeyer@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1508005}
diff --git a/content/browser/resources/media/client_renderer.js b/content/browser/resources/media/client_renderer.js
index 299d620..837057c 100644
--- a/content/browser/resources/media/client_renderer.js
+++ b/content/browser/resources/media/client_renderer.js
@@ -116,19 +116,9 @@
       this.filterText.onkeyup = this.onTextChange_.bind(this);
     }
 
-    const copyPropertiesButtons =
-        document.getElementsByClassName('copy-properties-button');
-    if (copyPropertiesButtons) {
-      for (let i = 0; i < copyPropertiesButtons.length; i++) {
-        copyPropertiesButtons[i].onclick = this.copyProperties_.bind(this);
-      }
-    }
-
-    const copyLogButtons = document.getElementsByClassName('copy-log-button');
-    if (copyLogButtons) {
-      for (let i = 0; i < copyLogButtons.length; i++) {
-        copyLogButtons[i].onclick = this.copyLog_.bind(this);
-      }
+    this.copyLogButton = $('copy-log-button');
+    if (this.copyLogButton) {
+      this.copyLogButton.onclick = this.copyLog_.bind(this);
     }
 
     this.saveLogButton = $('save-log-button');
@@ -136,11 +126,30 @@
       this.saveLogButton.onclick = this.saveLog_.bind(this);
     }
 
+    this.closePlayerViewButton = $('close-player-view-button');
+    if (this.closePlayerViewButton) {
+      this.closePlayerViewButton.onclick = () => {
+        $('main-container').classList.remove('mobile-player-view-active');
+        document.body.classList.add(ClientRendererCss.NO_PLAYERS_SELECTED);
+        if (this.selectedPlayer) {
+          const element = this.playerListElement.querySelector(
+              `.tree-item[data-id="${this.selectedPlayer.id}"]`);
+          if (element) {
+            element.classList.remove('selected');
+          }
+          this.selectedPlayer = null;
+          const titleElement = $('player-details-title');
+          if (titleElement) {
+            titleElement.textContent = 'Player Properties';
+            titleElement.title = '';
+          }
+        }
+      };
+    }
+
     this.hiddenKeys = ['component_id', 'component_type', 'owner_id'];
 
-    // Tell CSS to hide certain content prior to making selections.
     document.body.classList.add(ClientRendererCss.NO_PLAYERS_SELECTED);
-    document.body.classList.add(ClientRendererCss.NO_COMPONENTS_SELECTED);
   }
 
   /**
@@ -229,6 +238,12 @@
       removeChildren(this.logTable);
       removeChildren(this.graphElement);
       document.body.classList.add(ClientRendererCss.NO_PLAYERS_SELECTED);
+      this.selectedPlayer = null;
+      const titleElement = $('player-details-title');
+      if (titleElement) {
+        titleElement.textContent = 'Player Properties';
+        titleElement.title = '';
+      }
     }
     this.redrawPlayerList_(players);
   }
@@ -365,10 +380,6 @@
   }
 
   redrawAudioComponentList_(componentType, components) {
-    // Group name imposes rule that only one component can be selected
-    // (and have its properties displayed) at a time.
-    const buttonGroupName = 'audio-components';
-
     const listElement = this.getListElementForAudioComponent_(componentType);
     if (!listElement) {
       console.error(
@@ -378,15 +389,29 @@
 
     const fragment = document.createDocumentFragment();
     for (const id in components) {
-      const li = document.createElement('li');
-      const buttonCb = this.selectAudioComponent_.bind(
-          this, componentType, id, components[id]);
-      const friendlyName = this.getAudioComponentName_(componentType, id);
-      const label = document.createElement('label');
-      label.appendChild(document.createTextNode(friendlyName));
-      li.appendChild(
-          createSelectableButton(id, buttonGroupName, label, buttonCb));
-      fragment.appendChild(li);
+      const component = components[id];
+
+      const treeItem = document.createElement('div');
+      treeItem.classList.add('tree-item');
+      treeItem.dataset.id = id;
+      treeItem.classList.add(ClientRendererCss.ACTIVE_PLAYER);
+
+      const treeItemHeader = document.createElement('div');
+      treeItemHeader.classList.add('tree-item-header');
+      treeItemHeader.textContent =
+          this.getAudioComponentName_(componentType, id);
+      treeItem.appendChild(treeItemHeader);
+
+      const children = document.createElement('div');
+      children.classList.add('tree-item-children');
+      treeItem.appendChild(children);
+
+      treeItemHeader.addEventListener('click', (e) => {
+        treeItem.classList.toggle('expanded');
+        this.selectAudioComponent_(componentType, id, component);
+      });
+
+      fragment.appendChild(treeItem);
     }
     removeChildren(listElement);
     listElement.appendChild(fragment);
@@ -395,12 +420,32 @@
         this.selectedAudioComponentType === componentType &&
         this.selectedAudioComponentId in components) {
       // Re-select the selected component since the button was just recreated.
-      selectSelectableButton(this.selectedAudioComponentId);
+      const element = listElement.querySelector(
+          `.tree-item[data-id="${this.selectedAudioComponentId}"]`);
+      if (element) {
+        element.classList.add('selected');
+      }
     }
   }
 
   selectAudioComponent_(componentType, componentId, componentData) {
-    document.body.classList.remove(ClientRendererCss.NO_COMPONENTS_SELECTED);
+    const audioWrapper = $('audio-component-list-wrapper');
+    if (audioWrapper) {
+      const previouslySelected =
+          audioWrapper.querySelector('.tree-item.selected');
+      if (previouslySelected) {
+        previouslySelected.classList.remove('selected');
+      }
+    }
+
+    const listElement = this.getListElementForAudioComponent_(componentType);
+    if (listElement) {
+      const element =
+          listElement.querySelector(`.tree-item[data-id="${componentId}"]`);
+      if (element) {
+        element.classList.add('selected');
+      }
+    }
 
     this.selectedAudioComponentType = componentType;
     this.selectedAudioComponentId = componentId;
@@ -415,83 +460,96 @@
   redrawPlayerList_(players) {
     this.players = players;
 
-    // Group name imposes rule that only one component can be selected
-    // (and have its properties displayed) at a time.
-    const buttonGroupName = 'player-buttons';
-
-    let hasPlayers = false;
     const fragment = document.createDocumentFragment();
     for (const id in players) {
-      hasPlayers = true;
       const player = players[id];
       const p = player.properties;
-      const label = document.createElement('label');
 
-      const nameText = p.url || 'Player ' + player.id;
-      const nameNode = document.createElement('div');
-      nameNode.appendChild(document.createTextNode(nameText));
-      nameNode.className = 'player-name';
-      label.appendChild(nameNode);
+      const treeItem = document.createElement('div');
+      treeItem.classList.add('tree-item');
+      if (player.playerState === 'errored') {
+        treeItem.classList.add(ClientRendererCss.ERRORED_PLAYER);
+      } else if (player.playerState === 'ended') {
+        treeItem.classList.add(ClientRendererCss.ENDED_PLAYER);
+      } else {
+        treeItem.classList.add(ClientRendererCss.ACTIVE_PLAYER);
+      }
+      treeItem.dataset.id = id;
 
-      const frame = [];
-      if (p.frame_title) {
-        frame.push(p.frame_title);
+      const treeItemHeader = document.createElement('div');
+      treeItemHeader.classList.add('tree-item-header');
+      treeItemHeader.classList.add('selectable-button');
+
+      const playerName = document.createElement('div');
+      playerName.classList.add('player-name');
+      const url = p.url || 'Player ' + player.id;
+      if (url.length > 64) {
+        playerName.textContent = url.substring(0, 61) + '...';
+      } else {
+        playerName.textContent = url;
       }
-      if (p.frame_url) {
-        frame.push(p.frame_url);
-      }
-      const frameText = frame.join(' - ');
-      if (frameText) {
-        const frameNode = document.createElement('div');
-        frameNode.className = 'player-frame';
-        frameNode.appendChild(document.createTextNode(frameText));
-        label.appendChild(frameNode);
+      playerName.title = url;
+      treeItemHeader.appendChild(playerName);
+
+      let lastEvent = '';
+      for (let i = player.allEvents.length - 1; i >= 0; i--) {
+        if (player.allEvents[i].key === 'event') {
+          lastEvent = player.allEvents[i].value;
+          break;
+        }
       }
 
-      const desc = [];
-      if (p.width && p.height) {
-        desc.push(p.width + 'x' + p.height);
+      if (lastEvent) {
+        const playerFrame = document.createElement('div');
+        playerFrame.classList.add('player-frame');
+        playerFrame.textContent = lastEvent;
+        treeItemHeader.appendChild(playerFrame);
       }
-      if (p.video_codec_name) {
-        desc.push(p.video_codec_name);
-      }
-      if (p.video_codec_name && p.audio_codec_name) {
-        desc.push('+');
-      }
-      if (p.audio_codec_name) {
-        desc.push(p.audio_codec_name);
-      }
-      if (p.event) {
-        desc.push('(' + p.event + ')');
-      }
-      const descText = desc.join(' ');
-      if (descText) {
-        const descNode = document.createElement('div');
-        descNode.className = 'player-desc';
-        descNode.appendChild(document.createTextNode(descText));
-        label.appendChild(descNode);
-      }
+      treeItem.appendChild(treeItemHeader);
 
-      const li = document.createElement('li');
-      const buttonCb = this.selectPlayer_.bind(this, player);
-      li.appendChild(createSelectableButton(
-          id, buttonGroupName, label, buttonCb, player.playerState));
-      fragment.appendChild(li);
+      const children = document.createElement('div');
+      children.classList.add('tree-item-children');
+      treeItem.appendChild(children);
+
+      treeItemHeader.addEventListener('click', (e) => {
+        treeItem.classList.toggle('expanded');
+        this.selectPlayer_(player);
+      });
+
+      fragment.appendChild(treeItem);
     }
     removeChildren(this.playerListElement);
     this.playerListElement.appendChild(fragment);
 
     if (this.selectedPlayer && this.selectedPlayer.id in players) {
       // Re-select the selected player since the button was just recreated.
-      selectSelectableButton(this.selectedPlayer.id);
+      const element = this.playerListElement.querySelector(
+          `.tree-item[data-id="${this.selectedPlayer.id}"]`);
+      if (element) {
+        element.classList.add('selected');
+      }
     }
-
-    this.saveLogButton.style.display = hasPlayers ? 'inline-block' : 'none';
   }
 
   selectPlayer_(player) {
+    if (window.innerWidth <= 768) {
+      $('main-container').classList.add('mobile-player-view-active');
+    }
+
     document.body.classList.remove(ClientRendererCss.NO_PLAYERS_SELECTED);
 
+    const previouslySelected =
+        this.playerListElement.querySelector('.tree-item.selected');
+    if (previouslySelected) {
+      previouslySelected.classList.remove('selected');
+    }
+
+    const element = this.playerListElement.querySelector(
+        `.tree-item[data-id="${player.id}"]`);
+    if (element) {
+      element.classList.add('selected');
+    }
+
     this.selectedPlayer = player;
     this.selectedPlayerLogIndex = 0;
     this.selectedAudioComponentType = null;
@@ -502,6 +560,13 @@
     removeChildren(this.logTable);
     removeChildren(this.graphElement);
     this.drawLog_();
+
+    const titleElement = $('player-details-title');
+    if (titleElement) {
+      const playerName = player.properties.url || 'Player ' + player.id;
+      titleElement.textContent = playerName;
+      titleElement.title = playerName;
+    }
   }
 
   drawProperties_(propertyMap, propertiesTable) {
@@ -519,21 +584,52 @@
       const valueCell = row.insertCell(-1);
 
       keyCell.appendChild(document.createTextNode(key));
-      valueCell.appendChild(document.createTextNode(JSON.stringify(value)));
+
+      try {
+        if (key === 'kHlsBufferedRanges') {
+          throw new Error('Do Not Render As JSON');
+        }
+        const pre = document.createElement('pre');
+        pre.textContent = JSON.stringify(value, null, 2);
+        valueCell.appendChild(pre);
+      } catch (e) {
+        valueCell.appendChild(document.createTextNode(JSON.stringify(value)));
+      }
     }
   }
 
   appendEventToLog_(event) {
     if (this.filterFunction(event.key)) {
       const row = this.logTable.insertRow(-1);
+      row.classList.add('log-entry');
 
       const timestampCell = row.insertCell(-1);
-      timestampCell.classList.add('timestamp');
-      timestampCell.appendChild(
-          document.createTextNode(millisecondsToString(event.time)));
-      row.insertCell(-1).appendChild(document.createTextNode(event.key));
-      row.insertCell(-1).appendChild(
-          document.createTextNode(JSON.stringify(event.value)));
+      timestampCell.classList.add('log-timestamp');
+      timestampCell.textContent = millisecondsToString(event.time);
+
+      const propertyCell = row.insertCell(-1);
+      propertyCell.classList.add('log-property');
+      propertyCell.textContent = event.key;
+
+      const valueCell = row.insertCell(-1);
+      valueCell.classList.add('log-value');
+      try {
+        if (event.key === 'kHlsBufferedRanges') {
+          throw new Error('Do Not Render As JSON');
+        }
+        const pre = document.createElement('pre');
+        pre.textContent = JSON.stringify(event.value, null, 2);
+        valueCell.appendChild(pre);
+      } catch (e) {
+        valueCell.appendChild(
+            document.createTextNode(JSON.stringify(event.value)));
+      }
+
+      if (event.key.toLowerCase().includes('error')) {
+        row.classList.add('log-error');
+      } else if (event.key.toLowerCase().includes('warning')) {
+        row.classList.add('log-warning');
+      }
     }
   }
 
@@ -570,25 +666,6 @@
     navigator.clipboard.writeText(string);
   }
 
-  copyProperties_() {
-    if (!this.selectedPlayer && !this.selectedAudioCompontentData) {
-      return;
-    }
-    const properties =
-        this.selectedAudioCompontentData || this.selectedPlayer.properties;
-    const stringBuffer = [];
-
-    for (const key in properties) {
-      const value = properties[key];
-      stringBuffer.push(key.toString());
-      stringBuffer.push(': ');
-      stringBuffer.push(value.toString());
-      stringBuffer.push('\n');
-    }
-
-    this.renderClipboard(stringBuffer.join(''));
-  }
-
   onTextChange_(event) {
     const text = this.filterText.value.toLowerCase();
     const parts = text.split(',')
@@ -624,14 +701,33 @@
 
   createCdmRow_(cdm) {
     const template = $('cdm-row');
-    const span = template.content.querySelectorAll('span');
-    span[0].textContent = 'Key System: ' + cdm.key_system;
-    span[1].textContent = 'Robustness: ' + cdm.robustness;
-    span[2].textContent = 'Name: ' + cdm.name;
-    span[3].textContent = 'Version: ' + cdm.version;
-    span[4].textContent = 'Path: ' + cdm.path;
-    span[5].textContent = 'Status: ' + cdm.status;
-    span[6].textContent = 'Capabilities: ' + JSON.stringify(cdm.capability);
-    return document.importNode(template.content, true);
+    const clone = document.importNode(template.content, true);
+    const header = clone.querySelector('.cdm-header');
+    const tableBody = clone.querySelector('tbody');
+
+    header.textContent = cdm.key_system;
+
+    const addRow = (key, value) => {
+      const row = tableBody.insertRow(-1);
+      const keyCell = row.insertCell(-1);
+      const valueCell = row.insertCell(-1);
+      keyCell.textContent = key;
+      if (typeof value === 'object') {
+        const pre = document.createElement('pre');
+        pre.textContent = JSON.stringify(value, null, 2);
+        valueCell.appendChild(pre);
+      } else {
+        valueCell.textContent = value;
+      }
+    };
+
+    addRow('Robustness', cdm.robustness);
+    addRow('Name', cdm.name);
+    addRow('Version', cdm.version);
+    addRow('Path', cdm.path);
+    addRow('Status', cdm.status);
+    addRow('Capabilities', cdm.capability);
+
+    return clone;
   }
 }
diff --git a/content/browser/resources/media/media_internals.css b/content/browser/resources/media/media_internals.css
index 67421e8f..5574e2d 100644
--- a/content/browser/resources/media/media_internals.css
+++ b/content/browser/resources/media/media_internals.css
@@ -2,225 +2,260 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file. */
 
+:root {
+  --background-color: #fff;
+  --text-color: #202124;
+  --header-color: #202124;
+  --border-color: #dadce0;
+  --selected-background-color: #e8eaed;
+  --hover-background-color: #f1f3f4;
+  --toolbar-background-color: #f8f9fa;
+  --button-background-color: #e8eaed;
+  --button-hover-background-color: #dadce0;
+  --scrollbar-thumb-color: #dadce0;
+  --scrollbar-track-color: #f1f3f4;
+  --log-property-text: #1358a2;
+  --log-value-text: #6d4d72;
+}
+
+@media (prefers-color-scheme: dark) {
+  :root {
+    --background-color: #202124;
+    --text-color: #e8eaed;
+    --header-color: #e8eaed;
+    --border-color: #5f6368;
+    --selected-background-color: #3c4043;
+    --hover-background-color: #2e2f32;
+    --toolbar-background-color: #282a2d;
+    --button-background-color: #3c4043;
+    --button-hover-background-color: #4c5054;
+    --scrollbar-thumb-color: #5f6368;
+    --scrollbar-track-color: #3c4043;
+    --log-property-text: #67b0ff;
+    --log-value-text: #d596df;
+  }
+}
+
 html,
-body,
-#container {
-  font-family: Arial;
+body {
+  font-family: 'Roboto', sans-serif;
   height: 100%;
   margin: 0;
   padding: 0;
   width: 100%;
+  background-color: var(--background-color);
+  color: var(--text-color);
 }
 
-cr-tab-box {
+#main-container {
+  display: flex;
+  flex-direction: column;
+  height: 100%;
+}
+
+#content-container {
+  display: flex;
+  flex-grow: 1;
+  overflow-y: auto;
+}
+
+#left-pane {
+  width: 250px;
+  min-width: 250px;
+  border-right: 1px solid var(--border-color);
+  padding: 10px;
+  display: flex;
+  flex-direction: column;
+}
+
+#player-list-wrapper {
+  flex: 1;
+  overflow-y: auto;
+  /* To make the above combination work, we need to set min-height to 0. */
+  min-height: 0;
+}
+
+#left-pane-controls {
   padding-top: 10px;
 }
 
-div[slot='tab'] {
-  -webkit-user-select: none;
+#hide-players-button {
+  width: 100%;
 }
 
-body div[slot='panel'] {
-  box-shadow: none;
+#right-pane {
+  flex-grow: 1;
+  display: flex;
+  flex-direction: column;
+  overflow-y: auto;
 }
 
-div[slot='panel'] {
-  padding: 10px;
+#right-pane-toolbar {
+  display: flex;
+  border-bottom: 1px solid var(--border-color);
+  background-color: var(--toolbar-background-color);
+  padding: 5px;
+  flex-shrink: 0;
 }
 
-table {
-  -webkit-font-smoothing: antialiased;
+.toolbar-button {
+  background-color: transparent;
+  border: 1px solid transparent;
+  color: var(--text-color);
+  padding: 8px 12px;
+  cursor: pointer;
+  font-size: 14px;
+}
+
+.toolbar-button.selected {
+  background-color: var(--selected-background-color);
+  border-color: var(--border-color);
+}
+
+.toolbar-button:hover {
+  background-color: var(--hover-background-color);
+}
+
+#right-pane-content {
+  overflow-y: auto;
+}
+
+.panel {
+  display: none;
+  padding: 20px;
+  box-sizing: border-box;
+}
+
+#players-panel {
+  padding: 0;
+}
+
+.panel.active {
   display: block;
-  font-family: sans-serif;
-  font-size: 115%;
-  overflow: auto;
-  width: auto;
-}
-th {
-  background-color: rgb(74, 169, 228);
-  color: white;
-  font-weight: normal;
-  min-width: 230px;
-  padding: 2px;
-  text-align: center;
-}
-td {
-  background-color: rgb(238, 238, 238);
-  color: rgb(111, 111, 111);
-  min-width: 230px;
-  padding: 2px;
-  word-wrap: break-word;
 }
 
 h1,
 h2,
 h3 {
-  color: rgb(50,50,50);
+  color: var(--header-color);
+  font-weight: 500;
 }
 
-#container {
-  align-content: stretch;
-  align-items: flex-start;
+.property-wrapper h2,
+#log-wrapper h2,
+#video-capture-capabilities-wrapper h2 {
   display: flex;
-  flex-direction: row;
-  flex-wrap: wrap;
-  justify-content: space-between;
+  align-items: center;
 }
 
-#container > * {
-  margin: 0;
-  padding: 0;
-  padding-inline-start: 25px;
+.copy-properties-button,
+.copy-log-button,
+#video-capture-capabilities-copy-button {
+  margin-left: auto;
+  font-size: 12px;
+  padding: 4px 8px;
 }
 
-#list-wrapper {
-  align-content: stretch;
-  align-items: flex-start;
-  display: flex;
-  flex-direction: column;
-  justify-content: space-between;
+table {
+  width: 100%;
+  border-collapse: collapse;
+  margin-top: 10px;
 }
 
-#player-list-wrapper,
-#audio-component-list-wrapper {
-  align-self: stretch;
-  flex-grow: 1;
-  min-width: 200px;
-  overflow: auto;
+th,
+td {
+  border: 1px solid var(--border-color);
+  padding: 8px;
+  text-align: left;
 }
 
-#player-list-wrapper ul,
-#player-list-wrapper li,
-#audio-component-list-wrapper ul,
-#audio-component-list-wrapper li {
+th {
+  background-color: var(--toolbar-background-color);
+}
+
+tr:nth-child(even) {
+  background-color: var(--hover-background-color);
+}
+
+td {
+  word-wrap: break-word;
+}
+
+#player-property-table th:first-child,
+#player-property-table td:first-child {
+  width: 250px;
+}
+
+ul {
   list-style-type: none;
   padding: 0;
 }
-#list-wrapper button {
-  padding: 0;
-}
-
-.property-wrapper,
-#log-wrapper {
-  align-self: stretch;
-  display: block;
-  flex-grow: 0.25;
-  margin-bottom: 10px;
-  overflow: auto;
-}
-
-#video-capture-capabilities-wrapper {
-  align-self: stretch;
-  flex-grow: 0.5;
-  overflow: auto;
-}
-
-#log-wrapper > thead {
-  position: fixed;
-}
-
-#graphs li {
-  list-style-type: none;
-}
-
-#clipboard-dialog {
-  bottom: 0;
-  padding-top: 0;
-  top: 0;
-}
-
-::backdrop {
-  background-color: #000;
-  opacity: 0.5;
-}
-
-.timestamp {
-  min-width: 115px;
-}
-
-#video-capture-capabilities-table {
-  margin-bottom: 30px;
-}
-
-#video-capture-capabilities-table th,
-#video-capture-capabilities-table td {
-  min-width: 120px;
-}
-
-#video-capture-capabilities-table td {
-  padding: 5px;
-}
-
-#video-capture-capabilities-table tr td {
-  font-size: 13px;
-  text-align: center;
-}
-
-#video-capture-capabilities-table .video-capture-formats-table th,
-#video-capture-capabilities-table .video-capture-formats-table td {
-  min-width: 80px;
-  text-align: end;
-}
-
-#video-capture-capabilities-table .video-capture-formats-table th {
-  background: none;
-  color: #666;
-  font-size: 13px;
-  font-weight: bold;
-}
-
-#video-capture-capabilities-table .video-capture-formats-table td {
-  padding: 2px;
-}
 
 .show-none-if-empty:empty::after {
-  color: rgba(0, 0, 0, .5);
+  color: rgba(255, 255, 255, 0.5);
   content: 'none';
 }
 
-label.audio-focus-session,
-label.cdm,
-label.selectable-button {
-  border: solid 1px #999;
+.audio-focus-session {
+  border: 1px solid var(--border-color);
   border-radius: 3px;
   display: block;
   line-height: 1.4;
-  margin: 4px 0;
-  padding: 6px;
-  word-break: break-all;
+  margin: 8px 0;
+  padding: 12px;
+  overflow-wrap: break-word;
 }
 
-label.selectable-button {
-  background: rgb(187, 221, 255);
-  cursor: pointer;
-  user-select: none;
+.cdm-item {
+  border: 1px solid var(--border-color);
+  border-radius: 3px;
+  margin-bottom: 15px;
+  list-style-type: none;
 }
 
-input.selectable-button {
-  display: none;
+.cdm-info-list {
+  list-style-type: disc;
+  padding-inline-start: 40px;
+  margin-bottom: 15px;
 }
 
-input.selectable-button:checked + label.selectable-button {
-  background-color: rgb(33, 150, 243);
-  border-color: #666;
-  color: #FFF;
+.cdm-header {
+  background-color: var(--toolbar-background-color);
+  padding: 8px 12px;
+  font-weight: 500;
+  border-bottom: 1px solid var(--border-color);
 }
 
-input.selectable-button:hover + label.selectable-button {
-  border-color: #666;
+.cdm-properties-table {
+  width: 100%;
+  border-collapse: collapse;
+  margin-top: 0;
 }
 
-label.audio-focus-session,
-label.cdm {
-  background-color: #EEE;
+.cdm-properties-table td {
+  padding: 8px 12px;
+  border: none;
+  border-bottom: 1px solid var(--border-color);
+  vertical-align: top;
+  background-color: transparent;
+}
+
+.cdm-properties-table tr:last-child td {
+  border-bottom: none;
+}
+
+.cdm-properties-table td:first-child {
+  font-weight: 500;
+  width: 120px;
+  color: var(--log-property-text);
 }
 
 label.errored-player {
-  background-color: rgb(255, 187, 187);
+  background-color: #6b2c2c;
 }
 
 label.ended-player {
-  background-color: rgb(207, 255, 187);
+  background-color: #2c6b2c;
 }
 
 .player-name {
@@ -231,22 +266,255 @@
   font-style: italic;
   overflow: hidden;
   text-overflow: ellipsis;
+  overflow-wrap: anywhere;
+}
+
+.tree-item {
+  margin-bottom: 5px;
+}
+
+.tree-item-header {
+  border: 1px solid var(--border-color);
+  border-left: 5px solid transparent;
+  border-radius: 3px;
+  cursor: pointer;
+  min-height: 30px;
+  overflow-wrap: anywhere;
+  padding: 8px 12px;
+  position: relative;
+  user-select: none;
+}
+
+.tree-item-header:hover {
+  background-color: var(--hover-background-color);
+}
+
+.tree-item.errored-player > .tree-item-header {
+  border-left-color: #d93025; /* Red */
+}
+
+.tree-item.ended-player > .tree-item-header {
+  border-left-color: #1e8e3e; /* Green */
+}
+
+.tree-item.active-player > .tree-item-header {
+  border-left-color: #1a73e8; /* Blue */
+}
+
+
+
+.tree-item.selected > .tree-item-header {
+  background-color: var(--selected-background-color);
+}
+
+.tree-item-children {
+  display: none;
+  padding-left: 15px;
+}
+
+.tree-item.expanded > .tree-item-children {
+  display: block;
+}
+
+.log-entry {
+  border-bottom: 1px solid var(--border-color);
+}
+
+.log-timestamp {
+  color: #9e9e9e; /* Grey */
+}
+
+.log-property {
+  color: var(--log-property-text);
+}
+
+.log-value {
+  color: var(--log-value-text);
+}
+
+.log-error {
+  background-color: rgba(244, 67, 54, 0.2); /* Red */
+  color: #ef5350;
+}
+
+.log-warning {
+  background-color: rgba(255, 235, 59, 0.2); /* Yellow */
+  color: #ffee58;
+}
+
+#player-details-container {
+  display: flex;
+  flex-direction: column;
+  height: 100%;
+}
+
+#player-details-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  padding: 10px;
+  border-bottom: 1px solid var(--border-color);
+  background-color: var(--background-color);
+  position: sticky;
+  top: 0;
+  z-index: 1;
+}
+
+#player-details-title {
+  min-width: 0;
+  margin-right: 1em;
+  overflow: hidden;
+  text-overflow: ellipsis;
   white-space: nowrap;
 }
 
-.no-players-selected #players .property-wrapper,
-.no-players-selected #players #log-wrapper {
+#player-actions {
+  display: flex;
+  gap: 10px;
+  flex-shrink: 0;
+}
+
+#player-details-content {
+  overflow-y: auto;
+  flex-grow: 1;
+  padding: 10px;
+}
+
+.property-wrapper,
+#log-wrapper {
+  margin-bottom: 20px;
+}
+
+#filter-text {
+  background-color: var(--background-color);
+  color: var(--text-color);
+  border: 1px solid var(--border-color);
+  padding: 5px;
+}
+
+button {
+  background-color: var(--button-background-color);
+  color: var(--text-color);
+  border: 1px solid var(--border-color);
+  padding: 8px 12px;
+  cursor: pointer;
+}
+
+button:hover {
+  background-color: var(--button-hover-background-color);
+}
+
+::-webkit-scrollbar {
+  width: 10px;
+}
+
+::-webkit-scrollbar-track {
+  background: var(--scrollbar-track-color);
+}
+
+pre {
+  margin: 0;
+  white-space: pre-wrap;
+  word-break: break-all;
+}
+
+::-webkit-scrollbar-thumb {
+  background: var(--scrollbar-thumb-color);
+}
+
+.no-players-selected #player-details-container {
   display: none;
 }
 
-.no-components-selected #audio .property-wrapper {
+.no-players-selected #player-placeholder {
+  display: block;
+}
+
+#player-list:empty + #player-list-placeholder {
+  display: block;
+}
+
+#player-placeholder {
+  display: none;
+  padding: 20px;
+  text-align: center;
+  font-size: 16px;
+  color: var(--text-color);
+}
+
+#audio-component-list-wrapper {
+  margin-bottom: 20px;
+  padding-left: 20px;
+}
+
+#audio-output-stream-list > .tree-item-header {
+  line-height: 30px;
+}
+
+#audio-panel .tree-item {
+  width: 400px;
+}
+
+#close-player-view-button {
   display: none;
 }
 
-#audio-focus-session-list {
-  list-style: none;
-}
+@media (max-width: 768px) {
+  #content-container {
+    flex-direction: column;
+  }
 
-#cdm-list {
-  list-style: none;
+  body.players-tab-active.no-players-selected #right-pane {
+    display: none;
+  }
+
+  #left-pane {
+    width: auto;
+    min-width: 0;
+    border-right: none;
+    border-bottom: 1px solid var(--border-color);
+    height: 100%;
+  }
+
+  #right-pane-toolbar {
+    flex-wrap: nowrap;
+    overflow-x: auto;
+  }
+
+  .toolbar-button {
+    padding: 6px 8px;
+  }
+
+  #player-details-header {
+    flex-wrap: wrap;
+    gap: 10px;
+  }
+
+  #player-actions {
+    flex-wrap: wrap;
+  }
+
+  .mobile-player-view-active #left-pane,
+  .mobile-player-view-active #right-pane-toolbar {
+    display: none;
+  }
+
+  .mobile-player-view-active #right-pane {
+    position: fixed;
+    top: 0;
+    left: 0;
+    width: 100%;
+    height: 100%;
+    z-index: 2;
+    background-color: var(--background-color);
+  }
+
+  .mobile-player-view-active #close-player-view-button {
+    display: inline-block;
+  }
+
+  #player-property-table th:first-child,
+  #player-property-table td:first-child {
+    width: 80px;
+  }
 }
diff --git a/content/browser/resources/media/media_internals.html b/content/browser/resources/media/media_internals.html
index 82a30c16..36f355fc 100644
--- a/content/browser/resources/media/media_internals.html
+++ b/content/browser/resources/media/media_internals.html
@@ -14,161 +14,171 @@
 </head>
 
 <body>
-  <cr-tab-box hidden>
-    <div slot="tab">Players</div>
-    <div slot="tab">Audio</div>
-    <div slot="tab">Video Capture</div>
-    <div slot="tab">Audio Focus</div>
-    <div slot="tab">CDMs</div>
-    <div slot="panel" id="players">
-      <button id="save-log-button" title="Save all player logs into a file." style="display:none">Save log</button>
-      <button id="hide-players-button" title="Hide all players in the current view." stype="display:inline-block">Hide players</button>
-      <button id="copy-all-player-button">Copy all to clipboard</button>
-      <div id="list-wrapper">
+  <div id="main-container">
+    <div id="right-pane-toolbar">
+      <button id="players-tab-button" class="toolbar-button selected">Players</button>
+      <button id="audio-tab-button" class="toolbar-button">Audio</button>
+      <button id="video-capture-tab-button" class="toolbar-button">Video Capture</button>
+      <button id="audio-focus-tab-button" class="toolbar-button">Audio Focus</button>
+      <button id="cdms-tab-button" class="toolbar-button">CDMs</button>
+    </div>
+    <div id="content-container">
+      <div id="left-pane">
         <div id="player-list-wrapper">
-          <h2>Recent Players</h2>
+          <h2>Select a player</h2>
           <ul id="player-list" class="show-none-if-empty"></ul>
+          <div id="player-list-placeholder" class="placeholder-text" hidden>
+            No media player logs
+          </div>
+        </div>
+        <div id="left-pane-controls">
+          <button id="hide-players-button">Clear Players</button>
         </div>
       </div>
-      <div class="property-wrapper">
-        <h2>
-          Player Properties
-          <button class="copy-properties-button">Copy to clipboard</button>
-        </h2>
-        <table id="player-property-table">
-          <thead>
-            <tr>
-              <th>Property</th>
-              <th>Value</th>
-            </tr>
-          </thead>
-          <tbody></tbody>
-        </table>
-      </div>
-      <div id="log-wrapper">
-        <h2>
-          Log <input id="filter-text" type="text" placeholder="property filter">
-          <button class="copy-log-button">Copy to clipboard</button>
-        </h2>
-        <table id="log">
-          <thead>
-            <tr>
-              <th class="timestamp">Timestamp</th>
-              <th>Property</th>
-              <th>Value</th>
-            </tr>
-          </thead>
-          <tbody></tbody>
-        </table>
-      </div>
-      <ul id="graphs"></ul>
-    </div>
-    <div slot="panel" id="audio">
-      <button id="copy-all-audio-button">Copy all to clipboard</button>
-      <div>
-        <h2>General Information</h2>
-        <table id="general-audio-info-table">
-          <thead>
-            <tr>
-              <th>Property</th>
-              <th>Value</th>
-            </tr>
-          </thead>
-          <tbody></tbody>
-        </table>
-      </div>
-      <div id="audio-component-list-wrapper">
-        <h2>Input Controllers</h2>
-        <ul id="audio-input-controller-list" class="show-none-if-empty"></ul>
-      </div>
-      <div id="audio-component-list-wrapper">
-        <h2>Output Controllers</h2>
-        <ul id="audio-output-controller-list" class="show-none-if-empty"></ul>
-      </div>
-      <div id="audio-component-list-wrapper">
-        <h2>Output Streams</h2>
-        <ul id="audio-output-stream-list" class="show-none-if-empty"></ul>
-      </div>
-      <div class="property-wrapper">
-        <h2>
-          <span id="audio-property-name"></span> Properties
-          <button class="copy-properties-button">Copy to clipboard</button>
-        </h2>
-        <table id="audio-property-table">
-          <thead>
-            <tr>
-              <th>Property</th>
-              <th>Value</th>
-            </tr>
-          </thead>
-          <tbody></tbody>
-        </table>
+      <div id="right-pane">
+        <div id="right-pane-content">
+          <div id="players-panel" class="panel">
+            <div id="player-placeholder" class="placeholder-text">
+              Select a player to view properties and events.
+            </div>
+            <div id="player-details-container">
+              <div id="player-details-header">
+                <h2 id="player-details-title">Player Properties</h2>
+                <div id="player-actions">
+                  <input id="filter-text" type="text" placeholder="Filter log...">
+                  <button id="copy-log-button">Copy All</button>
+                  <button id="save-log-button">Save Log</button>
+                  <button id="close-player-view-button">Close</button>
+                </div>
+              </div>
+              <div id="player-details-content">
+                <table id="player-property-table">
+                  <thead>
+                    <tr>
+                      <th>Property</th>
+                      <th>Value</th>
+                    </tr>
+                  </thead>
+                  <tbody></tbody>
+                </table>
+                <div id="log-wrapper">
+                  <h2>Log</h2>
+                  <table id="log">
+                    <thead>
+                      <tr>
+                        <th class="timestamp">Timestamp</th>
+                        <th>Property</th>
+                        <th>Value</th>
+                      </tr>
+                    </thead>
+                    <tbody></tbody>
+                  </table>
+                </div>
+              </div>
+              <ul id="graphs"></ul>
+            </div>
+          </div>
+          <div id="audio-panel" class="panel" hidden>
+            <div id="audio-component-list-wrapper">
+              <h2>Audio</h2>
+              <h3>Input Controllers</h3>
+              <ul id="audio-input-controller-list" class="show-none-if-empty"></ul>
+              <h3>Output Controllers</h3>
+              <ul id="audio-output-controller-list" class="show-none-if-empty"></ul>
+              <h3>Output Streams</h3>
+              <ul id="audio-output-stream-list" class="show-none-if-empty"></ul>
+            </div>
+            <div>
+              <h2>General Information</h2>
+              <table id="general-audio-info-table">
+                <thead>
+                  <tr>
+                    <th>Property</th>
+                    <th>Value</th>
+                  </tr>
+                </thead>
+                <tbody></tbody>
+              </table>
+            </div>
+            <div class="property-wrapper">
+              <h2>
+                <span id="audio-property-name"></span> Properties
+                <button class="copy-properties-button">Copy to clipboard</button>
+              </h2>
+              <table id="audio-property-table">
+                <thead>
+                  <tr>
+                    <th>Property</th>
+                    <th>Value</th>
+                  </tr>
+                </thead>
+                <tbody></tbody>
+              </table>
+            </div>
+          </div>
+          <div id="video-capture-panel" class="panel" hidden>
+            <div id="video-capture-capabilities-wrapper">
+              <h2>
+                <span>Video Capture Device Capabilities</span>
+                <button id="video-capture-capabilities-copy-button">
+                  Copy to clipboard
+                </button>
+              </h2>
+              <table id="video-capture-capabilities-table">
+                <thead>
+                  <tr>
+                    <th>Device Name</th>
+                    <th>Formats</th>
+                    <th>Capture API</th>
+                    <th>Pan-Tilt-Zoom</th>
+                    <th>Device ID</th>
+                  </tr>
+                </thead>
+                <tbody id="video-capture-capabilities-tbody" class="show-none-if-empty"></tbody>
+              </table>
+            </div>
+          </div>
+          <div id="audio-focus-panel" class="panel" hidden>
+            <div id="list-wrapper">
+              <h2>Active Sessions</h2>
+              <ul id="audio-focus-session-list" class="show-none-if-empty"></ul>
+            </div>
+            <template id="audio-focus-session-row">
+              <li>
+                <label class="audio-focus-session">
+                  <span class="player-name"></span><br />
+                  <span class="player-frame"></span><br />
+                  <span class="player-desc"></span>
+                </label>
+              </li>
+            </template>
+          </div>
+          <div id="cdms-panel" class="panel" hidden>
+            <div id="list-wrapper">
+              <h2>Registered Content Decryption Modules</h2>
+              <ul class="cdm-info-list">
+                <li>Clear Key ("org.w3.clearkey") is always supported and not listed
+                  here.</li>
+                <li>Empty video codec profile list means we are not differentiating
+                  and assume all profiles are supported.</li>
+                <li>Codecs marked with "*" signals clear lead not supported.</li>
+              </ul>
+              <ul id="cdm-list" class="show-none-if-empty"></ul>
+            </div>
+            <template id="cdm-row">
+              <li class="cdm-item">
+                <div class="cdm-header"></div>
+                <table class="cdm-properties-table">
+                  <tbody>
+                  </tbody>
+                </table>
+              </li>
+            </template>
+          </div>
+        </div>
       </div>
     </div>
-    <div slot="panel" id="video-capture">
-      <div id="video-capture-capabilities-wrapper">
-        <h2>
-          <span>Video Capture Device Capabilities</span>
-          <button id="video-capture-capabilities-copy-button">
-            Copy to clipboard
-          </button>
-        </h2>
-        <table id="video-capture-capabilities-table">
-          <thead>
-            <tr>
-              <th>Device Name</th>
-              <th>Formats</th>
-              <th>Capture API</th>
-              <th>Pan-Tilt-Zoom</th>
-              <th>Device ID</th>
-            </tr>
-          </thead>
-          <tbody id="video-capture-capabilities-tbody" class="show-none-if-empty"></tbody>
-        </table>
-      </div>
-    </div>
-    <div slot="panel" id="audio-focus">
-      <div id="list-wrapper">
-        <h2>Active Sessions</h2>
-        <ul id="audio-focus-session-list" class="show-none-if-empty"></ul>
-      </div>
-      <template id="audio-focus-session-row">
-        <li>
-          <label class="audio-focus-session">
-            <span class="player-name"></span><br />
-            <span class="player-frame"></span><br />
-            <span class="player-desc"></span>
-          </label>
-        </li>
-      </template>
-    </div>
-    <div slot="panel" id="cdms">
-      <div id="list-wrapper">
-        <h2>Registered Content Decryption Modules</h2>
-        <ul>
-          <li>Clear Key ("org.w3.clearkey") is always supported and not listed
-            here.</li>
-          <li>Empty video codec profile list means we are not differentiating
-            and assume all profiles are supported.</li>
-          <li>Codecs marked with "*" signals clear lead not supported.</li>
-        </ul>
-        <ul id="cdm-list" class="show-none-if-empty"></ul>
-      </div>
-      <template id="cdm-row">
-        <li>
-          <label class="cdm">
-            <span class="key-system"></span><br />
-            <span class="robustness"></span><br />
-            <span class="name"></span><br />
-            <span class="version"></span><br />
-            <span class="path"></span><br />
-            <span class="capability-status"></span><br />
-            <span class="capability"></span><br />
-          </label>
-        </li>
-      </template>
-    </div>
-  </cr-tab-box>
+  </div>
   <script type="module" src="media_internals.js"></script>
 </body>
-</html>
+</html>
\ No newline at end of file
diff --git a/content/browser/resources/media/media_internals.js b/content/browser/resources/media/media_internals.js
index 9951deb6..547ba8b3 100644
--- a/content/browser/resources/media/media_internals.js
+++ b/content/browser/resources/media/media_internals.js
@@ -2,25 +2,65 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import 'chrome://resources/cr_elements/cr_tab_box/cr_tab_box.js';
-
 import {ClientRenderer} from './client_renderer.js';
 import {initialize} from './main.js';
 import {Manager} from './manager.js';
 
 initialize(new Manager(new ClientRenderer()));
-const tabBox = document.querySelector('cr-tab-box');
-tabBox.hidden = false;
 
-const _TabIndicies = {
-  '#players': 0,
-  '#audio': 1,
-  '#video-capture': 2,
-  '#audio-focus': 3,
-  '#cdms': 4,
-};
+const toolbar = document.getElementById('right-pane-toolbar');
+const panels = document.getElementById('right-pane-content');
 
-const tabHash = window.location.hash.toLowerCase();
-if (tabHash in _TabIndicies) {
-  tabBox.setAttribute('selected-index', _TabIndicies[tabHash]);
+toolbar.addEventListener('click', (event) => {
+  if (event.target.classList.contains('toolbar-button')) {
+    const selectedButton = toolbar.querySelector('.selected');
+    if (selectedButton) {
+      selectedButton.classList.remove('selected');
+    }
+    event.target.classList.add('selected');
+
+    const panelId = event.target.id.replace('-tab-button', '-panel');
+    const activePanel = panels.querySelector('.panel.active');
+    if (activePanel) {
+      activePanel.classList.remove('active');
+      activePanel.hidden = true;
+    }
+    const newActivePanel = panels.querySelector(`#${panelId}`);
+    if (newActivePanel) {
+      newActivePanel.classList.add('active');
+      newActivePanel.hidden = false;
+    }
+
+    if (event.target.id === 'players-tab-button') {
+      document.getElementById('left-pane').style.display = 'flex';
+      document.body.classList.add('players-tab-active');
+    } else {
+      document.getElementById('left-pane').style.display = 'none';
+      document.body.classList.remove('players-tab-active');
+    }
+  }
+});
+
+// Default to the players tab
+document.getElementById('players-tab-button').click();
+
+const contentContainer = document.getElementById('content-container');
+const leftPane = document.getElementById('left-pane');
+const rightPane = document.getElementById('right-pane');
+
+function updateLayoutForMobile() {
+  if (window.innerWidth <= 768) {
+    // Move left-pane below the toolbar in right-pane
+    if (leftPane.parentElement !== contentContainer) {
+      contentContainer.insertBefore(leftPane, rightPane);
+    }
+  } else {
+    // Move left-pane back to the main container
+    if (leftPane.parentElement !== contentContainer) {
+      contentContainer.insertBefore(leftPane, rightPane);
+    }
+  }
 }
+
+window.addEventListener('resize', updateLayoutForMobile);
+updateLayoutForMobile();