From 610d7bc0169c245bf2f449a7aede2b51abdde231 Mon Sep 17 00:00:00 2001 From: "YUKI \"Piro\" Hiroshi" Date: Sat, 30 Nov 2024 04:56:55 +0900 Subject: [PATCH] Render tree in tab preview panel with HTML #3412 --- webextensions/common/Tab.js | 29 +++++++++++++ webextensions/resources/tab-preview-frame.js | 33 ++++++++++++-- .../sidebar/components/TabElement.js | 43 ++++++++++++++++++- webextensions/sidebar/tab-preview-tooltip.js | 2 + 4 files changed, 102 insertions(+), 5 deletions(-) diff --git a/webextensions/common/Tab.js b/webextensions/common/Tab.js index 1b4fae0ce..948109c39 100644 --- a/webextensions/common/Tab.js +++ b/webextensions/common/Tab.js @@ -14,6 +14,7 @@ import { mapAndFilter, mapAndFilterUniq, toLines, + sanitizeForHTMLText, sanitizeForRegExpSource, isNewTabCommandTab, isFirefoxViewTab, @@ -608,6 +609,34 @@ export default class Tab { return tooltip.join('\n'); } + generateTooltipHtml() { + return this.cookieStoreName ? + `${sanitizeForHTMLText(this.tab.title)}${sanitizeForHTMLText(this.cookieStoreName)}` : + `${sanitizeForHTMLText(this.tab.title)}`; + } + + generateTooltipHtmlWithDescendants() { + return ``; + } + generateTooltipHtmlWithDescendantsInternal() { + let tooltip = `
  • ${this.generateTooltipHtml()}`; + const children = []; + for (const child of this.children) { + if (!child) + continue; + children.push(child.$TST.generateTooltipHtmlWithDescendantsInternal()); + } + if (children.length > 0) + tooltip += ``; + return `${tooltip}
  • `; + } + registerTooltipText(ownerId, text, isHighPriority = false) { if (isHighPriority) { this.highPriorityTooltipTexts.set(ownerId, text); diff --git a/webextensions/resources/tab-preview-frame.js b/webextensions/resources/tab-preview-frame.js index 59d0183b2..e5eb32a9d 100644 --- a/webextensions/resources/tab-preview-frame.js +++ b/webextensions/resources/tab-preview-frame.js @@ -178,6 +178,33 @@ try{ .updating { visibility: hidden; } + + + /* tree */ + .tab-preview-tooltip-text ul, + .tab-preview-tooltip-text ul ul { + margin: 0 0 0 1em; + padding: 0; + list-style: disc; + } + + .tab-preview-tooltip-text .title-line { + display: flex; + flex-direction: row; + max-width: 100%; + white-space: nowrap; + } + .tab-preview-tooltip-text .title-line .title { + overflow: hidden; + text-overflow: ellipsis; + } + .tab-preview-tooltip-text .title-line .cookieStoreName { + display: flex; + margin-left: 1ch; + } + .tab-preview-tooltip-text .title-line .cookieStoreName::before { + content: "- "; + } `; document.head.appendChild(style); @@ -270,7 +297,7 @@ function createPanel() { return panel; } -function updatePanel({ tabId, title, url, tooltipText, hasPreview, previewURL, tabRect, offsetTop, align, scale } = {}) { +function updatePanel({ tabId, title, url, tooltipText, tooltipHtml, hasPreview, previewURL, tabRect, offsetTop, align, scale } = {}) { if (!panel) return; @@ -297,12 +324,12 @@ function updatePanel({ tabId, title, url, tooltipText, hasPreview, previewURL, t const titleElement = panel.querySelector('.tab-preview-title'); const urlElement = panel.querySelector('.tab-preview-url'); const tooltipTextElement = panel.querySelector('.tab-preview-tooltip-text'); - if (typeof tooltipText == 'string') { + if (typeof tooltipHtml == 'string') { if (typeof title == 'string' && tooltipText != title) { titleElement.classList.add('hidden'); urlElement.classList.add('hidden'); - tooltipTextElement.textContent = tooltipText; + tooltipTextElement.innerHTML = tooltipHtml; tooltipTextElement.classList.remove('hidden'); panel.classList.add('extended'); } diff --git a/webextensions/sidebar/components/TabElement.js b/webextensions/sidebar/components/TabElement.js index 238d19b74..8a22a778c 100644 --- a/webextensions/sidebar/components/TabElement.js +++ b/webextensions/sidebar/components/TabElement.js @@ -5,7 +5,8 @@ */ import { - configs + configs, + sanitizeForHTMLText, } from '/common/common.js'; import * as Constants from '/common/constants.js'; import * as TabsStore from '/common/tabs-store.js'; @@ -409,6 +410,8 @@ windowId = ${tab.windowId} this.tooltip = this.$TST.generateTooltipText(); this.tooltipWithDescendants = this.$TST.generateTooltipTextWithDescendants(); + this.tooltipHtml = this.$TST.generateTooltipHtml(); + this.tooltipHtmlWithDescendants = this.$TST.generateTooltipHtmlWithDescendants(); const tooltipText = configs.tabPreviewTooltip ? null : @@ -429,7 +432,7 @@ windowId = ${tab.windowId} const highPriorityTooltipText = this.$TST.getHighPriorityTooltipText(); if (typeof highPriorityTooltipText == 'string') { if (highPriorityTooltipText) - return this.tooltip; + return highPriorityTooltipText; return null; } @@ -455,6 +458,42 @@ windowId = ${tab.windowId} return tooltip; } + get appliedTooltipHtml() { + if (configs.showCollapsedDescendantsByTooltip && + this.$TST.subtreeCollapsed && + this.$TST.hasChild) { + return this.tooltipHtmlWithDescendants; + } + + const highPriorityTooltipText = this.$TST.getHighPriorityTooltipText(); + if (typeof highPriorityTooltipText == 'string') { + if (highPriorityTooltipText) + return sanitizeForHTMLText(highPriorityTooltipText); + + return null; + } + + let tooltip = null; + + const tab = this.$TST.tab; + if (this.classList.contains('faviconized') || + this.overflow || + this.tooltip != tab.title) + tooltip = this.tooltipHtml; + else + tooltip = null; + + const lowPriorityTooltipText = this.$TST.getLowPriorityTooltipText(); + if (typeof lowPriorityTooltipText == 'string' && + !this.getAttribute('title')) { + if (lowPriorityTooltipText) + tooltip = sanitizeForHTMLText(lowPriorityTooltipText); + else + tooltip = null; + } + return tooltip; + } + _initExtraItemsContainers() { if (!this.extraItemsContainerIndentRoot) { this.extraItemsContainerIndentRoot = this.querySelector(`.${Constants.kEXTRA_ITEMS_CONTAINER}.indent`).attachShadow({ mode: 'open' }); diff --git a/webextensions/sidebar/tab-preview-tooltip.js b/webextensions/sidebar/tab-preview-tooltip.js index f81a10667..16ca9faca 100644 --- a/webextensions/sidebar/tab-preview-tooltip.js +++ b/webextensions/sidebar/tab-preview-tooltip.js @@ -303,6 +303,7 @@ async function onTabSubstanceEnter(event) { hoveringTabIds.add(event.target.tab.id); const tooltipText = event.target.appliedTooltipText; + const tooltipHtml = event.target.appliedTooltipHtml; const targetTabId = CUSTOM_PANEL_AVAILABLE_URLS_MATCHER.test(activeTab.url) ? activeTab.id : @@ -349,6 +350,7 @@ async function onTabSubstanceEnter(event) { title: event.target.tab.title, url, tooltipText, + tooltipHtml, hasPreview, timestamp: startAt, // Don't call Date.now() here, because it can become larger than the timestamp on mouseleave. canRetry: !!targetTabId,