diff --git a/src/browser/base/content/ZenUIManager.mjs b/src/browser/base/content/ZenUIManager.mjs index 4c6ac5ed4..c895c0109 100644 --- a/src/browser/base/content/ZenUIManager.mjs +++ b/src/browser/base/content/ZenUIManager.mjs @@ -665,7 +665,7 @@ var gZenVerticalTabsManager = { target.appendChild(child); }, - renameTabKeydown(event) { + async renameTabKeydown(event) { if (event.key === 'Enter') { let label = this._tabEdited.querySelector('.tab-label-container-editing'); let input = this._tabEdited.querySelector('#tab-label-input'); @@ -681,6 +681,10 @@ var gZenVerticalTabsManager = { } else { gBrowser.setTabTitle(this._tabEdited); } + if (this._tabEdited.getAttribute('zen-pin-id')) { + // Update pin title in storage + await gZenPinnedTabManager.updatePinTitle(this._tabEdited, this._tabEdited.label, !!newName); + } // Maybe add some confetti here?!? gZenUIManager.motion.animate( diff --git a/src/browser/base/zen-components/ZenPinnedTabManager.mjs b/src/browser/base/zen-components/ZenPinnedTabManager.mjs index 084cb0ad1..cddcb5b3a 100644 --- a/src/browser/base/zen-components/ZenPinnedTabManager.mjs +++ b/src/browser/base/zen-components/ZenPinnedTabManager.mjs @@ -70,7 +70,7 @@ } if (onInit) { - await this._refreshPinnedTabs(newWorkspace, { init: onInit }); + await this._refreshPinnedTabs({ init: onInit }); } } @@ -98,9 +98,9 @@ return this._enabled; } - async _refreshPinnedTabs(currentWorkspace, { init = false } = {}) { + async _refreshPinnedTabs({ init = false } = {}) { await this._initializePinsCache(); - await this._initializePinnedTabs(init, currentWorkspace); + await this._initializePinnedTabs(init); } async _initializePinsCache() { @@ -141,7 +141,7 @@ return this._pinsCache; } - async _initializePinnedTabs(init = false, currentWorkspace) { + async _initializePinnedTabs(init = false) { const pins = this._pinsCache; if (!pins?.length) { return; @@ -213,6 +213,10 @@ newTab.setAttribute('zen-essential', 'true'); } + if (pin.editedTitle) { + newTab.setAttribute('zen-has-static-label', 'true'); + } + // Initialize browser state if needed if (!newTab.linkedBrowser._remoteAutoRemoved) { let state = { @@ -349,7 +353,7 @@ await ZenPinnedTabsStorage.savePin(pin); const currentWorkspace = await ZenWorkspaces.getActiveWorkspace(); - await this._refreshPinnedTabs(currentWorkspace); + await this._refreshPinnedTabs(); } async _setPinnedAttributes(tab) { @@ -386,7 +390,7 @@ return; } const currentWorkspace = await ZenWorkspaces.getActiveWorkspace(); - await this._refreshPinnedTabs(currentWorkspace); + await this._refreshPinnedTabs(); } async _removePinnedAttributes(tab, isClosing = false) { @@ -411,7 +415,7 @@ } } const currentWorkspace = await ZenWorkspaces.getActiveWorkspace(); - await this._refreshPinnedTabs(currentWorkspace); + await this._refreshPinnedTabs(); } _initClosePinnedTabShortcut() { @@ -707,6 +711,33 @@ return document.documentElement.getAttribute('zen-sidebar-expanded') === 'true'; } + async updatePinTitle(tab, newTitle, isEdited = true, notifyObservers = true) { + const uuid = tab.getAttribute('zen-pin-id'); + await ZenPinnedTabsStorage.updatePinTitle(uuid, newTitle, isEdited, notifyObservers); + + await this._refreshPinnedTabs(); + + const browsers = Services.wm.getEnumerator('navigator:browser'); + + // update the label for the same pin across all windows + for (const browser of browsers) { + const tabs = browser.gBrowser.tabs; + for (let i = 0; i < tabs.length; i++) { + const tabToEdit = tabs[i]; + if (tabToEdit.getAttribute('zen-pin-id') === uuid && tabToEdit !== tab) { + tabToEdit.removeAttribute('zen-has-static-label'); + if (isEdited) { + gBrowser._setTabLabel(tabToEdit, newTitle); + tabToEdit.setAttribute('zen-has-static-label', 'true'); + } else { + gBrowser.setTabTitle(tabToEdit); + } + break; + } + } + } + } + applyDragoverClass(event, draggedTab) { const pinnedTabsTarget = event.target.closest('#vertical-pinned-tabs-container'); const essentialTabsTarget = event.target.closest('#zen-essentials-container'); diff --git a/src/browser/base/zen-components/ZenPinnedTabsStorage.mjs b/src/browser/base/zen-components/ZenPinnedTabsStorage.mjs index b47850b4e..02bbb1aac 100644 --- a/src/browser/base/zen-components/ZenPinnedTabsStorage.mjs +++ b/src/browser/base/zen-components/ZenPinnedTabsStorage.mjs @@ -24,6 +24,19 @@ var ZenPinnedTabsStorage = { ) `); + const columns = await db.execute(`PRAGMA table_info(zen_pins)`); + const columnNames = columns.map((row) => row.getResultByName('name')); + + // Helper function to add column if it doesn't exist + const addColumnIfNotExists = async (columnName, definition) => { + if (!columnNames.includes(columnName)) { + await db.execute(`ALTER TABLE zen_pins ADD COLUMN ${columnName} ${definition}`); + } + }; + + // Add edited_title column if it doesn't exist + await addColumnIfNotExists('edited_title', 'BOOLEAN NOT NULL DEFAULT 0'); + // Create indices await db.execute(` CREATE INDEX IF NOT EXISTS idx_zen_pins_uuid ON zen_pins(uuid) @@ -152,6 +165,7 @@ var ZenPinnedTabsStorage = { isEssential: Boolean(row.getResultByName('is_essential')), isGroup: Boolean(row.getResultByName('is_group')), parentUuid: row.getResultByName('parent_uuid'), + editedTitle: Boolean(row.getResultByName('edited_title')), })); }, @@ -176,6 +190,7 @@ var ZenPinnedTabsStorage = { isEssential: Boolean(row.getResultByName('is_essential')), isGroup: Boolean(row.getResultByName('is_group')), parentUuid: row.getResultByName('parent_uuid'), + editedTitle: Boolean(row.getResultByName('edited_title')), })); }, @@ -351,6 +366,60 @@ var ZenPinnedTabsStorage = { this._notifyPinsChanged('zen-pin-updated', Array.from(changedUUIDs)); }, + async updatePinTitle(uuid, newTitle, isEdited = true, notifyObservers = true) { + if (!uuid || typeof newTitle !== 'string') { + throw new Error('Invalid parameters: uuid and newTitle are required'); + } + + const changedUUIDs = new Set(); + + await PlacesUtils.withConnectionWrapper('ZenPinnedTabsStorage.updatePinTitle', async (db) => { + await db.executeTransaction(async () => { + const now = Date.now(); + + // Update the pin's title and edited_title flag + const result = await db.execute( + ` + UPDATE zen_pins + SET title = :newTitle, + edited_title = :isEdited, + updated_at = :now + WHERE uuid = :uuid + `, + { + uuid, + newTitle, + isEdited, + now, + } + ); + + // Only proceed with change tracking if a row was actually updated + if (result.rowsAffected > 0) { + changedUUIDs.add(uuid); + + // Record the change + await db.execute( + ` + INSERT OR REPLACE INTO zen_pins_changes (uuid, timestamp) + VALUES (:uuid, :timestamp) + `, + { + uuid, + timestamp: Math.floor(now / 1000), + } + ); + + await this.updateLastChangeTimestamp(db); + } + }); + }); + + if (notifyObservers && changedUUIDs.size > 0) { + this._notifyPinsChanged('zen-pin-updated', Array.from(changedUUIDs)); + } + }, + async __dropTables() { await PlacesUtils.withConnectionWrapper('ZenPinnedTabsStorage.__dropTables', async (db) => { await db.execute(`DROP TABLE IF EXISTS zen_pins`); diff --git a/src/browser/base/zen-components/ZenWorkspaces.mjs b/src/browser/base/zen-components/ZenWorkspaces.mjs index f9900b2c7..a038d6af9 100644 --- a/src/browser/base/zen-components/ZenWorkspaces.mjs +++ b/src/browser/base/zen-components/ZenWorkspaces.mjs @@ -154,7 +154,7 @@ var ZenWorkspaces = new (class extends ZenMultiWindowFeature { } get tabboxChildren() { - return this.activeWorkspaceStrip.children; + return this.activeWorkspaceStrip?.children || []; } get pinnedTabsContainer() {