diff --git a/.github/workflows/pr-tests.yml b/.github/workflows/pr-tests.yml index 462fa7f..1610785 100644 --- a/.github/workflows/pr-tests.yml +++ b/.github/workflows/pr-tests.yml @@ -4,12 +4,15 @@ on: pull_request: types: - opened - - edited + - synchronize + - reopened + - ready_for_review branches: - main permissions: contents: write + pull-requests: write jobs: test: @@ -24,27 +27,57 @@ jobs: deno-version: v2.x - name: Switch to branch - run: git switch "${{ github.head_ref }}" + run: | + git fetch origin "${{ github.head_ref }}" + git switch "${{ github.head_ref }}" + + - name: Run linter and capture errors + id: lint + continue-on-error: true + run: | + # Run the Deno linter and capture stderr + deno task lint 2> lint-error.log || true # do not fail + + - name: Comment on PR if linter fails + uses: actions/github-script@v7 + with: + script: | + const fs = require('fs'); + + // Read the linter error output + const errorOutput = fs.readFileSync('lint-error.log', 'utf8'); + const problemsFoundRegex = /Found [0-9]+ problem(s)?\n/m + if (!errorOutput || !errorOutput.match(problemsFoundRegex)) { + console.log("No lint errors."); + return; + } + + // Remove ANSI codes + const ansiRegex = /\x1b\[[0-9;]*m/g; + const cleanErrorOutput = errorOutput.replace(ansiRegex, ''); + + // Create a comment on the PR with the error message + await github.rest.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: `## Linter Failed:\n\n\`\`\`${cleanErrorOutput}\`\`\`` + }); + + // fail due to error found on previous step + process.exit(1); - - name: Run linter - run: deno task lint + - name: Remove lint-error.log + run: rm lint-error.log - name: Verify formatting run: deno task fmt - - name: Ensure Changes Exist - run: | - if git diff --exit-code; then - echo "No changes to commit, exiting." - exit 0 - fi - - - name: Set up Git user + - name: Push fmt changes run: | + if ! git diff --exit-code; then git config user.name "github-actions" git config user.email "github-actions@github.com" - - - name: Push fmt changes - run: | git commit -am "deno formatting" git push https://x-access-token:${{ github.token }}@github.com/${{ github.repository }} "${{ github.head_ref }}" + fi diff --git a/README.md b/README.md index e32a152..1877da5 100644 --- a/README.md +++ b/README.md @@ -6,15 +6,16 @@ ![Release version](https://img.shields.io/github/manifest-json/v/Astisme/again-why-salesforce?filename=manifest%2Ftemplate-manifest.json&label=Version) ![Last commit](https://img.shields.io/github/last-commit/Astisme/again-why-salesforce?labelColor=black&color=white) -![License](https://img.shields.io/github/license/astisme/again-why-salesforce) -![Code size](https://img.shields.io/github/languages/code-size/astisme/again-why-salesforce) +![License](https://img.shields.io/github/license/Astisme/again-why-salesforce) +![Code size](https://img.shields.io/github/languages/code-size/Astisme/again-why-salesforce) + This extension allows users to create custom tabs in Setup for their most-used settings. @@ -25,7 +26,7 @@ This is a fork of [Why Salesforce](https://www.github.com/walters954/why-salesfo [Install on Chrome Web Store](https://chrome.google.com/webstore/detail/why-salesforce/ghakkjfjpnhpggbkfkeplbefkipfoaod) -### Roadmap +## Roadmap - [x] Ability to customize tab - [x] Salesforce SLDS @@ -43,8 +44,18 @@ This is a fork of [Why Salesforce](https://www.github.com/walters954/why-salesfo - [x] Favourite button on current Setup page #12 - [x] Import & Export tabs -Contributors +## Contributing + +All contributions are welcome. Please head to the [issues page](https://github.com/Astisme/again-why-salesforce/issues) and pick one unassigned issue to work on. + +We'll assign it to you after you comment on it. + +## Best Practices + +The Best Practices followed by this project can be found [at this link](https://blog.jetbrains.com/webstorm/2024/10/javascript-best-practices-2024/). + +## Contributors - [Warren Walters](https://www.linkedin.com/in/walters954/) - [Chris Rouse (Firefox port)](https://www.linkedin.com/in/chris-rouse/) -- [Astisme](https://www.github.com/astisme/) +- [Astisme](https://www.github.com/Astisme/) diff --git a/action/basic.css b/action/basic.css index 08d28fc..55780fc 100644 --- a/action/basic.css +++ b/action/basic.css @@ -1,5 +1,6 @@ *, :before, :after { box-sizing: border-box; + background-color: transparent; } html { @@ -15,13 +16,14 @@ html { "Segoe UI Emoji", "Segoe UI Symbol"; line-height: 1.5; + color: var(--color); + background-color: var(--background-color); } body { padding: 0 !important; margin: 0; font-size: 0.8125rem; - background-color: transparent; } header { @@ -51,6 +53,10 @@ a { } } +input { + color: var(--color); +} + .button, button { position: relative; @@ -60,7 +66,7 @@ button { padding-bottom: 0; padding-left: 1rem; padding-right: 1rem; - border: none; + border: none; border-radius: 0.25rem; line-height: 1.875rem; text-decoration: none; @@ -74,19 +80,47 @@ button { margin-left: 0.25rem; width: 4rem; min-height: 2.3rem; - background-color: #e9e9ed; + color: var(--color); + background-color: var(--button-background-color); + + &:hover { + background-color: var(--button-background-color-hover); + } +} + +.success { + background-color: var(--success); - &:hover { - background-color: #cccccc; - } + &:hover { + background-color: var(--success-hover); + } +} + +.error { + background-color: var(--error); + + &:hover { + background-color: var(--error-hover); + } } .highlight { - background-color: lightblue; + color: var(--background-color); + background-color: var(--highlight); - &:hover { - background-color: skyblue; - } + &:hover { + background-color: var(--highlight-hover); + } +} + +:disabled { + color: var(--background-color); + background-color: var(--shade-hover); + cursor: not-allowed !important; + + &:hover { + background-color: var(--shade-hover); + } } .slds-assistive-text { diff --git a/action/logo.css b/action/logo.css index 5599da9..40e7086 100644 --- a/action/logo.css +++ b/action/logo.css @@ -3,6 +3,7 @@ a { margin-right: 0.75rem; display: flex; align-items: center; + color: var(--color); & > span { line-height: 1; diff --git a/action/logo.html b/action/logo.html index 01f5cda..f05031f 100644 --- a/action/logo.html +++ b/action/logo.html @@ -1,9 +1,13 @@ +
- Again, Why Salesforce logo + Again, Why Salesforce logo Again, Why Salesforce

diff --git a/action/notSalesforceSetup.html b/action/notSalesforceSetup.html index 5d27f57..4c2d74b 100644 --- a/action/notSalesforceSetup.html +++ b/action/notSalesforceSetup.html @@ -5,6 +5,7 @@ + @@ -16,12 +17,16 @@

This is not a Salesforce Lightning Setup Page

-
- Login to Salesforce - - + + Login to Salesforce + +
diff --git a/action/notSalesforceSetup.js b/action/notSalesforceSetup.js index 82d038c..a5fcc31 100644 --- a/action/notSalesforceSetup.js +++ b/action/notSalesforceSetup.js @@ -1,11 +1,12 @@ // deno-lint-ignore-file no-window -const authorizedDomainRegex = /^https:\/\/[a-zA-Z0-9.-]+\.lightning\.force\.com\/.*/; +const authorizedDomainRegex = + /^https:\/\/[a-zA-Z0-9.-]+\.lightning\.force\.com\/.*/; const page = new URLSearchParams(window.location.search).get("url"); const sfsetupTextEl = document.querySelector("h3"); const div = document.createElement("div"); -const prefix = document.createTextNode("This is not a ") -const strongEl = document.createElement("strong") +const prefix = document.createTextNode("This is not a "); +const strongEl = document.createElement("strong"); const otherText = document.createTextNode(""); sfsetupTextEl.innerText = ""; @@ -14,45 +15,45 @@ let insertPrefix = true; let strongFirst = true; if (page != null) { // we're in a salesforce page - let domain; + let domain; - try { - domain = new URL(page).origin; - } catch(error){ - strongEl.textContent = "Invalid URL"; - otherText.textContent = " detected." - insertPrefix = false; - } + try { + domain = new URL(page).origin; + } catch (_) { + strongEl.textContent = "Invalid URL"; + otherText.textContent = " detected."; + insertPrefix = false; + } - // domain is null if an error occurred - if(domain != null){ - // Validate the domain (make sure it's a Salesforce domain) - if (!authorizedDomainRegex.test(page)) { - strongEl.textContent = "Invalid Salesforce"; - otherText.textContent = " domain detected."; - insertPrefix = false; - } else { - // switch which button is shown - document.getElementById("login").classList.add("hidden"); - const goSetup = document.getElementById("go-setup"); - goSetup.classList.remove("hidden"); - // update the button href to use the domain - goSetup.href = `${domain}/lightning/setup/SetupOneHome/home`; - // update the bold on the text - otherText.textContent = "Salesforce Lightning"; - strongEl.textContent = " Setup Page" - strongFirst = false; - } - } + // domain is null if an error occurred + if (domain != null) { + // Validate the domain (make sure it's a Salesforce domain) + if (!authorizedDomainRegex.test(page)) { + strongEl.textContent = "Invalid Salesforce"; + otherText.textContent = " domain detected."; + insertPrefix = false; + } else { + // switch which button is shown + document.getElementById("login").classList.add("hidden"); + const goSetup = document.getElementById("go-setup"); + goSetup.classList.remove("hidden"); + // update the button href to use the domain + goSetup.href = `${domain}/lightning/setup/SetupOneHome/home`; + // update the bold on the text + otherText.textContent = "Salesforce Lightning"; + strongEl.textContent = " Setup Page"; + strongFirst = false; + } + } } else { - strongEl.textContent = "Salesforce Lightning"; - otherText.textContent = " Setup Page"; + strongEl.textContent = "Salesforce Lightning"; + otherText.textContent = " Setup Page"; } insertPrefix && div.appendChild(prefix); -if(strongFirst){ - div.appendChild(strongEl); - div.appendChild(otherText) +if (strongFirst) { + div.appendChild(strongEl); + div.appendChild(otherText); } else { - div.appendChild(otherText) - div.appendChild(strongEl); + div.appendChild(otherText); + div.appendChild(strongEl); } diff --git a/action/popup.css b/action/popup.css index 0d1430c..02d4ea0 100644 --- a/action/popup.css +++ b/action/popup.css @@ -5,13 +5,9 @@ body { div#top { position: relative; padding: 0; - background-color: #ffffff; - border-width: 1px; - border-style: solid; - border-color: #c9c9c9; border-radius: 0.25rem; background-clip: padding-box; - box-shadow: 0 2px 2px 0 #0000000f; /*rgba(0,0,0,0.1)*/ + box-shadow: 0 2px 2px 0 rgba(var(--rgbcolor), 0.1); display: block; } @@ -57,106 +53,76 @@ header { cursor: pointer; } - & > button#export { - color: #040f0f; - background-color: #5fb49c; - border-color: #5fb49c; - } - - & > button#import { - color: #ffffff; - background-color: #0176d3; - border-color: #0176d3; - } - & > img#delete-all { height: 2rem; } } } -div#top > div:last-child { +div#top > table { padding: 0 !important; margin-top: 0.75rem; margin-bottom: 0.75rem; + border-collapse: collapse; + border-spacing: 0; + border: none; + font-size: inherit; + width: 100%; + white-space: nowrap; + text-align: left; - & > table { - border-collapse: separate; - border-top: 1px solid #e5e5e5; - border-bottom: 1px solid #e5e5e5; - background-color: #ffffff; - font-size: inherit; - width: 100%; - border-spacing: 0; - - & > thead { - & th { - padding: 0.25rem 1rem; - background-color: #f3f3f3; - color: #444444; - font-weight: 700; - line-height: normal; - white-space: nowrap; - position: relative; - text-align: left; - } + & > thead { + & th { + padding: 0.25rem 1rem; + background-color: var(--shade); + font-weight: 700; + line-height: normal; + position: relative; + } - & th:first-child { - padding: 0; - width: 1rem; - } + & th:first-child { + padding: 0; + width: 1rem; + } - & th:last-child { - padding-right: 1.5rem; - } + & th:last-child { + padding-right: 1.5rem; } + } - & > tbody#tabs > tr.tab { + & > tbody#tabs { + & > tr.tab { counter-increment: row-number; & > td { padding: 0.25rem 0.5rem; padding-left: 1rem; - border-top: 1px solid #e5e5e5; - white-space: nowrap; - position: relative; - text-align: left; - transition: all 0.3s ease-in-out; - transform: translateX(0); /* Initially no translation */ - - &:not(:has(img, button)):hover { - transform: translateX(10px); - } - & > img { + & > svg { width: 1rem; height: 1rem; cursor: grab; + stroke: var(--color); } & > div { clear: left; - position: relative; + transition: all 0.3s ease-in-out; + transform: translateX(0); /* Initially no translation */ } - & > button.delete { - color: #ffffff; - background-color: #ba0517; - border-color: #ba0517; + &:hover > div { + transform: translateX(10px); } } } - & > tbody#tabs > tr.tab:last-child { - & button.delete { - color: #333333; - background-color: #c9c9c9; - border-color: #c9c9c9; - } + & > tr.tab:first-child > td { + padding-top: 0.75rem; + } - & :is(:not(input), button, img) { - cursor: not-allowed !important; - } + & > tr.tab:last-child :is(:not(input)) { + cursor: not-allowed !important; } } } @@ -170,9 +136,8 @@ input { min-height: calc(1.875rem + 2px); font: inherit; line-height: 1.875rem; - border: 1px solid #c9c9c9; + border: 1px solid var(--border-color); border-radius: 0.25rem; - background-color: #ffffff; display: inline-block; margin: 0; } diff --git a/action/popup.html b/action/popup.html index 6047841..e139d55 100644 --- a/action/popup.html +++ b/action/popup.html @@ -5,6 +5,7 @@ + @@ -16,50 +17,88 @@
- - + Delete all tabs
-
- - - - - - - - - - -
-
-
-
Tab Label
-
-
Tab Url
-
-
- Actions -
-
-
+ + + + + + + + + + +
+
+
+
Tab Label
+
+
Tab Url
+
+
+ Actions +
+

diff --git a/action/popup.js b/action/popup.js index f17b08f..60a008a 100644 --- a/action/popup.js +++ b/action/popup.js @@ -121,10 +121,6 @@ function focusListener(e) { function createElement() { const element = tabTemplate.content.firstElementChild.cloneNode(true); element.dataset.draggable = "false"; - /*element.addEventListener("focus", event => { - event.preventDefault(); - saveTabs(); - });*/ const deleteButton = element.querySelector("button.delete"); deleteButton.addEventListener("click", deleteTab); deleteButton.disabled = true; diff --git a/action/variables.css b/action/variables.css new file mode 100644 index 0000000..786cd25 --- /dev/null +++ b/action/variables.css @@ -0,0 +1,52 @@ +html { + --white: #ffffff; + --rgbwhite: 255,255,255; + --black: #000000; + --rgbblack: 0,0,0; + --green: #82c4b2; + --midgreen: #4ea68f; + --darkgreen: #346f5f; + --skyblue: #0176d3; + --seablue: #2a6bc6; + --lightgray: #f3f3f3; + --midgray: #e5e5e5; + --darkgray: #c9c9c9; + --darkergray: #666666; + --darkestgray: #333333; + --red: #ba0517; + --darkred: #951414; +} + +html[data-theme="light"] { + --color: var(--black); + --rgbcolor: var(--rgbblack); + --background-color: var(--white); + --shade: var(--lightgray); + --shade-hover: var(--midgray); + --highlight: var(--skyblue); + --highlight-hover: var(--seablue); + --success: var(--green); + --success-hover: var(--midgreen); + --border-color: var(--midgray); + --error: var(--red); + --error-hover: var(--darkred); + --button-background-color: var(--midgray); + --button-background-color-hover: var(--darkgray); +} + +html, html[data-theme="dark"] { + --color: var(--white); + --rgbcolor: var(--rgbwhite); + --background-color: var(--black); + --shade: var(--darkestgray); + --shade-hover: var(--darkergray); + --highlight: var(--skyblue); + --highlight-hover: var(--seablue); + --success: var(--darkgreen); + --success-hover: var(--midgreen); + --border-color: var(--darkgray); + --error: var(--red); + --error-hover: var(--darkred); + --button-background-color: var(--midgray); + --button-background-color-hover: var(--darkgray); +} diff --git a/assets/README.md b/assets/README.md index 93395ff..93d99d7 100644 --- a/assets/README.md +++ b/assets/README.md @@ -1 +1 @@ -This directory contains the SVGs used in around the extension and its icons. +This directory contains the assets using everywhere else. diff --git a/assets/icons/README.md b/assets/icons/README.md index a2fae84..329cf47 100644 --- a/assets/icons/README.md +++ b/assets/icons/README.md @@ -1 +1,3 @@ This directory contains the icons used by the extension, in different resolutions. + +The picture was created using [Craiyon](https://www.craiyon.com/), an AI image generator. diff --git a/assets/svgs/README.md b/assets/svgs/README.md new file mode 100644 index 0000000..93395ff --- /dev/null +++ b/assets/svgs/README.md @@ -0,0 +1 @@ +This directory contains the SVGs used in around the extension and its icons. diff --git a/assets/drag.svg b/assets/svgs/drag.svg similarity index 100% rename from assets/drag.svg rename to assets/svgs/drag.svg diff --git a/assets/slashed-star.svg b/assets/svgs/slashed-star.svg similarity index 100% rename from assets/slashed-star.svg rename to assets/svgs/slashed-star.svg diff --git a/assets/star.svg b/assets/svgs/star.svg similarity index 100% rename from assets/star.svg rename to assets/svgs/star.svg diff --git a/assets/trash.svg b/assets/svgs/trash.svg similarity index 100% rename from assets/trash.svg rename to assets/svgs/trash.svg diff --git a/manifest/build-manifest.mjs b/manifest/build-manifest.mjs index acd89ee..c5b445d 100644 --- a/manifest/build-manifest.mjs +++ b/manifest/build-manifest.mjs @@ -10,15 +10,12 @@ if (browser === "firefox") { delete manifest.minimum_chrome_version; delete manifest.background.service_worker; - delete manifest.browser_specific_settings.safari; - + delete manifest.browser_specific_settings.safari; } else if (browser === "chrome") { delete manifest.browser_specific_settings; - } else if (browser === "safari") { delete manifest.minimum_chrome_version; - delete manifest.browser_specific_settings.gecko; - + delete manifest.browser_specific_settings.gecko; } else { console.error( `Usage: ${process.argv[0]} ${ diff --git a/salesforce/content.js b/salesforce/content.js index b8e83cb..0e1dcdf 100644 --- a/salesforce/content.js +++ b/salesforce/content.js @@ -35,7 +35,7 @@ function setStorage(tabs) { sendMessage({ what: "set", tabs }, afterSet); } -function cleanupUrl(url = href, nochange = null) { +function cleanupUrl(url, nochange = null) { const asis = nochange == null ? url.startsWith("http") : nochange; if (url.startsWith("/lightning") || url.startsWith("/_ui/common")) { // normalized setup pages won't get here return `${baseUrl}${url}`; @@ -58,11 +58,41 @@ function generateRowTemplate(row) { let { tabTitle, url } = row; url = cleanupUrl(url); - return ``; + const li = document.createElement("li"); + li.setAttribute("role", "presentation"); + li.classList.add( + "oneConsoleTabItem", + "tabItem", + "slds-context-bar__item", + "borderRight", + "navexConsoleTabItem", + prefix, + ); + li.setAttribute("data-aura-class", "navexConsoleTabItem"); + + const a = document.createElement("a"); + a.setAttribute("data-draggable", "true"); + a.setAttribute("role", "tab"); + a.setAttribute("tabindex", "-1"); + a.setAttribute("title", tabTitle); + a.setAttribute("aria-selected", "false"); + a.setAttribute("href", url); + a.classList.add("tabHeader", "slds-context-bar__label-action"); + a.style.zIndex = 0; + + const span = document.createElement("span"); + span.classList.add("title", "slds-truncate"); + span.textContent = tabTitle; + + a.appendChild(span); + li.appendChild(a); + + // Highlight the tab related to the current page + if (href === url) { + li.classList.add("slds-is-active"); + } + + return li; } function generateSldsToastMessage(message, isSuccess) { @@ -123,8 +153,8 @@ function initTabs() { function generateFavouriteButton() { const assetDir = chrome.runtime.getURL("assets"); - const star = chrome.runtime.getURL("assets/star.svg"); - const slashedStar = chrome.runtime.getURL("assets/slashed-star.svg"); + const star = chrome.runtime.getURL("assets/svgs/star.svg"); + const slashedStar = chrome.runtime.getURL("assets/svgs/slashed-star.svg"); return `