Delete the copy of the logcat viewer in chromium
Now that we have two copies of the logcat viewer, one in chromium
and one in LUCI LogDog, we can delete the copy in chromium
so that we don't maintain two copies of the same logcat viewer.
Bug: 425725671
Change-Id: I046d8ce480b5f6b5ced78d51d5348b0e57a261e1
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7041662
Commit-Queue: Martin Kong <[email protected]>
Reviewed-by: Andrew Grieve <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1529640}
NOKEYCHECK=True
GitOrigin-RevId: 29c6b525ee3ad408846586ef8c468878e7b9a2a7
diff --git a/logcat_filtering/OWNERS b/logcat_filtering/OWNERS
deleted file mode 100644
index 14b04e6..0000000
--- a/logcat_filtering/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
[email protected]
[email protected]
diff --git a/logcat_filtering/README.md b/logcat_filtering/README.md
deleted file mode 100644
index ba6279b..0000000
--- a/logcat_filtering/README.md
+++ /dev/null
@@ -1,13 +0,0 @@
-## Logcat Filtering
-
-This directory contains the logcat filtering site, which can be used to
-browse, filter, and jump around a logcat file.
-
-### How to Use
-
-Just launch this in Google Chrome:
-
-```bash
-cd tools/android/logcat_filtering
-google-chrome index.html
-```
diff --git a/logcat_filtering/index.html b/logcat_filtering/index.html
deleted file mode 100644
index 5200ef9..0000000
--- a/logcat_filtering/index.html
+++ /dev/null
@@ -1,165 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-<!--
-Copyright 2025 The Chromium Authors
-Use of this source code is governed by a BSD-style license that can be
-found in the LICENSE file.
--->
-
-<head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>Logcat Viewer</title>
- <link rel="stylesheet" href="styles.css">
- <link rel="stylesheet"
- href="https://fonts.googleapis.com/icon?family=Material+Icons">
- <script defer src="script.js"></script>
-</head>
-
-<body class="light-mode">
- <div id="controls">
- <div>
- <label for="file-upload-button" class="google-button">
- <i class="material-icons">upload_file</i> Upload
- </label>
- <input type="file" id="file-upload-button" class="hidden-element">
- </div>
-
- <div>
- <label for="paste-logcat-button" class="google-button">
- <i class="material-icons">content_paste</i> Paste
- </label>
- <input type="button" id="paste-logcat-button" class="hidden-element">
- </div>
-
- <div class="dropdown">
- <div id="dropdown-header-process" class="dropdown-header">
- <i class="material-icons">memory</i> Processes
- </div>
- <ol id="dropdown-list-process" class="dropdown-list hidden-element">
- <input type="text" placeholder="Search processes..."
- id="dropdown-search-process" class="dropdown-search-input">
- <li id="display-all-process-li" data-value="all"
- data-type="main-process" class="dropdown-item selected">
- <input id="display-all-process-checkbox" type="checkbox" checked>
- <label>Display All Processes</label>
- </li>
- <li id="display-named-process-li" data-value="named"
- data-type="main-process" class="dropdown-item selected">
- <input id="display-named-process-checkbox" type="checkbox" checked>
- <label>Display All Named Processes</label>
- </li>
- </ol>
- </div>
-
- <div class="dropdown">
- <div id="dropdown-header-tag" class="dropdown-header">
- <i class="material-icons">label</i> Tags
- </div>
- <ol id="dropdown-list-tag" class="dropdown-list hidden-element">
- <input type="text" placeholder="Search tags..."
- id="dropdown-search-tag" class="dropdown-search-input">
- <li id="display-all-tag-li" data-value="all" data-type="main-tag"
- class="dropdown-item selected">
- <input id="display-all-tag-checkbox" type="checkbox" checked>
- <label>Display All Tags</label>
- </li>
- </ol>
- </div>
-
- <div class="dropdown">
- <div id="dropdown-header-priority" class="dropdown-header">
- <i class="material-icons">warning</i> Priorities
- </div>
- <ol id="dropdown-list-priority" class="dropdown-list hidden-element">
- <li id="display-all-priority-li" data-value="all"
- data-type="main-priority" class="dropdown-item selected">
- <input id="display-all-priority-checkbox" type="checkbox" checked>
- <label>Display All Priorities</label>
- </li>
- <li data-value="V" data-type="priority" class="dropdown-item selected">
- <input type="checkbox" checked>
- <label>Verbose</label>
- </li>
- <li data-value="D" data-type="priority" class="dropdown-item selected">
- <input type="checkbox" checked>
- <label>Debug</label>
- </li>
- <li data-value="I" data-type="priority" class="dropdown-item selected">
- <input type="checkbox" checked>
- <label>Info</label>
- </li>
- <li data-value="W" data-type="priority" class="dropdown-item selected">
- <input type="checkbox" checked>
- <label>Warning</label>
- </li>
- <li data-value="E" data-type="priority" class="dropdown-item selected">
- <input type="checkbox" checked>
- <label>Error</label>
- </li>
- <li data-value="F" data-type="priority" class="dropdown-item selected">
- <input type="checkbox" checked>
- <label>Fatal</label>
- </li>
- <li data-value="S" data-type="priority" class="dropdown-item selected">
- <input type="checkbox" checked>
- <label>Silent</label>
- </li>
- </ol>
- </div>
-
- <div class="google-button arrow-controls">
- <i class="material-icons">error</i>
- <span id="exception-feedback"
- class="arrow-feedback">Exceptions (0/0)</span>
- <i id="prev-exception-button"
- class="material-icons arrow">keyboard_arrow_up</i>
- <i id="next-exception-button"
- class="material-icons arrow">keyboard_arrow_down</i>
- </div>
-
- <div class="google-button arrow-controls">
- <i class="material-icons">rule</i>
- <span id="test-feedback" class="arrow-feedback">Tests (0/0)</span>
- <i id="prev-test-button"
- class="material-icons arrow">keyboard_arrow_up</i>
- <i id="next-test-button"
- class="material-icons arrow">keyboard_arrow_down</i>
- </div>
-
- <div class="dropdown">
- <div id="dropdown-header-settings" class="dropdown-header">
- <i class="material-icons">settings</i> Settings
- </div>
- <ol id="dropdown-list-settings" class="dropdown-list hidden-element">
- <li class="dropdown-item">
- <input type="checkbox" id="hide-date-time-checkbox">
- <label>Hide Date & Time</label>
- </li>
- <li class="dropdown-item">
- <input type="checkbox" id="wrap-text-checkbox">
- <label>Wrap Lines</label>
- </li>
- <li class="dropdown-item selected">
- <input type="checkbox" id="display-non-logcat-checkbox" checked>
- <label>Show Non-Logcat Lines</label>
- </li>
- <li class="dropdown-item selected">
- <input type="checkbox" id="always-show-activity-manager-checkbox"
- checked>
- <label>Always Show ActivityManager</label>
- </li>
- <li class="dropdown-item">
- <input type="checkbox" id="toggle-dark-mode-checkbox">
- <label>Dark Mode</label>
- </li>
- </ol>
- </div>
- </div>
-
- <div id="text-display-area" class="not-wrap-text">
- <div>Please upload a text file containing your logcat.</div>
- </div>
-</body>
-
-</html>
diff --git a/logcat_filtering/script.js b/logcat_filtering/script.js
deleted file mode 100644
index de5eb10..0000000
--- a/logcat_filtering/script.js
+++ /dev/null
@@ -1,1232 +0,0 @@
-// Copyright 2025 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-'use strict';
-
-/**
- * @fileoverview
- * Display a logcat file based on the options selected by the user.
- */
-
-/**
- * @typedef {Object} ParsedLine
- * @property {boolean} isLogcat
- * @property {string|undefined} originalLine - defined when isLogcat = false
- * @property {string|undefined} date - defined when isLogcat = true
- * @property {string|undefined} time - defined when isLogcat = true
- * @property {number|undefined} pid - defined when isLogcat = true
- * @property {number|undefined} tid - defined when isLogcat = true
- * @property {string|undefined} priority - defined when isLogcat = true
- * @property {string|undefined} tag - defined when isLogcat = true
- * @property {string|undefined} message - defined when isLogcat = true
- */
-
-/**
- * @typedef {Object} FilterOption
- * @property {HTMLElement} li
- * @property {HTMLElement} checkbox
- */
-
-// HTML elements:
-
-const controlsDiv = document.getElementById('controls');
-const fileUploadButton = document.getElementById('file-upload-button');
-const pasteLogcatButton = document.getElementById('paste-logcat-button');
-const pasteLogcatButtonLabel = document.querySelector(
- 'label[for="paste-logcat-button"]');
-const dropdownHeaderProcess = document.getElementById(
- 'dropdown-header-process');
-const dropdownSearchProcess = document.getElementById(
- 'dropdown-search-process');
-const dropdownListProcess = document.getElementById('dropdown-list-process');
-const dropdownHeaderTag = document.getElementById('dropdown-header-tag');
-const dropdownSearchTag = document.getElementById('dropdown-search-tag');
-const dropdownListTag = document.getElementById('dropdown-list-tag');
-const dropdownHeaderPriority = document.getElementById(
- 'dropdown-header-priority');
-const dropdownListPriority = document.getElementById('dropdown-list-priority');
-const exceptionFeedback = document.getElementById('exception-feedback');
-const nextExceptionButton = document.getElementById('next-exception-button');
-const prevExceptionButton = document.getElementById('prev-exception-button');
-const testFeedback = document.getElementById('test-feedback');
-const nextTestButton = document.getElementById('next-test-button');
-const prevTestButton = document.getElementById('prev-test-button');
-const dropdownHeaderSettings = document.getElementById(
- 'dropdown-header-settings');
-const dropdownListSettings = document.getElementById('dropdown-list-settings');
-const hideDateTimeCheckbox = document.getElementById('hide-date-time-checkbox');
-const wrapTextCheckbox = document.getElementById('wrap-text-checkbox');
-const displayNonLogcatCheckbox = document.getElementById(
- 'display-non-logcat-checkbox');
-const alwaysShowActivityManagerCheckbox = document.getElementById(
- 'always-show-activity-manager-checkbox');
-const toggleDarkModeCheckbox = document.getElementById(
- 'toggle-dark-mode-checkbox');
-const textDisplayArea = document.getElementById('text-display-area');
-
-// Event listeners:
-
-fileUploadButton.addEventListener('change', handleFileUpload);
-
-pasteLogcatButton.addEventListener('click', handlePasteLogcatButtonClick);
-
-dropdownHeaderProcess.addEventListener('click', (event) => {
- // Prevent this click from propagating to the document
- event.stopPropagation();
- toggleDropdown(dropdownListProcess);
-});
-
-dropdownSearchProcess.addEventListener('input', (event) => {
- filterDropdownItems(
- event.target.value,
- displaySingleNamedProcess.concat(displaySingleUnnamedProcess));
-});
-
-dropdownListProcess.addEventListener('click', handleProcessFilterOptionClick);
-
-dropdownHeaderTag.addEventListener('click', (event) => {
- // Prevent this click from propagating to the document
- event.stopPropagation();
- toggleDropdown(dropdownListTag);
-});
-
-dropdownSearchTag.addEventListener('input', (event) => {
- filterDropdownItems(event.target.value, displaySingleTag);
-});
-
-dropdownListTag.addEventListener('click', handleTagFilterOptionClick);
-
-dropdownHeaderPriority.addEventListener('click', (event) => {
- // Prevent this click from propagating to the document
- event.stopPropagation();
- toggleDropdown(dropdownListPriority);
-});
-
-dropdownListPriority.addEventListener('click', handlePriorityFilterOptionClick);
-
-nextExceptionButton.addEventListener('click', jumpToNextException);
-
-prevExceptionButton.addEventListener('click', jumpToPreviousException);
-
-nextTestButton.addEventListener('click', jumpToNextTest);
-
-prevTestButton.addEventListener('click', jumpToPreviousTest);
-
-dropdownHeaderSettings.addEventListener('click', (event) => {
- // Prevent this click from propagating to the document
- event.stopPropagation();
- toggleDropdown(dropdownListSettings);
-});
-
-dropdownListSettings.addEventListener('click', handleSettingsOptionClick);
-
-// Global click listener to close dropdowns when clicking outside
-document.addEventListener('click', (event) => {
- document.querySelectorAll('.dropdown-list').forEach(dropdown => {
- // Check if the clicked element is *not* inside the current dropdown, and
- // *not* the header of the current dropdown.
- if (!dropdown.contains(event.target) &&
- !dropdown.previousElementSibling.contains(event.target)) {
- dropdown.style.display = 'none';
- }
- });
-});
-
-// Check for system color scheme preference on page load
-const prefersDarkScheme = window.matchMedia('(prefers-color-scheme:dark)');
-if (prefersDarkScheme.matches) {
- document.body.classList.add('dark-mode');
- document.body.classList.remove('light-mode');
- toggleDarkModeCheckbox.checked = true;
-}
-
-// Listen for changes in system color scheme preference
-prefersDarkScheme.addEventListener('change', (event) => {
- if (event.matches) {
- document.body.classList.add('dark-mode');
- document.body.classList.remove('light-mode');
- toggleDarkModeCheckbox.checked = true;
- } else {
- document.body.classList.add('light-mode');
- document.body.classList.remove('dark-mode');
- toggleDarkModeCheckbox.checked = false;
- }
-});
-
-// Global variables:
-
-/** @type {Array<ParsedLine>} */
-let currentFileParsedLines = [];
-
-/** @type {FilterOption} */
-const displayAllProcesses = {
- li: document.getElementById('display-all-process-li'),
- checkbox: document.getElementById('display-all-process-checkbox'),
-};
-
-/** @type {FilterOption} */
-const displayNamedProcesses = {
- li: document.getElementById('display-named-process-li'),
- checkbox: document.getElementById('display-named-process-checkbox'),
-};
-
-/** @type {Array<FilterOption>} */
-let displaySingleNamedProcess = [];
-
-/** @type {Array<FilterOption>} */
-let displaySingleUnnamedProcess = [];
-
-/** @type {Set<number>} */
-let allPids = new Set();
-
-/** @type {Map<number, string>} */
-let pidToProcessName = new Map();
-
-/** @type {FilterOption} */
-const displayAllTags = {
- li: document.getElementById('display-all-tag-li'),
- checkbox: document.getElementById('display-all-tag-checkbox'),
-};
-
-/** @type {Array<FilterOption>} */
-let displaySingleTag = [];
-
-/** @type {Set<string>} */
-let allTags = new Set();
-
-/** @type {FilterOption} */
-const displayAllPriorities = {
- li: document.getElementById('display-all-priority-li'),
- checkbox: document.getElementById('display-all-priority-checkbox'),
-};
-
-const displaySinglePriorityLi = Array.from(
- dropdownListPriority.querySelectorAll('li[data-type="priority"]'));
-
-/** @type {Array<FilterOption>} */
-const displaySinglePriority = displaySinglePriorityLi.map(li => {
- const checkbox = li.querySelector('input[type="checkbox"]');
- return {
- li: li,
- checkbox: checkbox,
- };
-});
-
-/** @type {RegExp} */
-const pidRegexPattern = new RegExp('Start proc (\\d+):(.+?)\\/');
-
-/**
- * Toggles the display of a given dropdown list.
- * @param {HTMLElement} dropdownList The dropdown list element to toggle.
- */
-function toggleDropdown(dropdownList) {
- if (dropdownList.style.display === 'block') {
- dropdownList.style.display = 'none';
- } else {
- // Close other dropdowns before opening a new one
- document.querySelectorAll('.dropdown-list').forEach(list => {
- if (list !== dropdownList) {
- list.style.display = 'none';
- }
- });
-
- // Get the dropdown header to position the list against
- const header = dropdownList.previousElementSibling;
- const rect = header.getBoundingClientRect();
-
- // Set the dropdown list's position based on the header
- dropdownList.style.left = `${rect.left}px`;
- dropdownList.style.top = `${rect.bottom}px`;
-
- dropdownList.style.display = 'block';
- }
-}
-
-/**
- * This function is called when the user clicks on the file upload button.
- * @param {Event} event
- */
-function handleFileUpload(event) {
- const file = event.target.files[0];
- if (file) {
- const reader = new FileReader();
- reader.onload = function(e) {
- setUpElements(e.target.result.split('\n'));
- updateTextDisplayArea(false);
- };
- reader.onerror = function(e) {
- setUpElements([]);
- textDisplayArea.innerHTML = 'Encountered an error when reading the file.';
- };
- reader.readAsText(file);
- }
-
- // Reset the value of the file input element after the file is processed.
- if (event.target) {
- event.target.value = '';
- }
-}
-
-/**
- * This function is called when the user clicks on the paste logcat button.
- * @param {Event} event
- */
-function handlePasteLogcatButtonClick(event) {
- if (!pasteLogcatButtonLabel.textContent.includes('Finish Pasting')) {
- // Change the UI to allow the user to paste text.
- pasteLogcatButtonLabel.innerHTML =
- '<i class="material-icons">done</i> Finish Pasting';
- textDisplayArea.innerHTML = '';
- textDisplayArea.contentEditable = 'true';
- textDisplayArea.focus();
- } else {
- // Finish the pasting process and change the UI back to normal.
- pasteLogcatButtonLabel.innerHTML =
- '<i class="material-icons">content_paste</i> Paste';
- textDisplayArea.contentEditable = 'false';
- setUpElements(textDisplayArea.innerText.split('\n'));
- updateTextDisplayArea(false);
- }
-}
-
-/**
- * Given an array containing the lines in the current logcat file,
- * populate the global variables and set up the dropdowns.
- * @param {Array<string>} currentFileLines
- */
-function setUpElements(currentFileLines) {
- processCurrentFileLines(currentFileLines);
- setUpProcessDropdownList();
- setUpTagDropdownList();
- setUpPriorityDropdownList();
- setUpArrowFeedback();
-}
-
-/**
- * Process an array containing the lines in the current logcat file,
- * and populate the global variables currentFileParsedLines, allPids, allTags,
- * pidToProcessName based on the contents of the logcat file.
- * @param {Array<string>} currentFileLines
- */
-function processCurrentFileLines(currentFileLines) {
- currentFileParsedLines = [];
- allPids = new Set();
- allTags = new Set();
- pidToProcessName = new Map();
-
- for (const line of currentFileLines) {
- const parsedLine = parseOneLine(line);
- currentFileParsedLines.push(parsedLine);
- if (!parsedLine.isLogcat) {
- continue;
- }
-
- allPids.add(parsedLine.pid);
- allTags.add(parsedLine.tag);
-
- const match = pidRegexPattern.exec(parsedLine.message);
- if (match) {
- const processId = parseInt(match[1], 10);
- if (!isNaN(processId)) {
- const processName = match[2];
- allPids.add(processId);
- pidToProcessName.set(processId, processName);
- }
- }
- }
-}
-
-/**
- * Parse a single line in the logcat file, and return an object of type
- * ParsedLine that contains the individual components of the logcat line.
- * @param {string} line
- * @return {ParsedLine}
- */
-function parseOneLine(line) {
- const tokens = splitString(line, 7);
- if (tokens.length !== 13) {
- return { isLogcat: false, originalLine: line };
- }
-
- const date = tokens[0];
- const time = tokens[2];
- const pid = parseInt(tokens[4], 10);
- if (isNaN(pid)) {
- return { isLogcat: false, originalLine: line };
- }
- const tid = parseInt(tokens[6], 10);
- if (isNaN(tid)) {
- return { isLogcat: false, originalLine: line };
- }
- const priority = tokens[8];
- let tag = tokens[10];
- if (tag.endsWith(':')) {
- tag = tag.slice(0, -1);
- }
- let message = tokens[12];
- if (message.startsWith(': ')) {
- message = message.slice(2);
- } else {
- const whitespaceBeforeMessage = tokens[11];
- if (whitespaceBeforeMessage.length > 1) {
- message = whitespaceBeforeMessage.slice(1) + message;
- }
- }
-
- return { isLogcat: true, date, time, pid, tid, priority, tag, message };
-}
-
-/**
- * Split the input string into at most |maxParts| parts by whitespace.
- * If there are more than |maxParts| parts, the last part will contain all the
- * rest of the string so that the return value has exactly |maxParts| parts.
- * In addition to the parts that are not whitespace, the return value also
- * contains the whitespaces in between each part. For example, if the input
- * string is "abc def ghi jk" and maxParts is 3, then the return value is
- * ["abc", " ", "def", " ", "ghi jk"].
- * @param {string} inputString
- * @param {number} maxParts
- * @return {Array<string>}
- */
-function splitString(inputString, maxParts) {
- const parts = [];
- const whitespaceRegex = /\s+/g;
- let previousIndex = 0;
-
- // Each part except the last one corresponds to two entries in the parts array
- // so this loop keeps iterating while parts.length < (maxParts - 1) * 2
- while (parts.length < maxParts * 2 - 2) {
- const match = whitespaceRegex.exec(inputString);
- if (!match) {
- break;
- }
- parts.push(inputString.substring(previousIndex, match.index));
- parts.push(match[0]);
- previousIndex = whitespaceRegex.lastIndex;
- }
-
- parts.push(inputString.substring(previousIndex));
- return parts;
-}
-
-/**
- * Set up the dropdown list that filters the logcat based on process.
- */
-function setUpProcessDropdownList() {
- // Remove existing single process filter options from DOM.
- displaySingleNamedProcess.forEach(filterOption => filterOption.li.remove());
- displaySingleNamedProcess = [];
- displaySingleUnnamedProcess.forEach(filterOption => filterOption.li.remove());
- displaySingleUnnamedProcess = [];
-
- // Automatically select 'Display All Processes' and consequently all other
- // options in the dropdown when the user uploads a new file.
- displayAllProcesses.li.classList.add('selected');
- displayAllProcesses.checkbox.checked = true;
- displayNamedProcesses.li.classList.add('selected');
- displayNamedProcesses.checkbox.checked = true;
-
- const sortedPid = Array.from(allPids).sort((a, b) => a - b);
-
- // Named processes appear before the processes with no name.
- for (const pid of sortedPid) {
- if (pidToProcessName.has(pid)) {
- createProcessListItem(pid, pidToProcessName.get(pid));
- }
- }
-
- for (const pid of sortedPid) {
- if (!pidToProcessName.has(pid)) {
- createProcessListItem(pid);
- }
- }
-}
-
-/**
- * Create an HTML element of type li based on the given process id and name.
- * @param {number} pid
- * @param {string} processName
- */
-function createProcessListItem(pid, processName) {
- const listItem = document.createElement('li');
- listItem.dataset.value = pid;
- if (processName === undefined) {
- listItem.dataset.type = 'unnamed-process';
- } else {
- listItem.dataset.type = 'named-process';
- }
- listItem.classList.add('dropdown-item');
- listItem.classList.add('selected');
-
- const checkbox = document.createElement('input');
- checkbox.type = 'checkbox';
- checkbox.checked = true;
-
- const label = document.createElement('label');
- if (processName === undefined) {
- label.textContent = `${pid} (process name unknown)`;
- } else {
- label.textContent = `${pid} (${processName})`;
- }
-
- listItem.appendChild(checkbox);
- listItem.appendChild(label);
- dropdownListProcess.appendChild(listItem);
-
- if (processName === undefined) {
- displaySingleUnnamedProcess.push({
- li: listItem,
- checkbox: checkbox,
- });
- } else {
- displaySingleNamedProcess.push({
- li: listItem,
- checkbox: checkbox,
- });
- }
-}
-
-/**
- * This function is called when the user clicks on a process filter option.
- * @param {Event} event
- */
-function handleProcessFilterOptionClick(event) {
- const listItem = event.target.closest('li');
- if (!listItem) return;
-
- const checkbox = listItem.querySelector('input[type="checkbox"]');
- const value = listItem.dataset.value;
- const type = listItem.dataset.type;
-
- listItem.classList.toggle('selected');
- if (event.target.type !== 'checkbox') {
- checkbox.checked = !checkbox.checked;
- }
- const isSelected = checkbox.checked;
-
- if (type === 'main-process' && value === 'all') {
- displayNamedProcesses.li.classList.toggle('selected', isSelected);
- displayNamedProcesses.checkbox.checked = isSelected;
- for (const filterOption of displaySingleNamedProcess) {
- filterOption.li.classList.toggle('selected', isSelected);
- filterOption.checkbox.checked = isSelected;
- }
- for (const filterOption of displaySingleUnnamedProcess) {
- filterOption.li.classList.toggle('selected', isSelected);
- filterOption.checkbox.checked = isSelected;
- }
- } else if (type === 'main-process' && value === 'named') {
- for (const filterOption of displaySingleNamedProcess) {
- filterOption.li.classList.toggle('selected', isSelected);
- filterOption.checkbox.checked = isSelected;
- }
- updateDisplayAllProcessSelection();
- } else if (type === 'named-process') {
- updateDisplayNamedProcessSelection();
- updateDisplayAllProcessSelection();
- } else if (type === 'unnamed-process') {
- updateDisplayAllProcessSelection();
- }
-
- updateTextDisplayArea();
-}
-
-/**
- * Update the selected state of the 'Display All Processes' filter option.
- */
-function updateDisplayAllProcessSelection() {
- const allSelected = displayNamedProcesses.checkbox.checked &&
- displaySingleUnnamedProcess.every(
- filterOption => filterOption.checkbox.checked);
- displayAllProcesses.li.classList.toggle('selected', allSelected);
- displayAllProcesses.checkbox.checked = allSelected;
-}
-
-/**
- * Update the selected state of the 'Display All Named Processes' filter option.
- */
-function updateDisplayNamedProcessSelection() {
- const allSelected = displaySingleNamedProcess.every(
- filterOption => filterOption.checkbox.checked);
- displayNamedProcesses.li.classList.toggle('selected', allSelected);
- displayNamedProcesses.checkbox.checked = allSelected;
-}
-
-/**
- * Set up the dropdown list that filters the logcat based on tag.
- */
-function setUpTagDropdownList() {
- // Remove existing single tag filter options from DOM.
- displaySingleTag.forEach(filterOption => filterOption.li.remove());
- displaySingleTag = [];
-
- // Automatically select 'Display All Tags' and consequently all other
- // options in the dropdown when the user uploads a new file.
- displayAllTags.li.classList.add('selected');
- displayAllTags.checkbox.checked = true;
-
- const sortedTags = Array.from(allTags).sort();
- for (const tag of sortedTags) {
- createTagListItem(tag);
- }
-}
-
-/**
- * Create an HTML element of type li based on the given tag.
- * @param {string} tag
- */
-function createTagListItem(tag) {
- const listItem = document.createElement('li');
- listItem.dataset.value = tag;
- listItem.dataset.type = 'tag';
- listItem.classList.add('dropdown-item');
- listItem.classList.add('selected');
-
- const checkbox = document.createElement('input');
- checkbox.type = 'checkbox';
- checkbox.checked = true;
-
- const label = document.createElement('label');
- label.textContent = tag;
-
- listItem.appendChild(checkbox);
- listItem.appendChild(label);
- dropdownListTag.appendChild(listItem);
-
- displaySingleTag.push({
- li: listItem,
- checkbox: checkbox,
- });
-}
-
-/**
- * This function is called when the user clicks on a tag filter option.
- * @param {Event} event
- */
-function handleTagFilterOptionClick(event) {
- const listItem = event.target.closest('li');
- if (!listItem) return;
-
- const checkbox = listItem.querySelector('input[type="checkbox"]');
- const value = listItem.dataset.value;
- const type = listItem.dataset.type;
-
- listItem.classList.toggle('selected');
- if (event.target.type !== 'checkbox') {
- checkbox.checked = !checkbox.checked;
- }
- const isSelected = checkbox.checked;
-
- if (type === 'main-tag' && value === 'all') {
- for (const filterOption of displaySingleTag) {
- filterOption.li.classList.toggle('selected', isSelected);
- filterOption.checkbox.checked = isSelected;
- }
- } else if (type === 'tag') {
- updateDisplayAllTagSelection();
- }
-
- updateTextDisplayArea();
-}
-
-/**
- * Update the selected state of the 'Display All Tags' filter option.
- */
-function updateDisplayAllTagSelection() {
- const allSelected = displaySingleTag.every(
- filterOption => filterOption.checkbox.checked);
- displayAllTags.li.classList.toggle('selected', allSelected);
- displayAllTags.checkbox.checked = allSelected;
-}
-
-/**
- * Set up the dropdown list that filters the logcat based on priority.
- */
-function setUpPriorityDropdownList() {
- // Automatically select 'Display All Priorities' and consequently all other
- // options in the dropdown when the user uploads a new file.
- displayAllPriorities.li.classList.add('selected');
- displayAllPriorities.checkbox.checked = true;
-
- for (const filterOption of displaySinglePriority) {
- filterOption.li.classList.add('selected');
- filterOption.checkbox.checked = true;
- }
-}
-
-/**
- * This function is called when the user clicks on a priority filter option.
- * @param {Event} event
- */
-function handlePriorityFilterOptionClick(event) {
- const listItem = event.target.closest('li');
- if (!listItem) return;
-
- const checkbox = listItem.querySelector('input[type="checkbox"]');
- const value = listItem.dataset.value;
- const type = listItem.dataset.type;
-
- listItem.classList.toggle('selected');
- if (event.target.type !== 'checkbox') {
- checkbox.checked = !checkbox.checked;
- }
- const isSelected = checkbox.checked;
-
- if (type === 'main-priority' && value === 'all') {
- for (const filterOption of displaySinglePriority) {
- filterOption.li.classList.toggle('selected', isSelected);
- filterOption.checkbox.checked = isSelected;
- }
- } else if (type === 'priority') {
- updateDisplayAllPrioritySelection();
- }
-
- updateTextDisplayArea();
-}
-
-/**
- * Update the selected state of the 'Display All Priorities' filter option.
- */
-function updateDisplayAllPrioritySelection() {
- const allSelected = displaySinglePriority.every(
- filterOption => filterOption.checkbox.checked);
- displayAllPriorities.li.classList.toggle('selected', allSelected);
- displayAllPriorities.checkbox.checked = allSelected;
-}
-
-/**
- * This function is called when the user clicks on an option in the settings.
- * @param {Event} event
- */
-function handleSettingsOptionClick(event) {
- const listItem = event.target.closest('li');
- if (!listItem) return;
-
- const checkbox = listItem.querySelector('input[type="checkbox"]');
- const id = checkbox.id;
-
- listItem.classList.toggle('selected');
- if (event.target.type !== 'checkbox') {
- checkbox.checked = !checkbox.checked;
- }
-
- if (id === 'hide-date-time-checkbox') {
- updateTextDisplayArea();
-
- } else if (id === 'wrap-text-checkbox') {
- const shouldWrapText = wrapTextCheckbox.checked;
- if (shouldWrapText) {
- textDisplayArea.classList.add('wrap-text');
- textDisplayArea.classList.remove('not-wrap-text');
- } else {
- textDisplayArea.classList.add('not-wrap-text');
- textDisplayArea.classList.remove('wrap-text');
- }
-
- } else if (id === 'display-non-logcat-checkbox') {
- updateTextDisplayArea();
-
- } else if (id === 'always-show-activity-manager-checkbox') {
- updateTextDisplayArea();
-
- } else if (id === 'toggle-dark-mode-checkbox') {
- const isDarkMode = toggleDarkModeCheckbox.checked;
- if (isDarkMode) {
- document.body.classList.add('dark-mode');
- document.body.classList.remove('light-mode');
- } else {
- document.body.classList.add('light-mode');
- document.body.classList.remove('dark-mode');
- }
- }
-}
-
-/**
- * Filters the dropdown items based on the search term.
- * @param {string} searchTerm The text to search for.
- * @param {Array<FilterOption>} filterOptions The array of dropdown items to
- * filter.
- */
-function filterDropdownItems(searchTerm, filterOptions) {
- const lowerCaseSearchTerm = searchTerm.toLowerCase();
- for (const filterOption of filterOptions) {
- const labelText = filterOption.li.querySelector('label')
- .textContent.toLowerCase();
- if (labelText.includes(lowerCaseSearchTerm)) {
- filterOption.li.style.display = ''; // Show the item
- } else {
- filterOption.li.style.display = 'none'; // Hide the item
- }
- }
-}
-
-/**
- * Update the text display area with the contents of the current logcat file.
- */
-function updateTextDisplayArea(restoreScrollPosition = true) {
- // Find out which process ids are selected by the user.
- const selectedProcessOptions = Array.from(
- dropdownListProcess.querySelectorAll('li.selected'));
- const selectedPids = new Set();
-
- for (const option of selectedProcessOptions) {
- const type = option.dataset.type;
- const value = option.dataset.value;
- if (type === 'named-process' || type === 'unnamed-process') {
- const selectedPid = parseInt(value, 10);
- if (!isNaN(selectedPid)) {
- selectedPids.add(selectedPid);
- }
- }
- }
-
- // Find out which tags are selected by the user.
- const selectedTagOptions = Array.from(
- dropdownListTag.querySelectorAll('li.selected'));
- const selectedTags = new Set();
-
- for (const option of selectedTagOptions) {
- const type = option.dataset.type;
- const value = option.dataset.value;
- if (type === 'tag') {
- selectedTags.add(value);
- }
- }
-
- // Find out which priorities are selected by the user.
- const selectedPriorityOptions = Array.from(
- dropdownListPriority.querySelectorAll('li.selected'));
- const selectedPriorities = new Set();
-
- for (const option of selectedPriorityOptions) {
- const type = option.dataset.type;
- const value = option.dataset.value;
- if (type === 'priority') {
- selectedPriorities.add(value);
- }
- }
-
- // Find out the line numbers that will be displayed based on the options
- // selected by the user.
- const displayedLineNumbers = [];
- const displayNonLogcatLines = displayNonLogcatCheckbox.checked;
- const alwaysShowActivityManager = alwaysShowActivityManagerCheckbox.checked;
-
- for (const [i, parsedLine] of currentFileParsedLines.entries()) {
- if (!parsedLine.isLogcat) {
- if (displayNonLogcatLines) {
- displayedLineNumbers.push(i);
- }
- continue;
- }
-
- if (alwaysShowActivityManager) {
- if (parsedLine.tag === 'ActivityManager') {
- displayedLineNumbers.push(i);
- continue;
- }
- }
-
- if (selectedPids.has(parsedLine.pid) && selectedTags.has(parsedLine.tag) &&
- selectedPriorities.has(parsedLine.priority)) {
- displayedLineNumbers.push(i);
- }
- }
-
- // Map each line number to an HTML element with appropriate styling.
- const hideDateTime = hideDateTimeCheckbox.checked;
- const displayedHTMLElements = document.createDocumentFragment();
-
- for (const lineNumber of displayedLineNumbers) {
- const parsedLine = currentFileParsedLines[lineNumber];
- if (parsedLine.isLogcat) {
- const divElement = formatParsedLine(parsedLine, lineNumber, hideDateTime);
- displayedHTMLElements.appendChild(divElement);
- } else {
- const divElement = document.createElement('div');
- divElement.dataset.lineNumber = lineNumber;
- divElement.appendChild(document.createTextNode(parsedLine.originalLine));
- divElement.appendChild(document.createElement('br'));
- displayedHTMLElements.appendChild(divElement);
- }
- }
-
- if (!restoreScrollPosition) {
- textDisplayArea.innerHTML = '';
- textDisplayArea.appendChild(displayedHTMLElements);
- return;
- }
-
- // Find the first logcat line that is fully visible to the user
- // before the text display area is updated.
- const [firstVisibleLineNumber, firstVisibleLineOffset] =
- findFirstVisibleLine();
-
- // Update the text display area.
- textDisplayArea.innerHTML = '';
- textDisplayArea.appendChild(displayedHTMLElements);
- if (firstVisibleLineNumber === -1) {
- return;
- }
-
- // Scroll the window so that the firstVisibleLine becomes visible again.
- // If the firstVisibleLine no longer exists, make the next line visible.
- const logcatLines = Array.from(textDisplayArea.children);
- for (const logcatLine of logcatLines) {
- const lineNumber = parseInt(logcatLine.dataset.lineNumber, 10);
- if (!isNaN(lineNumber) && lineNumber >= firstVisibleLineNumber) {
- window.scrollTo(window.scrollX,
- logcatLine.offsetTop - firstVisibleLineOffset);
- break;
- }
- }
-}
-
-/**
- * Format a parsed logcat line into an HTML element with appropriate styling.
- * @param {ParsedLine} parsedLine
- * @param {number} lineNumber
- * @param {boolean} hideDateTime
- * @return {HTMLElement}
- */
-function formatParsedLine(parsedLine, lineNumber, hideDateTime) {
- const dim = !pidToProcessName.has(parsedLine.pid);
- const pidCssClasses = getPidStyling(parsedLine.pid, parsedLine.tag, dim);
- const tidCssClasses = getTidStyling(parsedLine.tid, parsedLine.pid, dim);
- const priorityCssClasses = getPriorityStyling(parsedLine.priority);
- const tagCssClasses = pidCssClasses.slice();
- tagCssClasses.push('log-bright');
- const messageCssClasses = pidCssClasses.slice();
-
- const pidElement = style(
- String(parsedLine.pid).padStart(5, ' '), pidCssClasses);
- const tidElement = style(
- String(parsedLine.tid).padStart(5, ' '), tidCssClasses);
- const priorityElement = style(parsedLine.priority, priorityCssClasses);
- const tagElement = style(parsedLine.tag.padEnd(8, ' '), tagCssClasses);
- const messageElement = style(parsedLine.message, messageCssClasses);
-
- const divElement = document.createElement('div');
- divElement.dataset.lineNumber = lineNumber;
- if (!hideDateTime) {
- divElement.appendChild(
- document.createTextNode(`${parsedLine.date} ${parsedLine.time} `));
- }
- divElement.appendChild(pidElement);
- divElement.appendChild(document.createTextNode(' '));
- divElement.appendChild(tidElement);
- divElement.appendChild(document.createTextNode(' '));
- divElement.appendChild(priorityElement);
- divElement.appendChild(document.createTextNode(' '));
- divElement.appendChild(tagElement);
- divElement.appendChild(document.createTextNode(': '));
- divElement.appendChild(messageElement);
- divElement.appendChild(document.createElement('br'));
- return divElement;
-}
-
-/**
- * Determine the CSS classes for styling a process id.
- * @param {number} pid
- * @param {string} tag
- * @param {boolean} dim
- * @return {Array<string>}
- */
-function getPidStyling(pid, tag, dim = false) {
- if (tag === 'ActivityManager' || tag === 'ActivityTaskManager') {
- return ['log-fore-black-pid'];
- }
- if (pidToProcessName.has(pid)) {
- return ['log-fore-yellow'];
- }
- if (dim) {
- return ['log-dim'];
- }
- return [];
-}
-
-/**
- * Determine the CSS classes for styling a thread id.
- * @param {number} tid
- * @param {number} pid
- * @param {boolean} dim
- * @return {Array<string>}
- */
-function getTidStyling(tid, pid, dim = false) {
- if (!dim && tid === pid) {
- return ['log-bright'];
- } else {
- return ['log-normal'];
- }
-}
-
-/**
- * Determine the CSS classes for styling a log priority.
- * @param {string} priority
- * @return {Array<string>}
- */
-function getPriorityStyling(priority) {
- let cssClasses = ['log-fore-black-priority'];
- if (priority === 'E' || priority === 'F') {
- cssClasses.push('log-back-red');
- } else if (priority === 'W') {
- cssClasses.push('log-back-yellow');
- } else if (priority === 'I') {
- cssClasses.push('log-back-green');
- } else if (priority === 'D') {
- cssClasses.push('log-back-blue');
- }
- return cssClasses;
-}
-
-/**
- * Apply CSS classes to the given text and wrap it in a span.
- * @param {string} text
- * @param {Array<string>} cssClasses
- * @return {HTMLElement}
- */
-function style(text, cssClasses) {
- const span = document.createElement('span');
- span.textContent = text;
-
- if (cssClasses !== undefined && cssClasses.length !== 0) {
- span.classList.add(...cssClasses);
- }
-
- return span;
-}
-
-/**
- * Find the first logcat line that is fully visible to the user.
- * Return its line number and its offset from the top edge of the screen.
- * Return [-1, -1] if no such logcat line can be found.
- * @return {Array<number>}
- */
-function findFirstVisibleLine() {
- const logcatLines = Array.from(textDisplayArea.children);
- const controlsHeight = controlsDiv.offsetHeight;
- const scrollPosition = window.scrollY;
- let firstVisibleLineNumber = -1;
- let firstVisibleLineOffset = -1;
-
- for (const logcatLine of logcatLines) {
- if (logcatLine.offsetTop - controlsHeight >= scrollPosition) {
- const lineNumber = parseInt(logcatLine.dataset.lineNumber, 10);
- if (!isNaN(lineNumber)) {
- firstVisibleLineNumber = lineNumber;
- firstVisibleLineOffset = logcatLine.offsetTop - scrollPosition;
- break;
- }
- }
- }
-
- return [firstVisibleLineNumber, firstVisibleLineOffset];
-}
-
-/**
- * Set up the feedback displayed on the exception and test buttons.
- * This is called initially after the user uploads a new logcat.
- */
-function setUpArrowFeedback() {
- let totalNumExceptions = 0;
- let totalNumTests = 0;
- for (const parsedLine of currentFileParsedLines) {
- if (isStartOfStackTrace(parsedLine)) {
- totalNumExceptions += 1;
- }
- if (isStartOfTest(parsedLine)) {
- totalNumTests += 1;
- }
- }
- exceptionFeedback.textContent = `Exceptions (0/${totalNumExceptions})`;
- testFeedback.textContent = `Tests (0/${totalNumTests})`;
-}
-
-/**
- * Scroll to the next logcat line that represents a program exception.
- */
-function jumpToNextException() {
- // Find the first logcat line that is fully visible to the user.
- const [firstVisibleLineNumber, _] = findFirstVisibleLine();
- if (firstVisibleLineNumber === -1) {
- return;
- }
-
- // Find the first line after the firstVisibleLine that represents
- // a program exception and scroll to that line.
- const logcatLines = Array.from(textDisplayArea.children);
- let totalNumExceptions = 0;
- let currentExceptionPosition = 0;
- for (const logcatLine of logcatLines) {
- const lineNumber = parseInt(logcatLine.dataset.lineNumber, 10);
- const parsedLine = currentFileParsedLines[lineNumber];
- if (isStartOfStackTrace(parsedLine)) {
- totalNumExceptions += 1;
- if (currentExceptionPosition === 0 &&
- lineNumber > firstVisibleLineNumber) {
- scrollToLine(logcatLine);
- currentExceptionPosition = totalNumExceptions;
- }
- }
- }
-
- if (totalNumExceptions === 0) {
- exceptionFeedback.textContent = `Exceptions (0/0)`;
- } else if (currentExceptionPosition === 0) {
- exceptionFeedback.textContent = `Exceptions ` +
- `(${totalNumExceptions}/${totalNumExceptions})`;
- } else {
- exceptionFeedback.textContent = `Exceptions ` +
- `(${currentExceptionPosition}/${totalNumExceptions})`;
- }
-}
-
-/**
- * Scroll to the previous logcat line that represents a program exception.
- */
-function jumpToPreviousException() {
- // Find the first logcat line that is fully visible to the user.
- const [firstVisibleLineNumber, _] = findFirstVisibleLine();
- if (firstVisibleLineNumber === -1) {
- return;
- }
-
- // Find the last line before the firstVisibleLine that represents
- // a program exception and scroll to that line.
- const logcatLines = Array.from(textDisplayArea.children);
- let totalNumExceptions = 0;
- let currentExceptionPosition = 0;
- let currentExceptionLogcatLine;
- for (const logcatLine of logcatLines) {
- const lineNumber = parseInt(logcatLine.dataset.lineNumber, 10);
- const parsedLine = currentFileParsedLines[lineNumber];
- if (isStartOfStackTrace(parsedLine)) {
- totalNumExceptions += 1;
- if (lineNumber < firstVisibleLineNumber - 1) {
- currentExceptionPosition = totalNumExceptions;
- currentExceptionLogcatLine = logcatLine;
- }
- }
- }
-
- if (totalNumExceptions === 0) {
- exceptionFeedback.textContent = `Exceptions (0/0)`;
- } else if (currentExceptionPosition === 0) {
- exceptionFeedback.textContent = `Exceptions (1/${totalNumExceptions})`;
- } else {
- scrollToLine(currentExceptionLogcatLine);
- exceptionFeedback.textContent = `Exceptions ` +
- `(${currentExceptionPosition}/${totalNumExceptions})`;
- }
-}
-
-/**
- * Given a ParsedLine, return whether it represents the start of a stack trace.
- * @param {ParsedLine} parsedLine
- * @return {boolean}
- */
-function isStartOfStackTrace(parsedLine) {
- return (parsedLine.isLogcat && parsedLine.tag === 'TestRunner' &&
- parsedLine.message === '----- begin exception -----') ||
- (!parsedLine.isLogcat && parsedLine.originalLine === 'Stack Trace:') ||
- (!parsedLine.isLogcat &&
- parsedLine.originalLine === '--------- beginning of crash');
-}
-
-/**
- * Scroll to the next logcat line that represents the start of a test.
- */
-function jumpToNextTest() {
- // Find the first logcat line that is fully visible to the user.
- const [firstVisibleLineNumber, _] = findFirstVisibleLine();
- if (firstVisibleLineNumber === -1) {
- return;
- }
-
- // Find the first line after the firstVisibleLine that represents
- // the start of a test and scroll to that line.
- const logcatLines = Array.from(textDisplayArea.children);
- let totalNumTests = 0;
- let currentTestPosition = 0;
- for (const logcatLine of logcatLines) {
- const lineNumber = parseInt(logcatLine.dataset.lineNumber, 10);
- const parsedLine = currentFileParsedLines[lineNumber];
- if (isStartOfTest(parsedLine)) {
- totalNumTests += 1;
- if (currentTestPosition === 0 &&
- lineNumber > firstVisibleLineNumber) {
- scrollToLine(logcatLine);
- currentTestPosition = totalNumTests;
- }
- }
- }
-
- if (totalNumTests === 0) {
- testFeedback.textContent = `Tests (0/0)`;
- } else if (currentTestPosition === 0) {
- testFeedback.textContent = `Tests ` +
- `(${totalNumTests}/${totalNumTests})`;
- } else {
- testFeedback.textContent = `Tests ` +
- `(${currentTestPosition}/${totalNumTests})`;
- }
-}
-
-/**
- * Scroll to the previous logcat line that represents the start of a test.
- */
-function jumpToPreviousTest() {
- // Find the first logcat line that is fully visible to the user.
- const [firstVisibleLineNumber, _] = findFirstVisibleLine();
- if (firstVisibleLineNumber === -1) {
- return;
- }
-
- // Find the last line before the firstVisibleLine that represents
- // the start of a test and scroll to that line.
- const logcatLines = Array.from(textDisplayArea.children);
- let totalNumTests = 0;
- let currentTestPosition = 0;
- let currentTestLogcatLine;
- for (const logcatLine of logcatLines) {
- const lineNumber = parseInt(logcatLine.dataset.lineNumber, 10);
- const parsedLine = currentFileParsedLines[lineNumber];
- if (isStartOfTest(parsedLine)) {
- totalNumTests += 1;
- if (lineNumber < firstVisibleLineNumber - 1) {
- currentTestPosition = totalNumTests;
- currentTestLogcatLine = logcatLine;
- }
- }
- }
-
- if (totalNumTests === 0) {
- testFeedback.textContent = `Tests (0/0)`;
- } else if (currentTestPosition === 0) {
- testFeedback.textContent = `Tests (1/${totalNumTests})`;
- } else {
- scrollToLine(currentTestLogcatLine);
- testFeedback.textContent = `Tests ` +
- `(${currentTestPosition}/${totalNumTests})`;
- }
-}
-
-/**
- * Given a ParsedLine, return whether it represents the start of a test.
- * @param {ParsedLine} parsedLine
- * @return {boolean}
- */
-function isStartOfTest(parsedLine) {
- return parsedLine.isLogcat && parsedLine.tag === 'TestRunner' &&
- parsedLine.message.startsWith('started: ');
-}
-
-/**
- * Given an HTMLElement representing one logcat line, scroll to that line.
- * @param {HTMLElement} logcatLine
- */
-function scrollToLine(logcatLine) {
- const controlsHeight = controlsDiv.offsetHeight;
- window.scrollTo(window.scrollX, logcatLine.offsetTop - controlsHeight);
-}
diff --git a/logcat_filtering/styles.css b/logcat_filtering/styles.css
deleted file mode 100644
index e1d94ac..0000000
--- a/logcat_filtering/styles.css
+++ /dev/null
@@ -1,314 +0,0 @@
-/* Copyright 2025 The Chromium Authors
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file. */
-
-.light-mode {
- --body-bg-color: white;
- --body-text-color: black;
- --controls-bg-color: white;
- --google-button-bg-color: #4285f4;
- --google-button-text-color: white;
- --google-button-hover-bg-color: #357ae8;
- --google-button-arrow-hover-bg-color: #0000001a;
- --dropdown-border-color: #ccc;
- --dropdown-header-bg-color: #f9f9f9;
- --dropdown-header-text-color: #5f6368;
- --dropdown-header-border-color: #eee;
- --dropdown-header-icon-color: #5f6368;
- --dropdown-list-border-color: #eee;
- --dropdown-item-text-color: #202124;
- --dropdown-item-hover-bg-color: #e8f0fe;
- --dropdown-item-hover-text-color: #1a73e8;
- --checkbox-accent-color: #4285f4;
- --checkbox-border-color: #5f6368;
- --checkbox-label-text-color: #202124;
- --text-display-bg-color: white;
- --text-display-text-color: black;
- --log-dim-color: rgb(135, 135, 135);
- --log-fore-yellow-color: rgb(160, 120, 80);
- --log-fore-black-pid-color: rgb(0, 0, 0);
- --log-fore-black-priority-color: rgb(0, 0, 0);
- --log-back-red-color: rgb(200, 60, 50);
- --log-back-yellow-color: rgb(160, 120, 80);
- --log-back-green-color: rgb(35, 145, 110);
- --log-back-blue-color: rgb(25, 70, 140);
-}
-
-.dark-mode {
- --body-bg-color: #1e1e1e;
- --body-text-color: #090a0a;
- --controls-bg-color: #1e1e1e;
- --google-button-bg-color: #5f6368;
- --google-button-text-color: #e8eaed;
- --google-button-hover-bg-color: #72767c;
- --google-button-arrow-hover-bg-color: #ffffff26;
- --dropdown-border-color: #5f6368;
- --dropdown-header-bg-color: #33363b;
- --dropdown-header-text-color: #e8eaed;
- --dropdown-header-border-color: #5f6368;
- --dropdown-header-icon-color: #e8eaed;
- --dropdown-list-border-color: #5f6368;
- --dropdown-item-text-color: #e8eaed;
- --dropdown-item-hover-bg-color: #3c4043;
- --dropdown-item-hover-text-color: #8ab4f8;
- --checkbox-accent-color: #8ab4f8;
- --checkbox-border-color: #9aa0a6;
- --checkbox-label-text-color: #e8eaed;
- --text-display-bg-color: #1e1e1e;
- --text-display-text-color: #e8eaed;
- --log-dim-color: rgb(200, 200, 200);
- --log-fore-yellow-color: rgb(160, 120, 80);
- --log-fore-black-pid-color: rgb(140, 140, 140);
- --log-fore-black-priority-color: rgb(0, 0, 0);
- --log-back-red-color: rgb(200, 60, 50);
- --log-back-yellow-color: rgb(160, 120, 80);
- --log-back-green-color: rgb(35, 145, 110);
- --log-back-blue-color: rgb(25, 70, 140);
-}
-
-.wrap-text {
- /* Preserve whitespace and wrap text whenever necessary */
- white-space: pre-wrap;
- word-break: break-all;
-}
-
-.not-wrap-text {
- /* Preserve whitespace and break text at newline characters only */
- white-space: pre;
-}
-
-.hidden-element {
- /* Hide the element by default */
- display: none;
-}
-
-body {
- /* Using Google's primary font */
- font-family: 'Roboto', sans-serif;
- background-color: var(--body-bg-color);
- color: var(--body-text-color);
- /* Add a vertical and/or horizontal scrollbar if needed */
- overflow: auto;
-}
-
-#controls {
- /* Fixed at the top regardless of how much we scroll */
- position: fixed;
- top: 0;
- left: 0;
- /* Prevent the content underneath from showing through */
- background-color: var(--controls-bg-color);
- /* Ensure the controls stay on top of other content */
- z-index: 100;
- width: 100%;
- padding: 8px 20px;
- display: flex;
- /* Adjust spacing between dropdowns and checkboxes */
- gap: 20px;
- /* Align items to the top */
- align-items: flex-start;
- /* Do not allow controls to wrap on smaller screens */
- flex-wrap: nowrap;
- /* Show a horizontal scrollbar on smaller screens */
- overflow-x: auto;
- /* Add a visual distinction between the controls and the text display area */
- border-bottom: 1px solid var(--dropdown-border-color);
-}
-
-.google-button {
- display: inline-flex;
- align-items: center;
- justify-content: center;
- padding: 10px 24px;
- font-size: 14px;
- font-weight: 500;
- background-color: var(--google-button-bg-color);
- color: var(--google-button-text-color);
- border: none;
- border-radius: 4px;
- cursor: pointer;
- box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24);
- transition: background-color 0.3s ease;
- /* Do not allow text wrapping */
- white-space: nowrap;
- /* Do not allow the user to highlight the text inside the button */
- user-select: none;
-}
-
-.google-button:hover {
- background-color: var(--google-button-hover-bg-color);
- box-shadow: 0 3px 6px rgba(0, 0, 0, 0.16), 0 3px 6px rgba(0, 0, 0, 0.23);
-}
-
-.google-button i {
- margin-right: 8px;
-}
-
-.google-button.arrow-controls {
- padding-right: 8px;
- /* Disable mouse interactions for the entire button */
- pointer-events: none;
-}
-
-.google-button .arrow-feedback {
- margin-right: 8px;
-}
-
-.google-button .arrow {
- cursor: pointer;
- font-size: 18px;
- border-radius: 3px;
- /* Re-enable mouse interaction only for the arrows */
- pointer-events: auto;
-}
-
-.google-button .arrow:hover {
- background-color: var(--google-button-arrow-hover-bg-color);
-}
-
-.dropdown {
- position: relative;
- border: 1px solid var(--dropdown-border-color);
- border-radius: 4px;
- box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
-}
-
-.dropdown-header {
- padding: 9px 16px;
- background-color: var(--dropdown-header-bg-color);
- color: var(--dropdown-header-text-color);
- border-bottom: 1px solid var(--dropdown-header-border-color);
- cursor: pointer;
- display: flex;
- align-items: center;
- font-size: 15px;
- font-weight: 500;
- /* Do not allow text wrapping */
- white-space: nowrap;
- /* Do not allow the user to highlight the text inside the dropdown header */
- user-select: none;
-}
-
-.dropdown-header i {
- margin-right: 12px;
- color: var(--dropdown-header-icon-color);
-}
-
-.dropdown-list {
- position: fixed;
- z-index: 200;
- list-style: none;
- padding: 0;
- margin: 0;
- border: 1px solid var(--dropdown-border-color);
- border-radius: 0 0 4px 4px;
- /* To contain the rounded corners */
- overflow: hidden;
- /* Add a maximum height for scrollability */
- max-height: 300px;
- overflow-y: auto;
- background-color: var(--dropdown-header-bg-color);
-}
-
-.dropdown-search-input {
- /* Full width minus padding */
- width: calc(100% - 32px);
- padding: 10px 16px;
- margin: 0;
- border: none;
- border-bottom: 1px solid var(--dropdown-list-border-color);
- background-color: var(--dropdown-header-bg-color);
- color: var(--dropdown-item-text-color);
- outline: none;
-}
-
-.dropdown-item {
- /* Adjust padding for checkbox and label */
- padding: 8px 16px;
- cursor: pointer;
- display: flex;
- align-items: center;
- font-size: 14px;
- color: var(--dropdown-item-text-color);
- transition: background-color 0.2s ease;
-}
-
-.dropdown-item:hover {
- background-color: var(--dropdown-item-hover-bg-color);
- color: var(--dropdown-item-hover-text-color);
-}
-
-.dropdown-item input[type="checkbox"] {
- margin-right: 12px;
- accent-color: var(--checkbox-accent-color);
- border: 1.5px solid var(--checkbox-border-color);
- border-radius: 2px;
-}
-
-.dropdown-item label {
- /* Allow the label to take up remaining space */
- flex-grow: 1;
- /* Make the label clickable */
- cursor: pointer;
- white-space: nowrap;
-}
-
-#text-display-area {
- /* Monospace font for code-like text */
- font-family: 'Roboto Mono', monospace;
- font-size: 14px;
- border: none;
- background-color: var(--text-display-bg-color);
- color: var(--text-display-text-color);
- margin-left: 20px;
- margin-top: 6em; /* Use em to adjust with page zoom. */
-}
-
-
-/* Styling applied to logcat lines */
-
-.log-normal {
- font-weight: normal;
-}
-
-.log-bright {
- font-weight: bold;
-}
-
-.log-dim {
- color: var(--log-dim-color);
-}
-
-
-/* Foreground colors */
-
-.log-fore-yellow {
- color: var(--log-fore-yellow-color);
-}
-
-.log-fore-black-pid {
- color: var(--log-fore-black-pid-color);
-}
-
-.log-fore-black-priority {
- color: var(--log-fore-black-priority-color);
-}
-
-
-/* Background colors */
-
-.log-back-red {
- background-color: var(--log-back-red-color);
-}
-
-.log-back-yellow {
- background-color: var(--log-back-yellow-color);
-}
-
-.log-back-green {
- background-color: var(--log-back-green-color);
-}
-
-.log-back-blue {
- background-color: var(--log-back-blue-color);
-}