Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
72 commits
Select commit Hold shift + click to select a range
ad536f4
fix: silence error logging for various operations
hoangsvit Jul 20, 2026
168a08b
feat: implement getActiveEmail function for improved email account ha…
hoangsvit Jul 20, 2026
6b70cbd
perf: optimize performance, fix bugs, add base email to inline popup
hoangsvit Jul 20, 2026
56cbed5
fix: remove unused error variable in catch block
hoangsvit Jul 20, 2026
74bde55
style: format code with Prettier
deepsource-autofix[bot] Jul 20, 2026
b6e4498
fix: prevent placeholder email usage when storage fails
hoangsvit Jul 20, 2026
c8b3c86
fix: surface disable inline helper errors to user
hoangsvit Jul 20, 2026
0bb5816
fix: surface alias-save failures with user feedback
hoangsvit Jul 20, 2026
94dcefa
fix: clean up partial icon initialization on error
hoangsvit Jul 20, 2026
c91fb33
style: format code with Prettier
deepsource-autofix[bot] Jul 20, 2026
9a187ee
fix: apply CodeRabbit auto-fixes
coderabbitai[bot] Jul 20, 2026
c9e136d
style: format code with Prettier
deepsource-autofix[bot] Jul 20, 2026
b6a3cb6
Merge pull request #68 from ePlus-DEV/coderabbitai/autofix/c91fb33
hoangsvit Jul 20, 2026
ba0e8ab
dev: limit extension to dev sites to reduce reload spam
hoangsvit Jul 20, 2026
235f04c
fix: correct WXT env detection for dev/build modes
hoangsvit Jul 20, 2026
d6e0e19
style: format code with Prettier
deepsource-autofix[bot] Jul 20, 2026
6434c8b
fix: remove unused originalTitle variable
hoangsvit Jul 20, 2026
60ace93
fix: include activeEmail in context menu cache key
hoangsvit Jul 20, 2026
e8fd212
fix: show distinct error state for history load failures
hoangsvit Jul 20, 2026
0535079
fix: update DEV_SITES to correct URLs and remove unused entries
hoangsvit Jul 20, 2026
ef912fe
style: format code with Prettier
deepsource-autofix[bot] Jul 20, 2026
4530981
fix: remove unused DEV_SITES entries for cleaner configuration
hoangsvit Jul 20, 2026
98e5959
fix: ensure iconContainer is not null and update state references in …
hoangsvit Jul 20, 2026
3958604
Refactor code structure for improved readability and maintainability
hoangsvit Jul 20, 2026
77574d8
chore: add one-time DeepSource fix workflow
hoangsvit Jul 20, 2026
0e259c1
fix: make one-time refactor workflow deterministic
hoangsvit Jul 20, 2026
afa5471
fix: remove unsafe non-null assertions
github-actions[bot] Jul 20, 2026
e41e019
chore: remove one-time DeepSource fix workflow
hoangsvit Jul 20, 2026
0f2f226
ci: prepare one-time changelog update
hoangsvit Jul 20, 2026
f31a1dc
ci: trigger one-time changelog update
hoangsvit Jul 20, 2026
6593627
ci: add one-time 1.3.1 changelog step
hoangsvit Jul 20, 2026
4c8fee3
ci: run one-time changelog step before validation
hoangsvit Jul 20, 2026
026ed39
docs: add changelog for 1.3.1
github-actions[bot] Jul 20, 2026
6aa3a11
ci: restore standard workflow after changelog update
hoangsvit Jul 20, 2026
4d3cdff
ci: remove temporary changelog workflow
hoangsvit Jul 20, 2026
dd9d3ef
ci: temporarily restore release-managed changelog
hoangsvit Jul 20, 2026
5c50896
revert: keep changelog release-managed
github-actions[bot] Jul 20, 2026
2e4d6f9
ci: restore standard workflow
hoangsvit Jul 20, 2026
198e720
feat(web): add browser and mobile landing enhancements
hoangsvit Jul 20, 2026
5424853
style: format code with Prettier
deepsource-autofix[bot] Jul 20, 2026
a2cd354
style(web): add responsive landing and dark mode styles
hoangsvit Jul 20, 2026
4d63d84
feat(web): wire landing enhancements and social metadata
hoangsvit Jul 20, 2026
4060d6e
style: format code with Prettier
deepsource-autofix[bot] Jul 20, 2026
7654278
perf(web): avoid duplicate Tailwind output
hoangsvit Jul 20, 2026
9e64077
style: format code with Prettier
deepsource-autofix[bot] Jul 20, 2026
5537b3f
ci: prepare one-time web documentation fix
hoangsvit Jul 20, 2026
01fae37
ci: trigger web documentation fix
hoangsvit Jul 20, 2026
8b92fe4
docs(web): document landing enhancement functions
github-actions[bot] Jul 20, 2026
90edfff
seo(web): improve crawler and social metadata
hoangsvit Jul 20, 2026
b884365
feat(web): add interactive product tour
hoangsvit Jul 20, 2026
7e97d8e
chore: prepare phase two integration
hoangsvit Jul 20, 2026
c71b885
chore: trigger phase two integration
hoangsvit Jul 20, 2026
e9d937b
style: format code with Prettier
deepsource-autofix[bot] Jul 20, 2026
6a6cd03
ci: integrate phase two once
hoangsvit Jul 20, 2026
e85d879
ci: retry phase two integration
hoangsvit Jul 20, 2026
2c41d4e
feat(web): integrate phase two product tour
github-actions[bot] Jul 20, 2026
3f4cf85
docs(web): describe interactive product tour
hoangsvit Jul 20, 2026
5a33c1a
ci: apply phase two quality fixes once
hoangsvit Jul 20, 2026
97fbe58
ci: retry phase two quality fixes
hoangsvit Jul 20, 2026
8b1f528
docs(web): expand product tour keywords
hoangsvit Jul 20, 2026
ed85f8a
ci: restore standard workflow
hoangsvit Jul 20, 2026
e30a040
refactor(web): extract phase two content
hoangsvit Jul 20, 2026
a18e73e
style: format code with Prettier
deepsource-autofix[bot] Jul 20, 2026
b7811a0
refactor(web): extract product tour
hoangsvit Jul 20, 2026
86e20d8
refactor(web): extract manual install guide
hoangsvit Jul 20, 2026
a67ce08
refactor(web): split phase two sections
hoangsvit Jul 20, 2026
495fb7f
style: format code with Prettier
deepsource-autofix[bot] Jul 20, 2026
5a4f5c7
chore(web): scope analysis for interactive demo
hoangsvit Jul 20, 2026
2bf1e95
chore(web): exclude phase two demo modules
hoangsvit Jul 20, 2026
aea2772
feat(web): support configurable preview base path
hoangsvit Jul 20, 2026
d2fa609
ci(web): add live pull request previews
hoangsvit Jul 20, 2026
ea11faf
fix(ci): use dedicated PR preview environment
hoangsvit Jul 20, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .deepsource.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ exclude_patterns = [
"docs/**",
"public/**",
"web/public/**",
"web/src/phase-two/**",
"yarn.lock",
"src/components/motion/**",
]
Expand Down
154 changes: 154 additions & 0 deletions .github/workflows/pr-preview.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
name: Deploy PR Preview

on:
pull_request:
branches: [main]
types: [opened, reopened, synchronize, closed]
paths:
- "web/**"
- "package.json"
- "yarn.lock"
- ".yarnrc.yml"
- ".github/workflows/pr-preview.yml"

concurrency:
group: pages
cancel-in-progress: false

permissions:
contents: read
pages: write
id-token: write
pull-requests: write

jobs:
build:
name: Build preview site
if: github.event.pull_request.head.repo.full_name == github.repository
runs-on: ubuntu-latest

steps:
- name: Checkout production site
uses: actions/checkout@v7
with:
ref: main
path: main-site

- name: Checkout pull request
if: github.event.action != 'closed'
uses: actions/checkout@v7
with:
ref: ${{ github.event.pull_request.head.sha }}
path: preview-site

- name: Enable Corepack
run: corepack enable

- name: Setup Node.js
uses: actions/setup-node@v7
with:
node-version: 24.x
cache: yarn
cache-dependency-path: main-site/yarn.lock

- name: Build production site
working-directory: main-site
run: |
yarn install --immutable --mode=skip-build
yarn exec wxt prepare
yarn workspace gmail-alias-toolkit-web build

- name: Build pull request preview
if: github.event.action != 'closed'
working-directory: preview-site
env:
VITE_BASE_PATH: /gmail-alias-toolkit/pr-preview/pr-${{ github.event.number }}/
run: |
yarn install --immutable --mode=skip-build
yarn exec wxt prepare
yarn workspace gmail-alias-toolkit-web build

- name: Assemble Pages artifact
shell: bash
run: |
set -euo pipefail
rm -rf site
mkdir -p site
cp -R main-site/web/dist/. site/

if [[ "${{ github.event.action }}" != "closed" ]]; then
preview_dir="site/pr-preview/pr-${{ github.event.number }}"
mkdir -p "$preview_dir"
cp -R preview-site/web/dist/. "$preview_dir/"
fi

touch site/.nojekyll

- name: Configure GitHub Pages
uses: actions/configure-pages@v6

- name: Upload Pages artifact
uses: actions/upload-pages-artifact@v5
with:
path: site

deploy:
name: Deploy PR preview
runs-on: ubuntu-latest
needs: build
environment:
name: pr-preview
url: https://eplus-dev.github.io/gmail-alias-toolkit/pr-preview/pr-${{ github.event.number }}/
outputs:
page_url: ${{ steps.deployment.outputs.page_url }}

steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v5

comment:
name: Update preview link
runs-on: ubuntu-latest
needs: deploy

steps:
- name: Comment preview URL
uses: actions/github-script@v8
env:
PAGE_URL: ${{ needs.deploy.outputs.page_url }}
with:
script: |
const marker = "<!-- gmail-alias-toolkit-pr-preview -->";
const rootUrl = process.env.PAGE_URL.replace(/\/?$/, "/");
const previewUrl = `${rootUrl}pr-preview/pr-${context.issue.number}/`;
const isClosed = context.payload.action === "closed";
const body = isClosed
? `${marker}\n## Preview removed\n\nThe temporary website preview for this pull request has been removed.`
: `${marker}\n## Website preview ready\n\n[Open PR preview ↗](${previewUrl})\n\nThis preview is rebuilt automatically whenever the pull request changes.`;

const { data: comments } = await github.rest.issues.listComments({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
per_page: 100,
});
const existing = comments.find((comment) =>
comment.body?.includes(marker),
);

if (existing) {
await github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: existing.id,
body,
});
} else {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body,
});
}
126 changes: 60 additions & 66 deletions entrypoints/background.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,23 @@ interface AppSettings {
badgeDisplay?: "none" | "total" | "all-time" | "today" | "week";
}

/** Get active email account from storage result. */
function getActiveEmail(
accountResult: { email_accounts?: EmailAccount[]; base_email?: string },
fallback = "your.email@gmail.com",
): string {
if (
accountResult.email_accounts &&
Array.isArray(accountResult.email_accounts)
) {
const activeAccount = accountResult.email_accounts.find(
(acc) => acc.isActive,
);
if (activeAccount) return activeAccount.email;
}
return accountResult.base_email || fallback;
}

Comment thread
coderabbitai[bot] marked this conversation as resolved.
export default defineBackground(() => {
// Create context menu on install
browser.runtime.onInstalled.addListener(async () => {
Expand Down Expand Up @@ -314,16 +331,7 @@ export default defineBackground(() => {
base_email?: string;
app_settings?: AppSettings;
};
let baseEmail = "your.email@gmail.com";

if (result.email_accounts && Array.isArray(result.email_accounts)) {
const activeAccount = result.email_accounts.find((acc) => acc.isActive);
if (activeAccount) {
baseEmail = activeAccount.email;
}
} else if (result.base_email) {
baseEmail = result.base_email;
}
const baseEmail = getActiveEmail(result);

const [username, domain] = baseEmail.split("@");
let emailToFill = "";
Expand Down Expand Up @@ -355,8 +363,8 @@ export default defineBackground(() => {
await saveWebsiteAlias(baseEmail, normalized, emailToFill);
}
}
} catch (error) {
console.debug("Error saving website alias:", error);
} catch {
// Silently fail
}
}

Expand Down Expand Up @@ -387,21 +395,7 @@ export default defineBackground(() => {
"email_accounts",
"base_email",
])) as { email_accounts?: EmailAccount[]; base_email?: string };
let activeEmail = "your.email@gmail.com";

if (
accountResult.email_accounts &&
Array.isArray(accountResult.email_accounts)
) {
const activeAccount = accountResult.email_accounts.find(
(acc) => acc.isActive,
);
if (activeAccount) {
activeEmail = activeAccount.email;
}
} else if (accountResult.base_email) {
activeEmail = accountResult.base_email;
}
const activeEmail = getActiveEmail(accountResult);

// Get history for active account
const historyKey = getAccountStorageKey(
Expand Down Expand Up @@ -457,8 +451,8 @@ export default defineBackground(() => {
} else {
await browser.action.setBadgeText({ text: "" });
}
} catch (error) {
console.error("Error updating badge:", error);
} catch {
// Silently fail
}
}

Expand All @@ -485,22 +479,7 @@ export default defineBackground(() => {
"email_accounts",
"base_email",
])) as { email_accounts?: EmailAccount[]; base_email?: string };
let activeEmail = accountEmail?.trim() || "your.email@gmail.com";

if (
!accountEmail &&
accountResult.email_accounts &&
Array.isArray(accountResult.email_accounts)
) {
const activeAccount = accountResult.email_accounts.find(
(acc) => acc.isActive,
);
if (activeAccount) {
activeEmail = activeAccount.email;
}
} else if (!accountEmail && accountResult.base_email) {
activeEmail = accountResult.base_email;
}
const activeEmail = accountEmail?.trim() || getActiveEmail(accountResult);

// Use account-specific storage keys
const historyKey = getAccountStorageKey(activeEmail, "gmail_alias_recent");
Expand Down Expand Up @@ -581,27 +560,42 @@ export default defineBackground(() => {
"email_accounts",
"base_email",
])) as { email_accounts?: EmailAccount[]; base_email?: string };
let activeEmail = "your.email@gmail.com";

if (
accountResult.email_accounts &&
Array.isArray(accountResult.email_accounts)
) {
const activeAccount = accountResult.email_accounts.find(
(acc) => acc.isActive,
const activeEmail = getActiveEmail(accountResult);

// Check cache: use cached suggestions only if both URL and email match
const cacheResult = (await browser.storage.session?.get?.(
"contextMenuWebsiteCache",
)) as
| {
contextMenuWebsiteCache?: {
url: string;
activeEmail: string;
suggestions: string[];
};
}
| undefined;
const cached = cacheResult?.contextMenuWebsiteCache;

let suggestions: string[];
if (cached?.url === info.pageUrl && cached?.activeEmail === activeEmail) {
suggestions = cached.suggestions;
} else {
// Generate suggestions and update cache
suggestions = await generateSuggestionsForWebsite(
activeEmail,
info.pageUrl,
);
if (activeAccount) {
activeEmail = activeAccount.email;
if (browser.storage.session) {
await browser.storage.session.set({
contextMenuWebsiteCache: {
url: info.pageUrl,
activeEmail,
suggestions,
},
});
}
} else if (accountResult.base_email) {
activeEmail = accountResult.base_email;
}

// Generate suggestions
const suggestions = await generateSuggestionsForWebsite(
activeEmail,
info.pageUrl,
);
if (suggestions.length === 0) return;

// Store suggestions in session storage for retrieval in click handler
Expand Down Expand Up @@ -629,17 +623,18 @@ export default defineBackground(() => {
);

for (const [index, suggestion] of suggestions.slice(0, 3).entries()) {
const [username, domain] = suggestion.split("@");
browser.contextMenus.create({
id: `website-suggestion-${index}`,
parentId: "website-alias-parent",
title: suggestion.split("@")[0],
title: `${username}@${domain}`,
contexts: ["editable"],
});
}

await dynamicContextMenus.refresh?.();
} catch (error) {
console.debug("Error updating website suggestions:", error);
} catch {
// Silently fail
}
});

Expand Down Expand Up @@ -679,7 +674,6 @@ export default defineBackground(() => {
sendResponse({ success: true });
}
} catch (error) {
console.error("Message handler error:", error);
sendResponse({ error: String(error) });
}
},
Expand Down
9 changes: 9 additions & 0 deletions entrypoints/content/email-helper.css
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,15 @@ input.gmail-alias-input-preview {
box-shadow: inset 0 0 0 1px #10b981 !important;
}

/* Input error effect */
input.gmail-alias-input-error {
background-color: #fee2e2 !important;
border-color: #dc2626 !important;
color: #7f1d1d !important;
-webkit-text-fill-color: #7f1d1d !important;
box-shadow: inset 0 0 0 1px #dc2626 !important;
}

input.gmail-alias-input-highlight::placeholder,
input.gmail-alias-input-preview::placeholder {
color: #6b7280 !important;
Expand Down
Loading