### Manage Bookmarks with DropdownMenu and GitHub Sync (JavaScript) Source: https://context7.com/sandeberger/thetab.ninja/llms.txt Initializes a DropdownMenu for managing bookmark pages. Handles page creation, deletion, editing, selection, and synchronization with GitHub. Dependencies include `DropdownMenu`, `createNewPage`, `switchPage`, `deletePage`, `editPage`, `synchronizeWithGitHub`, `isGitHubConfigValid`, `generateUUID`, `pushToGitHub`, `synchronizeGlobalSettings`, `fetchFromGitHub`, `renderCollections`, and `saveToLocalStorage`. It takes page data as input and outputs updated global settings and bookmark collections. ```javascript const dropdown = new DropdownMenu(container, { onCreate: async (item) => { console.log("Creating new page:", item); const newPage = await createNewPage(item.name); if (newPage) { await switchPage(newPage.guid); } }, onDelete: async (item) => { await deletePage(item.guid); }, onEdit: async (item) => { await editPage(item.guid, item.name); }, onSelect: async (item) => { await switchPage(item.guid); }, onButtonClick: () => { synchronizeWithGitHub(); } }); async function createNewPage(name) { if (!isGitHubConfigValid()) { alert('Please configure GitHub settings first'); return null; } try { const guid = generateUUID(); const filepath = `${guid}.json`; const emptyPageData = { collections: [], lastModified: Date.now() }; await pushToGitHub(emptyPageData, filepath); const newPage = { guid: guid, name: name, filepath: filepath, lastModified: Date.now() }; globalSettings.pages.push(newPage); await synchronizeGlobalSettings(); return newPage; } catch (error) { console.error('Failed to create new page:', error); return null; } } async function switchPage(pageGuid) { const currentPage = globalSettings.pages.find(p => p.guid === globalSettings.activePage); const newPage = globalSettings.pages.find(p => p.guid === pageGuid); if (!newPage) { throw new Error('Selected page not found'); } await synchronizeWithGitHub(); globalSettings.activePage = pageGuid; await synchronizeGlobalSettings(); bookmarkManagerData.collections = []; bookmarkManagerData.githubConfig.filepath = newPage.filepath; try { const pageData = await fetchFromGitHub(newPage.filepath); if (pageData && pageData.collections) { bookmarkManagerData.collections = pageData.collections.map(enrichCollection); } } catch (error) { console.error('Error loading page data:', error); } renderCollections(); saveToLocalStorage(); } ``` -------------------------------- ### GitHub Synchronization API Source: https://context7.com/sandeberger/thetab.ninja/llms.txt Fetches bookmark data from a GitHub repository using the GitHub API. This function handles authentication, retrieves file content, and manages large files through multiple API endpoints (Contents API, Blobs API, and raw download). ```APIDOC ## GitHub Synchronization API ### Description Fetches bookmark data from a GitHub repository using the GitHub API. ### Method POST (Internal Chrome Runtime Message) ### Endpoint chrome.runtime.sendMessage ### Parameters #### Request Body - **action** (string) - Required - The action to perform, 'fetchFromGitHub'. - **config** (object) - Required - Configuration for GitHub access. - **username** (string) - Required - GitHub username. - **repo** (string) - Required - GitHub repository name. - **pat** (string) - Required - GitHub Personal Access Token. - **filepath** (string) - Required - Path to the bookmark file in the repository. ### Request Example ```json { "action": "fetchFromGitHub", "config": { "username": "myusername", "repo": "bookmarks", "pat": "github_pat_xxxxx", "filepath": "bookmarks.json" } } ``` ### Response #### Success Response (200) - **content** (object) - The fetched bookmark data, parsed as JSON. #### Error Response - **error** (string) - An error message if the fetch fails. ``` -------------------------------- ### GitHub Push API Source: https://context7.com/sandeberger/thetab.ninja/llms.txt Pushes bookmark data to GitHub using the Git Data API. Creates a blob, tree, commit, and updates the reference to save changes to the repository. ```APIDOC ## GitHub Push API ### Description Pushes bookmark data to GitHub using the Git Data API. Creates a blob, tree, commit, and updates the reference to save changes to the repository. ### Method POST (Internal Chrome Runtime Message) ### Endpoint chrome.runtime.sendMessage ### Parameters #### Request Body - **action** (string) - Required - The action to perform, 'pushToGitHub'. - **config** (object) - Required - Configuration for GitHub access. - **username** (string) - Required - GitHub username. - **repo** (string) - Required - GitHub repository name. - **pat** (string) - Required - GitHub Personal Access Token. - **filepath** (string) - Required - Path to the bookmark file in the repository. - **content** (object) - Required - The bookmark data to push. - **collections** (array) - An array of bookmark collections. - **lastModified** (number) - Timestamp of the last modification. ### Request Example ```json { "action": "pushToGitHub", "config": { "username": "myusername", "repo": "bookmarks", "pat": "github_pat_xxxxx", "filepath": "bookmarks.json" }, "content": { "collections": [ { "id": "generated-uuid", "name": "Work Projects", "bookmarks": [ { "id": "generated-uuid", "title": "GitHub", "url": "https://github.com", "description": "Code hosting", "icon": "https://github.com/favicon.ico", "lastModified": 1678886400000, "deleted": false, "position": 0 } ], "lastModified": 1678886400000, "deleted": false, "position": 0 } ], "lastModified": 1678886400000 } } ``` ### Response #### Success Response (200) - **success** (boolean) - Indicates if the push operation was successful. #### Error Response - **error** (string) - An error message if the push fails. ``` -------------------------------- ### Complete Synchronization Workflow with GitHub in JavaScript Source: https://context7.com/sandeberger/thetab.ninja/llms.txt Orchestrates the complete synchronization workflow between local data and GitHub. This includes validating GitHub configuration, fetching data from both sources, merging datasets, updating local state, and pushing the merged data back to GitHub. ```javascript async function synchronizeWithGitHub() { if (!isGitHubConfigValid() || isSyncing) { return; } isSyncing = true; const syncButton = document.getElementById('syncButton'); syncButton.classList.add('syncing'); try { // Step 1: Sync global settings await synchronizeGlobalSettings(); // Step 2: Get active page filepath const activePage = globalSettings.pages.find(p => p.guid === globalSettings.activePage); const filepath = activePage.filepath; // Step 3: Fetch both local and remote data const [localData, remoteData] = await Promise.all([ loadFromLocalStorage(), fetchFromGitHub(filepath) ]); // Step 4: Validate structures if (localData && !validateDataStructure(localData)) { throw new Error('Invalid local data structure'); } if (remoteData && !validateDataStructure(remoteData)) { throw new Error('Invalid remote data structure'); } // Step 5: Merge collections const mergedCollections = mergeDatasets( (localData?.collections || []), (remoteData?.collections || []) ); // Step 6: Update local state bookmarkManagerData.collections = mergedCollections; bookmarkManagerData.lastSynced = Date.now(); // Step 7: Push merged data to GitHub await pushToGitHub({ collections: mergedCollections, lastModified: Date.now() }, filepath); // Step 8: Update UI and storage renderCollections(); saveToLocalStorage(); console.log('Synchronization completed successfully'); } catch (error) { console.error('Sync error:', error); alert(`Sync failed: ${error.message}`); } finally { isSyncing = false; syncButton.classList.remove('syncing'); } } ``` -------------------------------- ### Fetch Bookmarks from GitHub via API Source: https://context7.com/sandeberger/thetab.ninja/llms.txt Fetches bookmark data from a GitHub repository using the GitHub API. It handles authentication and retrieves file content. This function is essential for synchronizing bookmarks between the browser extension and a remote GitHub repository. ```javascript async function fetchFromGitHub(filepath) { const config = { username: 'myusername', repo: 'bookmarks', pat: 'github_pat_xxxxx', filepath: 'bookmarks.json' }; const response = await chrome.runtime.sendMessage({ action: 'fetchFromGitHub', config: config }); if (response.error) { console.error('Error fetching from GitHub:', response.error); return null; } // Response contains parsed JSON with collections return response.content; } // Example usage try { const bookmarkData = await fetchFromGitHub('bookmarks.json'); if (bookmarkData && bookmarkData.collections) { bookmarkManagerData.collections = bookmarkData.collections; renderCollections(); } } catch (error) { alert(`Sync failed: ${error.message}`); } ``` -------------------------------- ### Push Bookmarks to GitHub via API Source: https://context7.com/sandeberger/thetab.ninja/llms.txt Pushes bookmark data to GitHub using the Git Data API. This involves creating a blob, tree, and commit to save changes to the repository. It's crucial for updating the remote bookmark data after local modifications. ```javascript async function pushToGitHub(content, filepath) { const config = { username: 'myusername', repo: 'bookmarks', pat: 'github_pat_xxxxx', filepath: 'bookmarks.json' }; const bookmarkData = { collections: [ { id: generateUUID(), name: "Work Projects", bookmarks: [ { id: generateUUID(), title: "GitHub", url: "https://github.com", description: "Code hosting", icon: "https://github.com/favicon.ico", lastModified: Date.now(), deleted: false, position: 0 } ], lastModified: Date.now(), deleted: false, position: 0 } ], lastModified: Date.now() }; const response = await chrome.runtime.sendMessage({ action: 'pushToGitHub', config: config, content: bookmarkData }); if (response.success) { console.log('Successfully synced to GitHub'); } } ``` -------------------------------- ### Launch Collection as Tab Group Source: https://context7.com/sandeberger/thetab.ninja/llms.txt Launches a collection of bookmarks as a new tab group in Chrome. It retrieves the URLs from the specified collection and sends a message to the runtime to create a new window with these URLs, titled with the collection's name. Requires 'bookmarkManagerData' object. ```javascript // Launch collection as tab group function launchCollection(collectionId) { const collection = bookmarkManagerData.collections.find(c => c.id === collectionId); if (collection) { const urls = collection.bookmarks .filter(b => !b.deleted) .map(bookmark => bookmark.url); chrome.runtime.sendMessage({ action: 'launchCollection', urls: urls, collectionName: collection.name }, (response) => { if (response && response.success) { console.log('Collection launched successfully as tab group'); } }); } } ``` -------------------------------- ### JavaScript Search and Filter Implementation Source: https://context7.com/sandeberger/thetab.ninja/llms.txt This JavaScript code implements a search and filter system for collections and bookmarks. It listens for input in a search box, applies filters based on search terms and special operators ('#', '%', '|'), and dynamically shows/hides elements. It supports searching collections by name, global search across collections and bookmarks, and searching bookmarks by title or URL. Dependencies include the DOM and a global `bookmarkManagerData` object containing collection and bookmark information. ```javascript document.getElementById('searchBox').addEventListener('input', function() { const searchTerm = this.value.trim(); applyFilter(searchTerm); }); function applyFilter(searchTerm) { const collections = document.querySelectorAll('.collection'); const isCollectionSearch = searchTerm.startsWith('#'); // Search collections only const isGlobalSearch = searchTerm.startsWith('%'); // Search both collections and bookmarks // Support OR operator with pipe symbol let searchTerms = []; if (searchTerm) { const rawTerms = searchTerm.split('|'); searchTerms = rawTerms .map(term => term.trim().toLowerCase()) .filter(term => term.length > 0); } collections.forEach(collectionElement => { const collectionId = collectionElement.dataset.collectionId; const collectionData = bookmarkManagerData.collections.find(c => c.id === collectionId); const bookmarkElements = collectionElement.querySelectorAll('.bookmark'); let showCollection = false; if (!searchTerm) { // No search term - show everything collectionElement.classList.remove('hidden'); bookmarkElements.forEach(b => b.classList.remove('hidden')); return; } if (isCollectionSearch) { // Search only collection names: #work const collectionSearchTerms = searchTerms.map(t => t.replace(/^#/, '')); showCollection = collectionSearchTerms.some(term => collectionData.name.toLowerCase().includes(term) ); bookmarkElements.forEach(b => b.classList.toggle('hidden', !showCollection)); } else if (isGlobalSearch) { // Search both collections and bookmarks: %project const globalSearchTerms = searchTerms.map(t => t.replace(/^%/, '')); const collectionMatch = globalSearchTerms.some(term => collectionData.name.toLowerCase().includes(term) ); let hasVisibleBookmarks = false; bookmarkElements.forEach(bookmarkElement => { const bookmarkId = bookmarkElement.dataset.bookmarkId; const bookmarkData = collectionData.bookmarks.find(b => b.id === bookmarkId); const bookmarkMatch = globalSearchTerms.some(term => bookmarkData.title.toLowerCase().includes(term) || bookmarkData.url.toLowerCase().includes(term) ); bookmarkElement.classList.toggle('hidden', !bookmarkMatch); if (bookmarkMatch) hasVisibleBookmarks = true; }); showCollection = collectionMatch || hasVisibleBookmarks; } else { // Default: search only bookmarks let hasVisibleBookmarks = false; bookmarkElements.forEach(bookmarkElement => { const bookmarkId = bookmarkElement.dataset.bookmarkId; const bookmarkData = collectionData.bookmarks.find(b => b.id === bookmarkId); const bookmarkMatch = searchTerms.some(term => bookmarkData.title.toLowerCase().includes(term) || bookmarkData.url.toLowerCase().includes(term) ); bookmarkElement.classList.toggle('hidden', !bookmarkMatch); if (bookmarkMatch) hasVisibleBookmarks = true; }); showCollection = hasVisibleBookmarks; } collectionElement.classList.toggle('hidden', !showCollection); }); } ``` -------------------------------- ### Manage Bookmark Collections (JavaScript) Source: https://context7.com/sandeberger/thetab.ninja/llms.txt Handles the creation, editing, and soft deletion of bookmark collections. It increments positions for existing collections upon adding a new one and updates modification timestamps. Dependencies include `generateUUID`, `renderCollections`, and `saveToLocalStorage`. ```javascript function addCollection() { const name = prompt('Enter collection name:', 'My Collection'); if (name) { // Increment all existing positions bookmarkManagerData.collections.forEach(c => { c.position++; c.lastModified = Date.now(); }); const newCollection = { id: generateUUID(), name: name, isOpen: true, lastModified: Date.now(), deleted: false, position: 0, bookmarks: [] }; bookmarkManagerData.collections.push(newCollection); renderCollections(); saveToLocalStorage(); } } // Edit existing collection function editCollection(collectionId) { const collection = bookmarkManagerData.collections.find(c => c.id === collectionId); if (collection) { const newName = prompt('Enter new collection name:', collection.name); if (newName) { collection.name = newName; collection.lastModified = Date.now(); renderCollections(); } } } // Delete collection (soft delete) function deleteCollection(collectionId) { if (confirm('Are you sure you want to delete this collection?')) { const collectionIndex = bookmarkManagerData.collections.findIndex(c => c.id === collectionId); if (collectionIndex !== -1) { bookmarkManagerData.collections[collectionIndex].deleted = true; bookmarkManagerData.collections[collectionIndex].lastModified = Date.now(); renderCollections(); } } } ``` -------------------------------- ### Merge Datasets Logic in JavaScript Source: https://context7.com/sandeberger/thetab.ninja/llms.txt Implements sophisticated data merging logic to handle conflicts between local and remote changes during synchronization. It takes local and remote collections, builds a global index of bookmarks, and then reconstructs collections based on the latest versions, ensuring data integrity and conflict resolution. ```javascript function mergeDatasets(localCollections, remoteCollections) { const allCollections = [...localCollections, ...remoteCollections]; const collectionMap = new Map(); const globalBookmarks = new Map(); // Build global index of all bookmarks allCollections.forEach(collection => { collection.bookmarks.forEach(bookmark => { const existing = globalBookmarks.get(bookmark.id); if (!existing || existing.lastModified < bookmark.lastModified) { globalBookmarks.set(bookmark.id, { ...bookmark, parentCollection: collection.id }); } }); }); // Build collections based on latest version for (const collection of allCollections) { const existing = collectionMap.get(collection.id) || { ...collection, bookmarks: [], lastModified: 0 }; collectionMap.set(collection.id, { ...existing, name: existing.name || collection.name, lastModified: Math.max(existing.lastModified, collection.lastModified), bookmarks: [] }); } // Add bookmarks to correct collection globalBookmarks.forEach((bookmark, id) => { const collection = collectionMap.get(bookmark.parentCollection); if (collection) { collection.bookmarks.push(bookmark); } }); // Sort and return return Array.from(collectionMap.values()).map(collection => ({ ...collection, bookmarks: collection.bookmarks .filter(b => !b.deleted) .sort((a, b) => a.position - b.position) })); } ``` -------------------------------- ### Manage Bookmarks and Fetch Favicons (JavaScript) Source: https://context7.com/sandeberger/thetab.ninja/llms.txt Provides functionality to add, edit bookmarks within collections, and fetch favicons using a Chrome extension API. It assigns positions to new bookmarks and updates collection modification times. Requires `generateUUID`, `renderCollections`, and browser `chrome.runtime` API. ```javascript // Add bookmark to collection async function addBookmark(collectionId) { try { const title = prompt('Enter bookmark title:'); const url = prompt('Enter bookmark URL:', 'https://'); const description = prompt('Enter bookmark description:'); const collection = bookmarkManagerData.collections.find(c => c.id === collectionId); if (title && url && collection) { // Fetch favicon from Google's service const icon = await getFavicon(url); const newBookmark = { id: generateUUID(), title: title, url: url, description: description, icon: icon, lastModified: Date.now(), deleted: false, position: collection.bookmarks.length }; collection.bookmarks.push(newBookmark); collection.lastModified = Date.now(); renderCollections(); } } catch (error) { console.error('Bookmark Error:', error); } } // Get favicon for URL function getFavicon(url) { return new Promise((resolve, reject) => { chrome.runtime.sendMessage({ action: 'fetchFavicon', url }, (response) => { if (chrome.runtime.lastError) { reject(new Error(chrome.runtime.lastError.message)); return; } if (response && response.faviconUrl) { resolve(response.faviconUrl); } else { reject(new Error('No favicon URL received')); } }); }); } // Edit bookmark async function editBookmark(collectionId, bookmarkId) { const collection = bookmarkManagerData.collections.find(c => c.id === collectionId); if (collection) { const bookmark = collection.bookmarks.find(b => b.id === bookmarkId); if (bookmark) { const title = prompt('Edit bookmark title:', bookmark.title); const url = prompt('Edit bookmark URL:', bookmark.url); const description = prompt('Edit bookmark description:', bookmark.description); if (title && url) { const icon = await getFavicon(url); Object.assign(bookmark, { title, url, description, icon, lastModified: Date.now() }); collection.lastModified = Date.now(); renderCollections(); } } } } ``` -------------------------------- ### Bookmark Title and Description Styling Source: https://github.com/sandeberger/thetab.ninja/blob/master/bm.html Styles the title (h3) and description (p) within a bookmark. It handles overflow and ellipsis for long text and limits description lines for better readability. ```CSS .bookmark h3 { margin: 5px 0; font-size: 12px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .bookmark p { font-size: 10px; margin: 0; overflow: hidden; text-overflow: ellipsis; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; } ``` -------------------------------- ### Bookmark Image Styling Source: https://github.com/sandeberger/thetab.ninja/blob/master/bm.html Sets the maximum width and height for images within bookmarks, ensuring they fit within their container while maintaining aspect ratio. ```CSS .bookmark img { max-width: 32px; max-height: 32px; object-fit: contain; } ``` -------------------------------- ### Fetch and Display Chrome Tabs Source: https://context7.com/sandeberger/thetab.ninja/llms.txt Fetches all open tabs across all Chrome windows and displays their titles, URLs, and associated group information. It iterates through windows, then tab groups, and finally individual tabs, logging details to the console. No external dependencies beyond the Chrome extension API. ```javascript // Fetch all Chrome tabs from all windows chrome.runtime.sendMessage({ action: "getTabs" }, (response) => { if (response && response.length > 0) { response.forEach((windowData) => { console.log(`Window ${windowData.windowId}:`); // Access tab groups windowData.groups.forEach(group => { console.log(`Group: ${group.title} (${group.color})`); }); // Access individual tabs windowData.tabs.forEach(tab => { console.log(`Tab: ${tab.title} - ${tab.url}`); console.log(`Group ID: ${tab.groupId}`); }); }); } }); ``` -------------------------------- ### Bookmark Manager Layout Source: https://github.com/sandeberger/thetab.ninja/blob/master/bm.html Styles the bookmark manager container, setting it to take up available space and arrange its contents in a column. It also ensures a minimum height for the content. ```CSS #bookmarkManager { flex: 1; display: flex; flex-direction: column; min-height: 0; } ``` -------------------------------- ### Individual Bookmark Styling Source: https://github.com/sandeberger/thetab.ninja/blob/master/bm.html Defines the appearance of individual bookmark elements, including borders, padding, background, and dimensions. It uses flexbox for internal layout and includes transitions for transform and opacity on hover. ```CSS .bookmark { border: 1px solid #ddd; padding: 6px; width: 133px; height: 80px; background: rgba(255, 255, 255, 0.1); backdrop-filter: blur(1px); position: relative; cursor: pointer; overflow: hidden; display: flex; flex-direction: column; border-radius: 4px; transition: transform 0.2s ease, opacity 0.2s ease; will-change: transform, opacity; } .bookmark:hover { box-shadow: 0 2px 5px rgba(0,0,0,0.2); } ``` -------------------------------- ### CSS Form Styling Source: https://github.com/sandeberger/thetab.ninja/blob/master/popup.html This CSS snippet defines the visual appearance of a form and its elements within the thetab.ninja project. It includes styles for box sizing, body margins and padding, form containers, form fields, labels, text inputs, and buttons. It also includes hover and focus states for enhanced user interaction. Dependencies: None, standard CSS. ```css /* Gör så box-sizing räknar in border och padding i elementets totala storlek */ * { box-sizing: border-box; } body { margin: 0; padding: 16px; font-family: Arial, sans-serif; width: 300px; background-color: #f9f9f9; } .form-container { background-color: #ffffff; border: 1px solid #ddd; border-radius: 6px; padding: 16px; box-shadow: 0 1px 3px rgba(0,0,0,0.2); } .form-field { margin-bottom: 16px; } label { display: block; font-weight: bold; margin-bottom: 6px; font-size: 14px; } /* GEMENSAM STYLING FÖR INPUT OCH KNAPP */ input[type="text"], button { display: block; /* Helt egna block, tar upp hela bredden */ width: 100%; /* Samma bredd */ padding: 10px; /* Liknande padding */ margin: 0; /* Ingen extra marginal */ font-size: 14px; /* Samma teckenstorlek */ border-radius: 4px; /* Samma hörnrundning */ } /* Specifik styling för input-fältet */ input[type="text"] { border: 1px solid #ccc; /* Om du vill förstärka fokus, kan du göra: outline: none; och en box-shadow när fokus är på (se nedan). */ } input[type="text"]:focus { border-color: #4CAF50; box-shadow: 0 0 0 1px #4CAF50; outline: none; } /* Specifik styling för knappen */ button { border: none; background-color: #4CAF50; color: #ffffff; cursor: pointer; margin-top: 4px; /* lite andrum mellan fält och knapp */ } button:hover { background-color: #45a049; } ``` -------------------------------- ### Pane Layout and Collapsing Styles Source: https://github.com/sandeberger/thetab.ninja/blob/master/bm.html Defines the layout and styling for left and right panes, including their width, transitions, and scrollable content. It also styles the collapsed state of these panes, reducing their width. ```CSS #leftPane, #rightPane { width: 250px; /*flex: 0 0 250px;*/ transition: width 0.3s; overflow-y: auto; } #leftPane.closed, #rightPane.closed { width: 30px; /*flex: 0 0 30px;*/ } ``` -------------------------------- ### Global and Body Styles Source: https://github.com/sandeberger/thetab.ninja/blob/master/bm.html Sets base styles for the HTML and body elements, including height, margin, font family, and background properties. It also defines general transitions for background and text color, and flexbox layout for the body. ```CSS html, body { height: 100%; margin: 0; } body { font-family: Arial, sans-serif; background-image: url(''); background-repeat: no-repeat; background-position: center center; background-attachment: fixed; background-size: cover; /* Välj 'cover' eller 'contain' */ margin: 0; padding: 20px; display: flex; transition: background-color 0.3s, color 0.3s; flex-direction: row; } ``` -------------------------------- ### Bookmarks Container Styling Source: https://github.com/sandeberger/thetab.ninja/blob/master/bm.html Styles the container for bookmarks, using flexbox to allow wrapping of items and defining a gap between them. It also sets a minimum height for the container. ```CSS .bookmarks { display: flex; flex-wrap: wrap; gap: 10px; min-height: 50px; } ``` -------------------------------- ### Specific Collection Action Button Styling Source: https://github.com/sandeberger/thetab.ninja/blob/master/bm.html Styles specific action buttons for collections, including 'Add Bookmark', 'Edit Collection', 'Move Collection', and 'Delete Collection', with distinct background colors and hover effects. ```CSS .add-bookmark { background-color: #4CAF50; color: white; } .add-bookmark:hover { background-color: #45a049; } .edit-collection { background-color: #555555; color: white; } .edit-collection:hover { background-color: #444444; } .move-collection { background-color: #f0f0f0; color: #333; } .move-collection:hover { background-color: #e0e0e0; } .delete-collection { background-color: #f44336; color: white; } .delete-collection:hover { background-color: #da190b; } ``` -------------------------------- ### Generic Collection Button Styling Source: https://github.com/sandeberger/thetab.ninja/blob/master/bm.html Provides base styling for buttons within a collection, such as padding, cursor, border, and font size. It also includes transitions for background and text color on hover. ```CSS .collection-button { padding: 5px 10px; cursor: pointer; border: none; border-radius: 4px; font-size: 14px; transition: background-color 0.3s, color 0.3s; } ``` -------------------------------- ### Save Chrome Tabs to Collection Source: https://context7.com/sandeberger/thetab.ninja/llms.txt Saves all currently open tabs into a specified collection. It fetches tabs, filters out the extension's own page, creates bookmark entries for each tab, and adds them to the collection. Optionally closes tabs after saving if the 'closeWhenSaveTab' setting is enabled. Requires 'generateUUID' and 'saveToLocalStorage' functions, and 'bookmarkManagerData' object. ```javascript // Save all Chrome tabs to a collection async function fetchAllTabs(collectionId) { try { const selfUrl = chrome.runtime.getURL("bm.html"); chrome.runtime.sendMessage({ action: "getTabs" }, (response) => { if (response && response.length > 0) { let allTabs = []; response.forEach(windowData => { allTabs = allTabs.concat(windowData.tabs); }); const collection = bookmarkManagerData.collections.find(c => c.id === collectionId); if (!collection) return; allTabs.forEach(tab => { // Skip extension's own page if (tab.url === selfUrl) return; const newBookmark = { id: generateUUID(), title: tab.title, url: tab.url, description: "", icon: tab.favIconUrl || "default-icon.png", lastModified: Date.now(), deleted: false, position: collection.bookmarks.length }; collection.bookmarks.push(newBookmark); // Close tab if setting enabled if (bookmarkManagerData.closeWhenSaveTab) { chrome.tabs.remove(tab.tabId); } }); collection.lastModified = Date.now(); renderCollections(); saveToLocalStorage(); } }); } catch (error) { console.error("Error in fetchAllTabs:", error); } } ``` -------------------------------- ### Bookmark Edit and Delete Icons Styling Source: https://github.com/sandeberger/thetab.ninja/blob/master/bm.html Positions and styles the edit and delete icons within the bookmark element. They are absolutely positioned at the bottom right and initially hidden, becoming visible on hover. ```CSS .bookmark .edit-icon, .bookmark .delete-icon { position: absolute; bottom: 5px; cursor: pointer; opacity: 0; transition: opacity 0.3s; } .bookmark .edit-icon { right: 25px; } .bookmark .delete-icon { right: 5px; } .bookmark:hover .edit-icon, .bookmark:hover .delete-icon { opacity: 1; } ``` -------------------------------- ### Window Title Styling Source: https://github.com/sandeberger/thetab.ninja/blob/master/bm.html Styles the title bar of a window element, making it clickable with padding, background color, margin, and rounded corners. ```CSS .window-title { cursor: pointer; padding: 10px; background-color: #f0f0f0; margin-bottom: 5px; border-radius: 4px; } ``` -------------------------------- ### Collection Header Styling Source: https://github.com/sandeberger/thetab.ninja/blob/master/bm.html Styles the header for each collection, using flexbox to align items with space between them and centering them vertically. It also includes margin for spacing below the header. ```CSS .collection-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px; } ``` -------------------------------- ### Empty Collection Message Styling Source: https://github.com/sandeberger/thetab.ninja/blob/master/bm.html Styles the message displayed when a collection is empty, providing visual cues with padding, centered text, background color, dashed border, and rounded corners. ```CSS .empty-collection-message { padding: 20px; text-align: center; background-color: #f0f0f0; border: 2px dashed #ccc; margin-top: 10px; width: 100%; border-radius: 4px; } ``` -------------------------------- ### Add Collection Button Styling Source: https://github.com/sandeberger/thetab.ninja/blob/master/bm.html Styles the 'Add Collection' button with a semi-transparent green background, white text, padding, and rounded borders. It includes transitions for background color and hover effects for transform and box-shadow. ```CSS #addCollection { background-color: rgba(76, 297, 80, 0.6); border: none; color: white; padding: 10px 20px; text-align: center; text-decoration: none; display: inline-block; font-size: 16px; margin: 4px 2px; cursor: pointer; border-radius: 16px; transition: background-color 0.3s; box-shadow: 0 4px 8px rgba(0,0,0,0.2); transition: transform 0.2s, box-shadow 0.2s; } #addCollection:hover { background-color: #45a049; } ``` -------------------------------- ### Tab Icon Styling Source: https://github.com/sandeberger/thetab.ninja/blob/master/bm.html Sets the dimensions for icons within a tab element and adds right margin for spacing between the icon and the text. ```CSS .tab img { width: 16px; height: 16px; margin-right: 10px; } ``` -------------------------------- ### Collection Action Buttons Styling Source: https://github.com/sandeberger/thetab.ninja/blob/master/bm.html Styles the container for action buttons within a collection, using flexbox for layout. It also controls the opacity of these buttons, making them appear on hover. ```CSS .collection-actions { display: flex; gap: 5px; opacity: 0; transition: opacity 0.3s; } .collection:hover .collection-actions { opacity: 1; } ``` -------------------------------- ### Dragging Element Styling Source: https://github.com/sandeberger/thetab.ninja/blob/master/bm.html Applies styling to an element when it is being dragged, reducing its opacity and applying a slight scaling effect for visual feedback. ```CSS .dragging { opacity: 0.3; transform: scale(0.95); } ``` -------------------------------- ### Global Scrollbar Styling for Firefox Source: https://github.com/sandeberger/thetab.ninja/blob/master/bm.html Applies scrollbar styling for Firefox browsers, setting the scrollbar width to 'thin' and defining the color of the scrollbar and its track. ```CSS /* Globala inställningar för Firefox (Anv ej nu) */ * { scrollbar-width: thin; scrollbar-color: rgba(0, 0, 0, 0.4) transparent; } ``` -------------------------------- ### Toggle Collection Button Styling Source: https://github.com/sandeberger/thetab.ninja/blob/master/bm.html Styles the button used to toggle the visibility of a collection. It includes styles for rotation transitions and specific colors for dark mode. ```CSS .toggle-collection { color: #333; background: none; border: none; font-size: 18px; cursor: pointer; padding: 0 10px; transition: transform 0.3s ease; /* NY egenskap */ } .collection.is-open .toggle-collection { transform: rotate(0deg); } .collection:not(.is-open) .toggle-collection { transform: rotate(180deg); } body.dark-mode .toggle-collection { background: none; border: none; font-size: 18px; cursor: pointer; padding: 0 10px; color: #fff; /* Ljus färg för mörkt läge */ } ``` -------------------------------- ### Tab Title Overflow Handling Source: https://github.com/sandeberger/thetab.ninja/blob/master/bm.html Styles the title text within a tab, preventing it from wrapping and hiding any overflow. It also ensures the text is displayed on a single line. ```CSS .tab-title { white-space: nowrap; overflow: hid ``` -------------------------------- ### Individual Tab Styling Source: https://github.com/sandeberger/thetab.ninja/blob/master/bm.html Styles individual tab elements, arranging their content horizontally with alignment for icons and text. It indicates a draggable area with a pointer cursor. ```CSS .tab { display: flex; align-items: center; padding: 5px; cursor: move; } ``` -------------------------------- ### Collection Title Area Styling Source: https://github.com/sandeberger/thetab.ninja/blob/master/bm.html Styles the area containing the collection title, using flexbox to align items and center them vertically. ```CSS .collection-title-area { display: flex; align-items: center; } ``` -------------------------------- ### Drag Over Indicator Styling Source: https://github.com/sandeberger/thetab.ninja/blob/master/bm.html Applies styling to indicate when an element is being dragged over another, typically by changing the background color and border style. ```CSS .drag-over { background-color: #e0e0e0; border: 2px dashed #666; } ``` -------------------------------- ### Tabs List Styling Source: https://github.com/sandeberger/thetab.ninja/blob/master/bm.html Styles the container for a list of tabs, arranging them in a column and enabling vertical scrolling for overflow. It also allows text to wrap. ```CSS .tabs-list { display: flex; flex-direction: column; overflow-y: auto; white-space: normal; } ``` -------------------------------- ### Scrollbar Styling for WebKit Browsers Source: https://github.com/sandeberger/thetab.ninja/blob/master/bm.html Customizes the appearance of scrollbars for WebKit-based browsers (Chrome, Safari, Edge). It defines the width of the scrollbar, its track background, and the thumb color and border-radius. ```CSS ::-webkit-scrollbar { width: 8px; height: 8px; } ::-webkit-scrollbar-track { background: transparent; } ::-webkit-scrollbar-thumb { background: rgba(0, 0, 0, 0.4); border-radius: 4px; } ``` -------------------------------- ### Drag Handle Styling Source: https://github.com/sandeberger/thetab.ninja/blob/master/bm.html Styles the drag handle element, indicating it's a draggable area with specific background, padding, and text color. It also includes specific styling for dark mode. ```CSS .drag-handle { cursor: move; padding: 5px 10px; margin-right: 10px; background-color: #e0e0e0; border-radius: 4px; color: #333; /* Mörk färg för ljust läge */ } body.dark-mode .drag-handle { background-color: #444; /* Mörkare bakgrund för mörkt läge */ color: #ddd; /* Ljusare text för mörkt läge */ } ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.