diff --git a/browser/base/content/browser.xhtml b/browser/base/content/browser.xhtml
index 351b28291bf1..e31e82a8f8ed 100644
--- a/browser/base/content/browser.xhtml
+++ b/browser/base/content/browser.xhtml
@@ -100,6 +100,7 @@
+
diff --git a/browser/components/tabbrowser/SmartTabGrouping.sys.mjs b/browser/components/tabbrowser/SmartTabGrouping.sys.mjs
index da8c91bd330f..f8a7421c6b60 100644
--- a/browser/components/tabbrowser/SmartTabGrouping.sys.mjs
+++ b/browser/components/tabbrowser/SmartTabGrouping.sys.mjs
@@ -252,7 +252,7 @@ export class SmartTabGroupingManager {
* This function will terminate a grouping or label generation in progress
* It is currently not implemented.
*/
- terminateProcesses() {
+ terminateProcess() {
// TODO - teminate AI processes, This method will be
// called when tab grouping panel is closed.
}
@@ -382,13 +382,11 @@ export class SmartTabGroupingManager {
) {
this.embeddingEngine = await this._createMLEngine(this.config.embedding);
}
- const result = [];
- for (let runArg of inputData.inputArgs) {
- const request = { args: [[runArg]], options: inputData.runOptions };
- let embeddingsPerTab = await this.embeddingEngine.run(request);
- result.push(Object.values(embeddingsPerTab.ort_tensor.cpuData));
- }
- return result;
+ const request = {
+ args: [inputData.inputArgs],
+ options: inputData.runOptions,
+ };
+ return await this.embeddingEngine.run(request);
}
/**
diff --git a/browser/components/tabbrowser/content/tabbrowser.js b/browser/components/tabbrowser/content/tabbrowser.js
index 49e8180ac1e4..5d136fa732dc 100644
--- a/browser/components/tabbrowser/content/tabbrowser.js
+++ b/browser/components/tabbrowser/content/tabbrowser.js
@@ -99,6 +99,7 @@
this.tabContainer = document.getElementById("tabbrowser-tabs");
this.tabGroupMenu = document.getElementById("tab-group-editor");
this.tabbox = document.getElementById("tabbrowser-tabbox");
+ this.tabGroupNameField = document.getElementById("tab-group-name");
this.tabpanels = document.getElementById("tabbrowser-tabpanels");
this.verticalPinnedTabsContainer = document.getElementById(
"vertical-pinned-tabs-container"
@@ -2948,6 +2949,13 @@
return null;
}
+ gBrowser.getGroupTitleForTabs(tabs).then(newLabel => {
+ group.label = newLabel;
+ if (this.tabGroupMenu.panel.state !== "closed") {
+ this.tabGroupNameField.value = newLabel;
+ }
+ });
+
group.dispatchEvent(
new CustomEvent("TabGroupCreate", {
bubbles: true,
diff --git a/browser/components/tabbrowser/content/tabgroup-menu.js b/browser/components/tabbrowser/content/tabgroup-menu.js
index 7c435ddb5144..570244e0d3bc 100644
--- a/browser/components/tabbrowser/content/tabgroup-menu.js
+++ b/browser/components/tabbrowser/content/tabgroup-menu.js
@@ -30,15 +30,8 @@
"gray",
"red",
];
- static markup = `
-
+ static headerSection = /*html*/ `
+ `;
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ static editActions = /*html*/ `
-
+
@@ -129,7 +85,163 @@
data-l10n-id="tab-group-editor-action-delete">
+ `;
+ static suggestionsHeader = /*html*/ `
+
+
+
+
+ `;
+
+ static suggestionsSection = /*html*/ `
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ `;
+
+ static suggestionsButton = /*html*/ `
+
+
+ `;
+
+ static loadingSection = /*html*/ `
+
+
+
+
+
+
+ `;
+
+ static defaultActions = /*html*/ `
+
+
+
+
+
+
+ `;
+
+ static loadingActions = /*html*/ `
+
+
+
+
+ `;
+
+ static markup = /*html*/ `
+
+
+
+ ${this.headerSection}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ${this.editActions}
+
+
+
+ ${this.suggestionsButton}
+
+
+
+
+
+
+
+
+ ${this.defaultActions}
+
+
+
+ ${this.suggestionsHeader}
+ ${this.loadingSection}
+ ${this.loadingActions}
+ ${this.suggestionsSection}
+
`;
@@ -158,6 +270,7 @@
ERROR: 11,
};
+ #tabGroupMain;
#activeGroup;
#cancelButton;
#createButton;
@@ -167,7 +280,25 @@
#panel;
#swatches;
#swatchesContainer;
+ #defaultActions;
#suggestionState = MozTabbrowserTabGroupMenu.State.CREATE_STANDARD_INITIAL;
+ #suggestionsHeading;
+ #suggestionsHeader;
+ #suggestionsContainer;
+ #suggestions;
+ #suggestionButton;
+ #cancelSuggestionsButton;
+ #createSuggestionsButton;
+ #suggestionsLoading;
+ #selectSuggestionsToggle;
+ #suggestionsMessage;
+ #suggestionsDisclaimer;
+ #selectedSuggestedTabs = [];
+ #suggestedTabs = [];
+ #suggestionsLoadActions;
+ #suggestionsLoadCancel;
+ #suggestionsSeparator;
+ #smartTabGroupingManager;
constructor() {
super();
@@ -195,6 +326,11 @@
this.#swatchesContainer = this.querySelector(
".tab-group-editor-swatches"
);
+
+ this.#defaultActions = this.querySelector("#tab-group-default-actions");
+ this.#tabGroupMain = this.querySelector("#tab-group-main");
+ this.#initSuggestions();
+
this.#populateSwatches();
this.#cancelButton.addEventListener("click", () => {
@@ -211,6 +347,22 @@
}
});
+ /**
+ * Check if the smart suggest button should be shown
+ * If there are no ungrouped tabs, the button should be hidden
+ */
+ this.canShowAIUserInterface = () => {
+ const { tabs } = gBrowser;
+ let show = false;
+ tabs.forEach(tab => {
+ if (tab.group === null) {
+ show = true;
+ }
+ });
+
+ return show;
+ };
+
document
.getElementById("tabGroupEditor_addNewTabInGroup")
.addEventListener("command", () => {
@@ -247,6 +399,78 @@
this.#swatchesContainer.addEventListener("change", this);
}
+ #initSuggestions() {
+ const AI_ICON = "chrome://global/skin/icons/highlights.svg";
+ const { SmartTabGroupingManager } = ChromeUtils.importESModule(
+ "resource:///modules/SmartTabGrouping.sys.mjs"
+ );
+ this.#smartTabGroupingManager = new SmartTabGroupingManager();
+
+ // Init Suggestion Button
+ this.#suggestionButton = this.querySelector(
+ "#tab-group-suggestion-button"
+ );
+ this.#suggestionButton.iconSrc = AI_ICON;
+ this.#suggestionButton.addEventListener("click", () => {
+ this.#handleSmartSuggest();
+ });
+
+ // Init Suggestions UI
+ this.#suggestionsHeading = this.querySelector(
+ "#tab-group-suggestions-heading"
+ );
+ this.#suggestionsHeader = this.querySelector(
+ "#tab-group-suggestions-header"
+ );
+ this.#suggestionsContainer = this.querySelector(
+ "#tab-group-suggestions-container"
+ );
+ this.#suggestions = this.querySelector("#tab-group-suggestions");
+ this.#selectSuggestionsToggle = this.querySelector(
+ "#tab-group-select-toggle"
+ );
+ this.#selectSuggestionsToggle.addEventListener("click", () => {
+ this.#handleSelectToggle();
+ });
+ this.#suggestionsMessage = this.querySelector(
+ "#tab-group-suggestions-message"
+ );
+ this.#suggestionsMessage.iconSrc = AI_ICON;
+ this.#suggestionsDisclaimer = this.querySelector(
+ "#tab-group-suggestions-disclaimer"
+ );
+ this.#createSuggestionsButton = this.querySelector(
+ "#tab-group-create-suggestions-button"
+ );
+ this.#createSuggestionsButton.addEventListener("click", () => {
+ this.activeGroup.addTabs(this.#selectedSuggestedTabs);
+ this.close(true);
+ });
+ this.#cancelSuggestionsButton = this.querySelector(
+ "#tab-group-cancel-suggestions-button"
+ );
+ this.#cancelSuggestionsButton.addEventListener("click", () => {
+ this.close();
+ });
+ this.#suggestionsSeparator = this.querySelector(
+ "#tab-group-suggestions-separator"
+ );
+
+ // Init Loading UI
+ this.#suggestionsLoading = this.querySelector(
+ "#tab-group-suggestions-loading"
+ );
+ this.#suggestionsLoadActions = this.querySelector(
+ "#tab-group-suggestions-load-actions"
+ );
+ this.#suggestionsLoadCancel = this.querySelector(
+ "#tab-group-suggestions-load-cancel"
+ );
+ this.#suggestionsLoadCancel.addEventListener("click", () => {
+ this.#handleLoadSuggestionsCancel();
+ });
+ }
+
#populateSwatches() {
this.#clearSwatches();
for (let colorCode of MozTabbrowserTabGroupMenu.COLORS) {
@@ -348,11 +572,10 @@
openCreateModal(group) {
this.activeGroup = group;
this.createMode = true;
- this.suggestionState =
- MozTabbrowserTabGroupMenu.State.CREATE_STANDARD_INITIAL;
- if (lazy.smartTabGroupsEnabled) {
- //TODO: set appropriate state
- }
+ this.suggestionState = lazy.smartTabGroupsEnabled
+ ? MozTabbrowserTabGroupMenu.State.CREATE_AI_INITIAL
+ : MozTabbrowserTabGroupMenu.State.CREATE_STANDARD_INITIAL;
+
this.#panel.openPopup(group.firstChild, {
position: this.#panelPosition,
});
@@ -361,11 +584,10 @@
openEditModal(group) {
this.activeGroup = group;
this.createMode = false;
- this.suggestionState =
- MozTabbrowserTabGroupMenu.State.EDIT_STANDARD_INITIAL;
- if (lazy.smartTabGroupsEnabled) {
- //TODO: set appropriate state
- }
+ this.suggestionState = lazy.smartTabGroupsEnabled
+ ? MozTabbrowserTabGroupMenu.State.EDIT_AI_INITIAL
+ : MozTabbrowserTabGroupMenu.State.EDIT_STANDARD_INITIAL;
+
this.#panel.openPopup(group.firstChild, {
position: this.#panelPosition,
});
@@ -419,6 +641,7 @@
}
}
this.activeGroup = null;
+ this.#smartTabGroupingManager.terminateProcess();
}
on_keypress(event) {
@@ -487,8 +710,192 @@
this.#renderSuggestionState();
}
+ #handleLoadSuggestionsCancel() {
+ // TODO look into actually canceling any processes
+ this.suggestionState = this.createMode
+ ? MozTabbrowserTabGroupMenu.State.CREATE_AI_INITIAL
+ : MozTabbrowserTabGroupMenu.State.EDIT_AI_INITIAL;
+ }
+
+ #handleSelectToggle() {
+ const currentState =
+ this.#selectSuggestionsToggle.getAttribute("data-state");
+ const isDeselect = currentState === "deselect";
+
+ isDeselect ? this.#handleDeselectAll() : this.#handleSelectAll();
+ const newState = isDeselect ? "select" : "deselect";
+ this.#setSelectToggleState(newState);
+ }
+
+ #handleSelectAll() {
+ document
+ .querySelectorAll(".tab-group-suggestion-checkbox")
+ .forEach(checkbox => {
+ checkbox.checked = true;
+ });
+ // Reset selected tabs to all suggested tabs
+ this.#selectedSuggestedTabs = this.#suggestedTabs;
+ }
+
+ #handleDeselectAll() {
+ document
+ .querySelectorAll(".tab-group-suggestion-checkbox")
+ .forEach(checkbox => {
+ checkbox.checked = false;
+ });
+ this.#selectedSuggestedTabs = [];
+ }
+
+ async #handleSmartSuggest() {
+ // Loading
+ this.suggestionState = MozTabbrowserTabGroupMenu.State.LOADING;
+ const tabs = await this.#smartTabGroupingManager.smartTabGroupingForGroup(
+ this.activeGroup,
+ gBrowser.tabs
+ );
+
+ if (!tabs.length) {
+ // No un-grouped tabs found
+ this.suggestionState = this.#createMode
+ ? MozTabbrowserTabGroupMenu.State.CREATE_AI_WITH_NO_SUGGESTIONS
+ : MozTabbrowserTabGroupMenu.State.EDIT_AI_WITH_NO_SUGGESTIONS;
+ return;
+ }
+
+ this.#selectedSuggestedTabs = tabs;
+ this.#suggestedTabs = tabs;
+ tabs.forEach((tab, index) => {
+ this.#createRow(tab, index);
+ });
+
+ this.suggestionState = this.#createMode
+ ? MozTabbrowserTabGroupMenu.State.CREATE_AI_WITH_SUGGESTIONS
+ : MozTabbrowserTabGroupMenu.State.EDIT_AI_WITH_SUGGESTIONS;
+ }
+
+ #createRow(tab, index) {
+ // Create Row
+ let row = document.createXULElement("toolbaritem");
+ row.setAttribute("context", "tabContextMenu");
+ row.setAttribute("id", `tab-bar-${index}`);
+
+ // Create Checkbox
+ let checkbox = document.createXULElement("checkbox");
+ checkbox.value = tab;
+ checkbox.setAttribute("checked", true);
+ checkbox.classList.add("tab-group-suggestion-checkbox");
+ checkbox.addEventListener("CheckboxStateChange", e => {
+ const isChecked = e.target.checked;
+ const currentTab = e.target.value;
+
+ if (isChecked) {
+ this.#selectedSuggestedTabs.push(currentTab);
+ } else {
+ this.#selectedSuggestedTabs = this.#selectedSuggestedTabs.filter(
+ t => t != currentTab
+ );
+ }
+ });
+
+ row.appendChild(checkbox);
+
+ // Create Row Label
+ let label = document.createXULElement("toolbarbutton");
+ label.classList.add(
+ "all-tabs-button",
+ "subviewbutton",
+ "subviewbutton-iconic",
+ "tab-group-suggestion-label"
+ );
+ label.setAttribute("flex", "1");
+ label.setAttribute("crop", "end");
+ label.label = tab.label;
+ label.image = tab.image;
+ label.disabled = true;
+ row.appendChild(label);
+
+ // Apply Row to Suggestions
+ this.#suggestions.appendChild(row);
+ }
+
+ /**
+ * Set the state of the select toggle button
+ * @param {string} "select" | "deselect"
+ */
+ #setSelectToggleState(state) {
+ this.#selectSuggestionsToggle.setAttribute("data-state", state);
+ this.#selectSuggestionsToggle.setAttribute(
+ "data-l10n-id",
+ `tab-group-editor-${state}-suggestions`
+ );
+ }
+
+ /**
+ * Element visibility utility function.
+ * Toggles the `hidden` attribute of a DOM element.
+ *
+ * @param {HTMLElement|XULElement} element - The DOM element to show/hide.
+ * @param {boolean} shouldShow - Whether the element should be shown (true) or hidden (false).
+ */
+ #setElementVisibility(element, shouldShow) {
+ element.hidden = !shouldShow;
+ }
+
+ #showDefaultTabGroupActions(value) {
+ this.#setElementVisibility(this.#defaultActions, value);
+ }
+
+ #showSmartSuggestionsContainer(value) {
+ this.#setElementVisibility(this.#suggestionsContainer, value);
+ }
+
+ #showSuggestionButton(value) {
+ this.#setElementVisibility(this.#suggestionButton, value);
+ }
+
+ #showSuggestionMessage(value) {
+ this.#setElementVisibility(this.#suggestionsMessage, value);
+ }
+
+ #showSuggestionsDisclaimer(value) {
+ this.#setElementVisibility(this.#suggestionsDisclaimer, value);
+ }
+
+ #showSuggestionsSeparator(value) {
+ this.#setElementVisibility(this.#suggestionsSeparator, value);
+ }
+
+ #setLoadingState(value) {
+ this.#setElementVisibility(this.#suggestionsLoadActions, value);
+ this.#setElementVisibility(this.#suggestionsLoading, value);
+ }
+
+ #setSuggestionsButtonCreateModeState(value) {
+ const translationString = value
+ ? "tab-group-editor-smart-suggest-button-create"
+ : "tab-group-editor-smart-suggest-button-edit";
+
+ this.#suggestionButton.setAttribute("data-l10n-id", translationString);
+ }
+
+ /**
+ * Unique state setter for a "3rd" panel state while in Edit Mode
+ * that just shows suggestions and hides the majority of the panel
+ * @param {boolean} value
+ */
+ #setEditModeSuggestionState(value) {
+ this.#setElementVisibility(this.#suggestionsHeader, !value);
+ this.#setElementVisibility(this.#tabGroupMain, !value);
+ this.#setElementVisibility(this.#suggestionsHeading, value);
+ }
+
#resetCommonUI() {
- // TODO - has commun UI reset logic
+ this.#setLoadingState(false);
+ this.#setEditModeSuggestionState(false);
+ this.#suggestedTabs = [];
+ this.#selectedSuggestedTabs = [];
+ this.#suggestions.innerHTML = "";
+ this.#showSmartSuggestionsContainer(false);
}
#renderSuggestionState() {
@@ -496,64 +903,111 @@
// CREATE STANDARD INITIAL
case MozTabbrowserTabGroupMenu.State.CREATE_STANDARD_INITIAL:
this.#resetCommonUI();
- //TODO
+ this.#showDefaultTabGroupActions(true);
+ this.#showSuggestionButton(false);
+ this.#showSuggestionMessage(false);
+ this.#showSuggestionsDisclaimer(false);
+ this.#showSuggestionsSeparator(false);
break;
//CREATE AI INITIAL
case MozTabbrowserTabGroupMenu.State.CREATE_AI_INITIAL:
this.#resetCommonUI();
- //TODO
+ this.#showSuggestionButton(true);
+ this.#showDefaultTabGroupActions(true);
+ this.#showSuggestionMessage(false);
+ this.#setSelectToggleState("deselect");
+ this.#showSuggestionsDisclaimer(true);
+ this.#setSuggestionsButtonCreateModeState(true);
+ this.#showSuggestionsSeparator(true);
break;
// CREATE AI INITIAL SUGGESTIONS DISABLED
case MozTabbrowserTabGroupMenu.State
.CREATE_AI_INITIAL_SUGGESTIONS_DISABLED:
this.#resetCommonUI();
- //TODO
+ this.#showSuggestionButton(false);
+ this.#showSuggestionsDisclaimer(false);
+ this.#showSuggestionMessage(true);
+ this.#showDefaultTabGroupActions(true);
+ this.#showSuggestionsSeparator(true);
break;
// CREATE AI WITH SUGGESTIONS
case MozTabbrowserTabGroupMenu.State.CREATE_AI_WITH_SUGGESTIONS:
- //TODO
+ this.#setLoadingState(false);
+ this.#showSmartSuggestionsContainer(true);
+ this.#showSuggestionButton(false);
+ this.#showSuggestionsSeparator(true);
+ this.$showDefaultTabGroupActions(false);
break;
// CREATE AI WITH NO SUGGESTIONS
case MozTabbrowserTabGroupMenu.State.CREATE_AI_WITH_NO_SUGGESTIONS:
- //TODO
+ this.#setLoadingState(false);
+ this.#showSuggestionMessage(true);
+ this.#showDefaultTabGroupActions(true);
+ this.#showSuggestionButton(false);
+ this.#showSuggestionsSeparator(true);
break;
// EDIT STANDARD INITIAL
case MozTabbrowserTabGroupMenu.State.EDIT_STANDARD_INITIAL:
this.#resetCommonUI();
- //TODO
+ this.#showSuggestionButton(false);
+ this.#showSuggestionMessage(false);
+ this.#showDefaultTabGroupActions(false);
+ this.#showSuggestionsDisclaimer(false);
+ this.#showSuggestionsSeparator(false);
break;
// EDIT AI INITIAL
case MozTabbrowserTabGroupMenu.State.EDIT_AI_INITIAL:
this.#resetCommonUI();
- //TODO
+ this.#showSuggestionMessage(false);
+ this.#setSelectToggleState("deselect");
+ this.#showSuggestionButton(true);
+ this.#showDefaultTabGroupActions(false);
+ this.#showSuggestionsDisclaimer(false);
+ this.#setSuggestionsButtonCreateModeState(false);
+ this.#showSuggestionsSeparator(true);
break;
// EDIT AI INITIAL SUGGESTIONS DISABLED
case MozTabbrowserTabGroupMenu.State
.EDIT_AI_INITIAL_SUGGESTIONS_DISABLED:
this.#resetCommonUI();
- //TODO
+ this.#showSuggestionMessage(true);
+ this.#showSuggestionButton(false);
+ this.#showDefaultTabGroupActions(false);
+ this.#showSuggestionsDisclaimer(false);
+ this.#showSuggestionsSeparator(true);
break;
// EDIT AI WITH SUGGESTIONS
case MozTabbrowserTabGroupMenu.State.EDIT_AI_WITH_SUGGESTIONS:
- //TODO
+ this.#setLoadingState(false);
+ this.#showSmartSuggestionsContainer(true);
+ this.#setEditModeSuggestionState(true);
+ this.#showSuggestionsSeparator(false);
break;
// EDIT AI WITH NO SUGGESTIONS
case MozTabbrowserTabGroupMenu.State.EDIT_AI_WITH_NO_SUGGESTIONS:
- //TODO
+ this.#setLoadingState(false);
+ this.#showSuggestionMessage(true);
+ this.#showSuggestionsSeparator(true);
break;
// LOADING
case MozTabbrowserTabGroupMenu.State.LOADING:
- //TODO
+ this.#showDefaultTabGroupActions(false);
+ this.#showSuggestionButton(false);
+ this.#showSuggestionsDisclaimer(false);
+ this.#showSuggestionMessage(false);
+ this.#setLoadingState(true);
+ this.#showSuggestionsSeparator(true);
+ this.#showDefaultTabGroupActions(false);
break;
// ERROR
diff --git a/browser/locales-preview/smartTabGroups.ftl b/browser/locales-preview/smartTabGroups.ftl
new file mode 100644
index 000000000000..da4e202612f4
--- /dev/null
+++ b/browser/locales-preview/smartTabGroups.ftl
@@ -0,0 +1,28 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+tab-group-editor-title-suggest = Suggested tabs
+
+tab-group-editor-smart-suggest-button-edit=
+ .label = Suggest more tabs for group
+
+# Button appearing during creation of new tab group.
+# Clicking will suggest tabs to group together in the tab group.
+tab-group-editor-smart-suggest-button-create=
+ .label = Suggest tabs to group
+
+tab-group-editor-suggestions-header = Suggested Tabs
+
+tab-group-suggestions-loading-header = Suggested Tabs
+
+tab-group-editor-deselect-suggestions =
+ .label = Deselect all
+
+tab-group-editor-select-suggestions =
+ .label = Select all
+
+tab-group-editor-no-tabs-found =
+ .label = As you open similar tabs, { -brand-short-name } will suggest them for this group.
+
+tab-group-suggestions-disclaimer = { -brand-short-name } uses AI to suggest tabs and group names. Some suggestions may be inaccurate.
diff --git a/browser/locales/jar.mn b/browser/locales/jar.mn
index 4eebca6ddd1e..7de950268c81 100644
--- a/browser/locales/jar.mn
+++ b/browser/locales/jar.mn
@@ -17,6 +17,7 @@
preview/credentialChooser.ftl (../../toolkit/components/credentialmanagement/credentialChooser.ftl)
browser (%browser/**/*.ftl)
preview/backupSettings.ftl (../locales-preview/backupSettings.ftl)
+ preview/smartTabGroups.ftl (../locales-preview/smartTabGroups.ftl)
preview/tabUnload.ftl (../locales-preview/tabUnload.ftl)
@AB_CD@.jar:
diff --git a/browser/themes/shared/tabbrowser/tabs.css b/browser/themes/shared/tabbrowser/tabs.css
index a68a2ba03193..4d828b4bb6c5 100644
--- a/browser/themes/shared/tabbrowser/tabs.css
+++ b/browser/themes/shared/tabbrowser/tabs.css
@@ -6,6 +6,8 @@
--tabstrip-inner-border: 1px solid color-mix(in srgb, currentColor 25%, transparent);
--tabstrip-min-height: calc(var(--tab-min-height) + 2 * var(--tab-block-margin));
--tab-min-height: 36px;
+ --tab-group-suggestions-loading-animation-color-1: color-mix(in srgb, currentColor 5%, transparent);
+ --tab-group-suggestions-loading-animation-color-2: color-mix(in srgb, currentColor 35%, transparent);
&[uidensity=compact] {
--tab-min-height: 29px;
}
@@ -1690,3 +1692,110 @@ toolbar:not(#TabsToolbar) #firefox-view-button {
fill: transparent;
stroke: light-dark(var(--tab-group-color), var(--tab-group-color-invert));
}
+
+.tab-group-suggestion-label {
+ --text-color-disabled: var(--panel-color);
+ --button-opacity-disabled: 1;
+}
+
+#tab-group-suggestions-heading,
+#tab-group-main {
+ flex-direction: column;
+}
+
+#tab-group-default-actions,
+#tab-group-suggestions-load-actions,
+#tab-group-suggestion-button,
+#tab-group-suggestions-message {
+ display: none;
+}
+
+#tab-group-suggestions-heading:not([hidden]),
+#tab-group-main:not([hidden]),
+#tab-group-suggestions-separator:not([hidden]),
+#tab-group-suggestions-load-actions:not([hidden]),
+#tab-group-suggestions-loading:not([hidden]),
+#tab-group-default-actions:not([hidden]),
+#tab-group-suggestion-button:not([hidden]),
+#tab-group-suggestions-message:not([hidden]) {
+ display: flex;
+}
+
+#tab-group-suggestions-container:not([hidden]) {
+ display: block;
+}
+
+#tab-group-suggestions-disclaimer > a {
+ display: inline;
+ color: inherit;
+}
+
+#tab-group-suggestions-disclaimer:not([hidden]) {
+ display: initial;
+}
+
+#tab-group-select-toggle {
+ position: relative;
+ margin-top: 0.5em;
+}
+
+#tab-group-suggestion-button {
+ color: var(--color-accent-primary);
+ --button-font-weight: var(--font-weight-normal);
+}
+
+#tab-group-suggestions-loading {
+ gap: 4px;
+ flex-direction: column;
+ margin: 0 8px 8px;
+}
+
+#tab-group-suggestions-message {
+ font-size: 0.85em;
+ --button-font-weight: var(--font-weight-normal);
+}
+
+.tab-group-suggestions-loading-header,
+#tab-group-suggestions-header {
+ margin: 8px 0;
+}
+
+@keyframes tab-group-loading-block-animation {
+ 0% {
+ background-position: 200% 0;
+ }
+
+ 50% {
+ background-position: 0 0;
+ }
+
+ 100% {
+ background-position: -200% 0;
+ }
+}
+
+.tab-group-suggestions-loading-block:nth-of-type(2) {
+ animation-delay: 0.1s;
+}
+.tab-group-suggestions-loading-block:nth-of-type(3) {
+ animation-delay: 0.2s;
+}
+.tab-group-suggestions-loading-block:nth-of-type(4) {
+ animation-delay: 0.3s;
+}
+
+.tab-group-suggestions-loading-block {
+ animation: tab-group-loading-block-animation 3s infinite;
+ background: linear-gradient(100deg,
+ color-mix(in srgb, var(--tab-group-suggestions-loading-animation-color-2), transparent 60%) 30%,
+ var(--tab-group-suggestions-loading-animation-color-1) 50%,
+ color-mix(in srgb, var(--tab-group-suggestions-loading-animation-color-2), transparent 60%) 70%);
+ background-size: 200% 100%;
+ border-radius: var(--border-radius-small);
+ height: 1.5em;
+ margin: 0;
+ margin-bottom: 0.5em;
+ padding: 0;
+ white-space: nowrap;
+ width: 100%;
+}