Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
87 changes: 87 additions & 0 deletions .github/workflows/chrome-extension.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
name: Chrome Extension

on:
# Build on PRs that touch extension code
pull_request:
paths:
- "apps/chrome-extension/**"
- "packages/core/**"
- "packages/common/**"

# Publish when a GitHub release is created (after changeset release)
release:
types: [published]

# Manual trigger for testing
workflow_dispatch:
inputs:
publish:
description: "Publish to Chrome Web Store"
required: false
default: "false"
type: choice
options:
- "false"
- "true"

concurrency:
group: chrome-extension-${{ github.ref }}
cancel-in-progress: true

jobs:
build:
name: Build Extension
runs-on: ubuntu-latest
steps:
- name: Check out
uses: actions/checkout@v4

- name: Install Bun
uses: oven-sh/setup-bun@v2
with:
bun-version-file: package.json

- name: Install dependencies
run: bun install --frozen-lockfile

- name: Build packages (core + common)
run: bun run build:packages

- name: Build Chrome extension
working-directory: apps/chrome-extension
run: bun run build

- name: Package extension
working-directory: apps/chrome-extension
run: cd dist && zip -r ../web3icons-chrome.zip .

- name: Upload build artifact
uses: actions/upload-artifact@v4
with:
name: chrome-extension
path: apps/chrome-extension/web3icons-chrome.zip
retention-days: 30

publish:
name: Publish to Chrome Web Store
needs: build
runs-on: ubuntu-latest
# Only publish on release events or manual trigger with publish=true
if: >
github.event_name == 'release' ||
(github.event_name == 'workflow_dispatch' && github.event.inputs.publish == 'true')
steps:
- name: Download build artifact
uses: actions/download-artifact@v4
with:
name: chrome-extension

- name: Upload to Chrome Web Store
uses: mnao305/chrome-extension-upload@v5.0.0
with:
file-path: web3icons-chrome.zip
extension-id: ${{ secrets.CHROME_EXTENSION_ID }}
client-id: ${{ secrets.CHROME_CLIENT_ID }}
client-secret: ${{ secrets.CHROME_CLIENT_SECRET }}
refresh-token: ${{ secrets.CHROME_REFRESH_TOKEN }}
publish: true
2 changes: 2 additions & 0 deletions apps/chrome-extension/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
dist/
node_modules/
Binary file added apps/chrome-extension/icons/icon-128.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added apps/chrome-extension/icons/icon-16.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added apps/chrome-extension/icons/icon-32.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added apps/chrome-extension/icons/icon-48.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
22 changes: 22 additions & 0 deletions apps/chrome-extension/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"manifest_version": 3,
"name": "Web3 Icons",
"version": "1.0.0",
"description": "Browse and copy 2,500+ crypto icons. Tokens, networks, wallets, and exchanges in branded, mono, and background variants.",
"permissions": ["clipboardWrite"],
"action": {
"default_popup": "popup.html",
"default_icon": {
"16": "icons/icon-16.png",
"32": "icons/icon-32.png",
"48": "icons/icon-48.png",
"128": "icons/icon-128.png"
}
},
"icons": {
"16": "icons/icon-16.png",
"32": "icons/icon-32.png",
"48": "icons/icon-48.png",
"128": "icons/icon-128.png"
}
}
21 changes: 21 additions & 0 deletions apps/chrome-extension/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"name": "@web3icons/chrome-extension",
"version": "1.0.0",
"private": true,
"description": "Browse and copy 2,500+ crypto icons from any tab. Tokens, networks, wallets, and exchanges in branded, mono, and background variants.",
"author": "AK <hey@akml.io>",
"scripts": {
"dev": "bun run build:css --watch & bun run build:js --watch",
"build": "bun run build:css && bun run build:js",
"build:css": "npx @tailwindcss/cli --input ./src/css/input.css --output ./dist/popup.css --minify",
"build:js": "bun run scripts/build.ts"
},
"dependencies": {
"@web3icons/core": "4.0.50",
"@web3icons/common": "0.11.45"
},
"devDependencies": {
"tailwindcss": "^4.1.11",
"typescript": ">=5"
}
}
46 changes: 46 additions & 0 deletions apps/chrome-extension/popup.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="popup.css">
</head>
<body class="bg-gray-darkest font-mono text-xs">
<main id="app" class="flex w-[360px] flex-col gap-3 p-4" style="min-height: 500px; max-height: 580px;">
<!-- Tabs -->
<div id="tabs" class="relative flex w-full border-b border-gray-lightest pb-2">
<div id="tab-indicator" class="absolute bottom-0 left-0 h-[1px] bg-white transition-all duration-150"></div>
</div>

<!-- Search -->
<div class="relative flex w-full items-center gap-2 rounded-full border border-gray-lightest bg-gray-light px-4 focus-within:border-primary">
<input
id="search"
type="text"
placeholder="Search icons..."
class="h-8 w-full bg-transparent text-white placeholder:text-white/40 focus:outline-none"
/>
</div>

<!-- Variant Toggle -->
<div id="variants" class="relative inline-flex w-full rounded-full border border-gray-lightest bg-gray-light p-1 text-white"></div>

<!-- Icon Grid -->
<div id="icon-grid" class="flex-1 overflow-y-auto -mx-4" style="max-height: 360px;">
<div id="icons" class="grid grid-cols-4 gap-0"></div>
</div>

<!-- Toast -->
<div id="toast" class="fixed bottom-16 left-1/2 -translate-x-1/2 rounded-full bg-primary px-4 py-2 text-xs text-white opacity-0 transition-opacity duration-200 pointer-events-none">
Copied!
</div>

<!-- Footer -->
<div class="flex items-center justify-between border-t border-gray-lightest pt-3 -mx-4 px-4">
<a href="https://web3icons.io" target="_blank" rel="noopener" class="text-white/30 hover:text-white/60 transition-colors">web3icons.io</a>
<a href="https://akml.io" target="_blank" rel="noopener" class="text-white/30 hover:text-white/60 transition-colors">by AK</a>
</div>
</main>
<script src="popup.js"></script>
</body>
</html>
31 changes: 31 additions & 0 deletions apps/chrome-extension/scripts/build.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { build } from 'bun'
import { cpSync, mkdirSync, existsSync } from 'fs'
import { join } from 'path'

const ROOT = join(import.meta.dir, '..')
const DIST = join(ROOT, 'dist')

// Ensure dist exists
mkdirSync(DIST, { recursive: true })

// Bundle popup.ts
await build({
entrypoints: [join(ROOT, 'src/popup.ts')],
outdir: DIST,
naming: 'popup.js',
minify: true,
target: 'browser',
format: 'esm',
})

// Copy static files
cpSync(join(ROOT, 'popup.html'), join(DIST, 'popup.html'))
cpSync(join(ROOT, 'manifest.json'), join(DIST, 'manifest.json'))

// Copy icons if they exist
const iconsDir = join(ROOT, 'icons')
if (existsSync(iconsDir)) {
cpSync(iconsDir, join(DIST, 'icons'), { recursive: true })
}

console.log('Build complete: dist/')
35 changes: 35 additions & 0 deletions apps/chrome-extension/src/css/input.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
@import "tailwindcss";

@theme {
--color-gray-lightest: #222222;
--color-gray-light: #131313;
--color-gray: #111111;
--color-gray-dark: #0F0F0F;
--color-gray-darker: #0B0B0B;
--color-gray-darkest: #080808;
--color-primary: #FF3D00;
}

body {
background-color: var(--color-gray-darkest);
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
font-size: 0.75rem;
}

/* Icon grid scrollbar */
#icon-grid::-webkit-scrollbar {
width: 4px;
}
#icon-grid::-webkit-scrollbar-track {
background: transparent;
}
#icon-grid::-webkit-scrollbar-thumb {
background: #222;
border-radius: 2px;
}

/* SVG sizing inside icon cards */
.icon-card svg {
width: 32px;
height: 32px;
}
Loading
Loading