Skip to content

feat: add a copper favicon for light and dark mode - #24

Open
Hamas-AboAlmaali wants to merge 1 commit into
masaok:mainfrom
Hamas-AboAlmaali:feat/favicon-icon
Open

Hamas-AboAlmaali wants to merge 1 commit into
masaok:mainfrom
Hamas-AboAlmaali:feat/favicon-icon

Conversation

@Hamas-AboAlmaali

Copy link
Copy Markdown

Closes #14

Why

The dashboard shipped the default Next.js favicon (the Vercel triangle), so the browser tab didn't show Copper's brand.

Scope

  • Add apps/web/src/app/icon.svg: the copper dot with a verdigris ring, matching the header Wordmark, with dark-mode colors via prefers-color-scheme.
  • Remove the default apps/web/src/app/favicon.ico.

Verification

curl -s localhost:3000/ | grep -o '<link rel="icon"[^>]*>' returns the /icon.svg link, and /icon.svg returns 200. The production build lists /icon.svg as a static route.

pnpm verify (all checks pass)

$ pnpm check && pnpm build
$ pnpm lint && pnpm test:scripts && pnpm check:docs && pnpm check:features && pnpm typecheck && pnpm test
$ biome check .
Checked 136 files in 59ms. No fixes applied.
$ node --test scripts/test/*.test.mjs
TAP version 13

Subtest: check-docs reports a missing file and a missing heading, and nothing else

ok 1 - check-docs reports a missing file and a missing heading, and nothing else

duration_ms: 1.866916
type: 'test'
...

Subtest: check-feature-map reports a route with no row

ok 2 - check-feature-map reports a route with no row

duration_ms: 0.400958
type: 'test'
...
1..2

tests 2

suites 0

pass 2

fail 0

cancelled 0

skipped 0

todo 0

duration_ms 34.938459

$ node scripts/check-docs.mjs
check-docs: 14 files, 0 problems
$ node scripts/check-feature-map.mjs
check-feature-map: 22 routes, 0 missing
$ turbo run typecheck

• turbo 2.11.7
• Packages in scope: @copper-analytics/next, @copper/core, @copper/db, @copper/ingest, @copper/tracker, @copper/ui, @copper/web
• Running typecheck in 7 packages
• Remote caching disabled

@copper-analytics/next:typecheck: cache miss, executing b84d7b42250665bb
@copper/core:typecheck: cache miss, executing 00fbf40c45537e7b
@copper/ingest:typecheck: cache miss, executing ffbf27d7b117f171
@copper/db:typecheck: cache miss, executing 30656100eeca7c35
@copper/tracker:build: cache miss, executing 7628726cda19cd92
@copper/ui:typecheck: cache miss, executing c91f6e4451ef537a
@copper/ui:typecheck: $ tsc --noEmit
@copper/tracker:build: $ node scripts/build.mjs
@copper-analytics/next:typecheck: $ tsc --noEmit
@copper/db:typecheck: $ tsc --noEmit
@copper/core:typecheck: $ tsc --noEmit
@copper/ingest:typecheck: $ tsc --noEmit
@copper/tracker:typecheck: cache miss, executing 56ac13561e1ed4f0
@copper/web:typecheck: cache miss, executing 4c5e0abe64c523f7
@copper/tracker:typecheck: $ tsc --noEmit
@copper/web:typecheck: $ next typegen && tsc --noEmit
@copper/web:typecheck: Generating route types...
@copper/web:typecheck: ✓ Types generated successfully

Tasks: 8 successful, 8 total
Cached: 0 cached, 8 total
Time: 771ms

$ turbo run test

• turbo 2.11.7
• Packages in scope: @copper-analytics/next, @copper/core, @copper/db, @copper/ingest, @copper/tracker, @copper/ui, @copper/web
• Running test in 7 packages
• Remote caching disabled

@copper-analytics/next:test: cache miss, executing aa0478fe9ce8029e
@copper/core:test: cache miss, executing a103e56f44d05567
@copper/db:test: cache miss, executing b7b5ed7f337eacde
@copper/ingest:test: cache miss, executing 7dafc1a3f92ae1a3
@copper/tracker:build: cache hit, replaying logs 7628726cda19cd92
@copper/tracker:build: $ node scripts/build.mjs
@copper/tracker:test: cache miss, executing 6fc0f4629aafebb0
@copper/web:test: cache miss, executing 666b21e83c67d03d
@copper/core:test: $ vitest run
@copper/ingest:test: $ vitest run
@copper-analytics/next:test: $ vitest run
@copper/tracker:test: $ vitest run
@copper/db:test: $ vitest run
@copper/web:test: $ vitest run --passWithNoTests
@copper/tracker:test:
@copper/tracker:test: �[1m�[30m�[46m RUN �[49m�[39m�[22m �[36mv5.0.3 �[39m�[90m/Users/hamas/work/open-source/copper-analytics/packages/tracker�[39m
@copper/tracker:test:
@copper/core:test:
@copper/core:test: �[1m�[30m�[46m RUN �[49m�[39m�[22m �[36mv5.0.3 �[39m�[90m/Users/hamas/work/open-source/copper-analytics/packages/core�[39m
@copper/core:test:
@copper-analytics/next:test:
@copper-analytics/next:test: �[1m�[30m�[46m RUN �[49m�[39m�[22m �[36mv5.0.3 �[39m�[90m/Users/hamas/work/open-source/copper-analytics/packages/next�[39m
@copper-analytics/next:test:
@copper/ingest:test:
@copper/ingest:test: �[1m�[30m�[46m RUN �[49m�[39m�[22m �[36mv5.0.3 �[39m�[90m/Users/hamas/work/open-source/copper-analytics/apps/ingest�[39m
@copper/ingest:test:
@copper/web:test:
@copper/web:test: �[1m�[30m�[46m RUN �[49m�[39m�[22m �[36mv5.0.3 �[39m�[90m/Users/hamas/work/open-source/copper-analytics/apps/web�[39m
@copper/web:test:
@copper/db:test:
@copper/db:test: �[1m�[30m�[46m RUN �[49m�[39m�[22m �[36mv5.0.3 �[39m�[90m/Users/hamas/work/open-source/copper-analytics/packages/db�[39m
@copper/db:test:
@copper/tracker:test: �[32m✓�[39m test/tracker.test.ts �[2m(�[22m�[2m6 tests�[22m�[2m)�[22m�[32m 4�[2mms�[22m�[39m
@copper-analytics/next:test: �[32m✓�[39m test/attributes.test.ts �[2m(�[22m�[2m2 tests�[22m�[2m)�[22m�[32m 4�[2mms�[22m�[39m
@copper/tracker:test:
@copper/tracker:test: �[2m Test Files �[22m �[1m�[32m1 passed�[39m�[22m�[90m (1)�[39m
@copper/tracker:test: �[2m Tests �[22m �[1m�[32m6 passed�[39m�[22m�[90m (6)�[39m
@copper/tracker:test: �[2m Start at �[22m 07:43:19
@copper/tracker:test: �[2m Duration �[22m 175ms�[2m (transform 64%, import 25%, tests 6%, worker 5%)�[22m
@copper/tracker:test:
@copper-analytics/next:test:
@copper-analytics/next:test: �[2m Test Files �[22m �[1m�[32m1 passed�[39m�[22m�[90m (1)�[39m
@copper-analytics/next:test: �[2m Tests �[22m �[1m�[32m2 passed�[39m�[22m�[90m (2)�[39m
@copper-analytics/next:test: �[2m Start at �[22m 07:43:19
@copper-analytics/next:test: �[2m Duration �[22m 179ms�[2m (transform 52%, import 39%, tests 6%, worker 3%)�[22m
@copper-analytics/next:test:
@copper/web:test: �[32m✓�[39m test/env.test.ts �[2m(�[22m�[2m3 tests�[22m�[2m)�[22m�[32m 3�[2mms�[22m�[39m
@copper/core:test: �[32m✓�[39m test/aggregator.test.ts �[2m(�[22m�[2m10 tests�[22m�[2m)�[22m�[32m 14�[2mms�[22m�[39m
@copper/core:test: �[32m✓�[39m test/ranges.test.ts �[2m(�[22m�[2m8 tests�[22m�[2m)�[22m�[32m 15�[2mms�[22m�[39m
@copper/core:test: �[32m✓�[39m test/core.test.ts �[2m(�[22m�[2m13 tests�[22m�[2m)�[22m�[32m 40�[2mms�[22m�[39m
@copper/core:test:
@copper/core:test: �[2m Test Files �[22m �[1m�[32m3 passed�[39m�[22m�[90m (3)�[39m
@copper/core:test: �[2m Tests �[22m �[1m�[32m31 passed�[39m�[22m�[90m (31)�[39m
@copper/core:test: �[2m Start at �[22m 07:43:19
@copper/core:test: �[2m Duration �[22m 290ms�[2m (transform 73%, tests 14%, import 12%, worker 1%)�[22m
@copper/core:test:
@copper/ingest:test: �[32m✓�[39m test/app.test.ts �[2m(�[22m�[2m16 tests�[22m�[2m)�[22m�[32m 40�[2mms�[22m�[39m
@copper/ingest:test: �[32m✓�[39m test/shard.test.ts �[2m(�[22m�[2m11 tests�[22m�[2m)�[22m�[32m 52�[2mms�[22m�[39m
@copper/web:test: �[32m✓�[39m test/params.test.ts �[2m(�[22m�[2m2 tests�[22m�[2m)�[22m�[32m 2�[2mms�[22m�[39m
@copper/web:test: �[32m✓�[39m test/validate.test.ts �[2m(�[22m�[2m3 tests�[22m�[2m)�[22m�[32m 22�[2mms�[22m�[39m
@copper/web:test:
@copper/web:test: �[2m Test Files �[22m �[1m�[32m3 passed�[39m�[22m�[90m (3)�[39m
@copper/web:test: �[2m Tests �[22m �[1m�[32m8 passed�[39m�[22m�[90m (8)�[39m
@copper/web:test: �[2m Start at �[22m 07:43:19
@copper/web:test: �[2m Duration �[22m 254ms�[2m (transform 77%, import 13%, tests 8%, worker 2%)�[22m
@copper/web:test:
@copper/ingest:test: �[32m✓�[39m test/pipeline.test.ts �[2m(�[22m�[2m1 test�[22m�[2m)�[22m�[33m 1322�[2mms�[22m�[39m
@copper/ingest:test: �[32m✓�[39m one hour of traffic, end to end �[2m(1)�[22m
@copper/ingest:test: �[33m�[2m✓�[22m�[39m lands in stats_hourly, stats_daily and breakdown_daily with the right totals�[33m 1321�[2mms�[22m�[39m
@copper/ingest:test:
@copper/ingest:test: �[2m Test Files �[22m �[1m�[32m3 passed�[39m�[22m�[90m (3)�[39m
@copper/ingest:test: �[2m Tests �[22m �[1m�[32m28 passed�[39m�[22m�[90m (28)�[39m
@copper/ingest:test: �[2m Start at �[22m 07:43:19
@copper/ingest:test: �[2m Duration �[22m 1.80s�[2m (tests 67%, transform 21%, import 12%)�[22m
@copper/ingest:test:
@copper/db:test: �[32m✓�[39m test/migrate.test.ts �[2m(�[22m�[2m4 tests�[22m�[2m)�[22m�[33m 2138�[2mms�[22m�[39m
@copper/db:test: �[32m✓�[39m migrations �[2m(4)�[22m
@copper/db:test: �[33m�[2m✓�[22m�[39m apply from an empty database and create every table in the data model�[33m 1019�[2mms�[22m�[39m
@copper/db:test: �[33m�[2m✓�[22m�[39m skip what is already applied�[33m 591�[2mms�[22m�[39m
@copper/db:test: �[33m�[2m✓�[22m�[39m roll a failing migration back and do not record it�[33m 525�[2mms�[22m�[39m
@copper/db:test: �[32m✓�[39m test/queries.test.ts �[2m(�[22m�[2m7 tests�[22m�[2m)�[22m�[33m 4245�[2mms�[22m�[39m
@copper/db:test: �[32m✓�[39m seriesFor �[2m(2)�[22m
@copper/db:test: �[33m�[2m✓�[22m�[39m returns hourly points inside the span, for one project�[33m 1188�[2mms�[22m�[39m
@copper/db:test: �[33m�[2m✓�[22m�[39m returns daily points and groups them by month�[33m 579�[2mms�[22m�[39m
@copper/db:test: �[32m✓�[39m totalsFor �[2m(1)�[22m
@copper/db:test: �[33m�[2m✓�[22m�[39m sums a span and returns zeros for an empty one�[33m 544�[2mms�[22m�[39m
@copper/db:test: �[32m✓�[39m breakdownFor �[2m(2)�[22m
@copper/db:test: �[33m�[2m✓�[22m�[39m merges daily documents in the span�[33m 481�[2mms�[22m�[39m
@copper/db:test: �[33m�[2m✓�[22m�[39m uses the monthly document for rolled-up months and daily ones for the rest�[33m 476�[2mms�[22m�[39m
@copper/db:test: �[32m✓�[39m overview and export �[2m(2)�[22m
@copper/db:test: �[33m�[2m✓�[22m�[39m returns daily rows for several projects since a day�[33m 487�[2mms�[22m�[39m
@copper/db:test: �[33m�[2m✓�[22m�[39m exports every daily row and finds project owners�[33m 489�[2mms�[22m�[39m
@copper/db:test: �[32m✓�[39m test/store.test.ts �[2m(�[22m�[2m11 tests�[22m�[2m)�[22m�[33m 6082�[2mms�[22m�[39m
@copper/db:test: �[32m✓�[39m applyFlush �[2m(9)�[22m
@copper/db:test: �[33m�[2m✓�[22m�[39m writes hourly, daily and breakdown rows with the totals it was given�[33m 1197�[2mms�[22m�[39m
@copper/db:test: �[33m�[2m✓�[22m�[39m does not double count a replayed flush�[33m 583�[2mms�[22m�[39m
@copper/db:test: �[33m�[2m✓�[22m�[39m applies the new hour and skips the old one when a retry carries both�[33m 539�[2mms�[22m�[39m
@copper/db:test: �[33m�[2m✓�[22m�[39m treats the same flush id on another shard as a different flush�[33m 484�[2mms�[22m�[39m
@copper/db:test: �[33m�[2m✓�[22m�[39m rolls the whole flush back when a write fails, so a retry applies it once�[33m 479�[2mms�[22m�[39m
@copper/db:test: �[33m�[2m✓�[22m�[39m buckets an hour into the project's local day�[33m 485�[2mms�[22m�[39m
@copper/db:test: �[33m�[2m✓�[22m�[39m adds two partial units for the same hour�[33m 487�[2mms�[22m�[39m
@copper/db:test: �[33m�[2m✓�[22m�[39m ignores site keys it does not know and still records the flush�[33m 459�[2mms�[22m�[39m
@copper/db:test: �[33m�[2m✓�[22m�[39m keeps the daily breakdown at 50 entries per dimension plus (other)�[33m 448�[2mms�[22m�[39m
@copper/db:test: �[32m✓�[39m projectConfig �[2m(1)�[22m
@copper/db:test: �[33m�[2m✓�[22m�[39m returns domains and cap, and hides unknown or disabled projects�[33m 464�[2mms�[22m�[39m
@copper/db:test: �[32m✓�[39m runDaily �[2m(1)�[22m
@copper/db:test: �[33m�[2m✓�[22m�[39m applies retention, rolls finished months up and records the database size�[33m 456�[2mms�[22m�[39m
@copper/db:test:
@copper/db:test: �[2m Test Files �[22m �[1m�[32m3 passed�[39m�[22m�[90m (3)�[39m
@copper/db:test: �[2m Tests �[22m �[1m�[32m22 passed�[39m�[22m�[90m (22)�[39m
@copper/db:test: �[2m Start at �[22m 07:43:19
@copper/db:test: �[2m Duration �[22m 6.52s�[2m (tests 93%, import 5%, transform 2%)�[22m
@copper/db:test:

Tasks: 7 successful, 7 total
Cached: 1 cached, 7 total
Time: 6.898s

$ turbo run build

• turbo 2.11.7
• Packages in scope: @copper-analytics/next, @copper/core, @copper/db, @copper/ingest, @copper/tracker, @copper/ui, @copper/web
• Running build in 7 packages
• Remote caching disabled

@copper/ingest:build: cache miss, executing 00415607ed4c6b86
@copper/tracker:build: cache hit, replaying logs 7628726cda19cd92
@copper/tracker:build: $ node scripts/build.mjs
@copper/web:build: cache miss, executing bb380ef3fb003786
@copper/ingest:build: $ wrangler deploy --dry-run --outdir dist
@copper/web:build: $ next build
@copper/web:build: ▲ Next.js 16.3.8 (Turbopack)
@copper/web:build: - Environments: .env.local
@copper/web:build: ✓ Running next.config.ts took 13ms
@copper/web:build:
@copper/web:build: Creating an optimized production build ...
@copper/ingest:build:
@copper/ingest:build: ⛅️ wrangler 4.147.0
@copper/ingest:build: ────────────────────
@copper/ingest:build: Total Upload: 481.63 KiB / gzip: 108.87 KiB
@copper/ingest:build: Your Worker has access to the following bindings:
@copper/ingest:build: Binding Resource
@copper/ingest:build: env.SHARD (Shard) Durable Object
@copper/ingest:build: env.APP_URL ("") Environment Variable
@copper/ingest:build:
@copper/ingest:build: --dry-run: exiting now.
@copper/web:build: ✓ Compiled successfully in 672ms
@copper/web:build: Running TypeScript ...
@copper/web:build: Finished TypeScript in 207ms ...
@copper/web:build: Collecting page data using 11 workers ...
@copper/web:build: Generating static pages using 11 workers (0/11) ...
@copper/web:build: Generating static pages using 11 workers (2/11)
@copper/web:build: Generating static pages using 11 workers (5/11)
@copper/web:build: Generating static pages using 11 workers (8/11)
@copper/web:build: ✓ Generating static pages using 11 workers (11/11) in 105ms
@copper/web:build: Finalizing page optimization ...
@copper/web:build:
@copper/web:build: Route (app)
@copper/web:build: ┌ ƒ /
@copper/web:build: ├ ○ /_not-found
@copper/web:build: ├ ƒ /api/auth/[...all]
@copper/web:build: ├ ƒ /api/e
@copper/web:build: ├ ƒ /api/health
@copper/web:build: ├ ƒ /api/live/[siteKey]
@copper/web:build: ├ ƒ /api/revalidate
@copper/web:build: ├ ƒ /api/v1/stats
@copper/web:build: ├ ƒ /c.js
@copper/web:build: ├ ƒ /dashboard
@copper/web:build: ├ ƒ /docs
@copper/web:build: ├ ○ /icon.svg
@copper/web:build: ├ ƒ /new
@copper/web:build: ├ ƒ /p/[siteKey]
@copper/web:build: ├ ƒ /p/[siteKey]/export.csv
@copper/web:build: ├ ƒ /p/[siteKey]/settings
@copper/web:build: └ ƒ /share/[siteKey]
@copper/web:build:
@copper/web:build:
@copper/web:build: ○ (Static) prerendered as static content
@copper/web:build: ƒ (Dynamic) server-rendered on demand
@copper/web:build:

Tasks: 3 successful, 3 total
Cached: 1 cached, 3 total
Time: 2.792s

@vercel

vercel Bot commented Oct 6, 2026

Copy link
Copy Markdown

@Hamas-AboAlmaali is attempting to deploy a commit to the MasaoK Team Team on Vercel.

A member of the Team first needs to authorize it.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add a favicon and app icon to the dashboard

1 participant