diff --git a/package.json b/package.json index 7957745..7a2778c 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "keybpmmap", "private": true, - "version": "0.5.0", + "version": "0.6.0", "type": "module", "scripts": { "dev": "vite", diff --git a/public/bg.png b/public/bg.png new file mode 100644 index 0000000..453f24b Binary files /dev/null and b/public/bg.png differ diff --git a/public/social/apple-music.png b/public/social/apple-music.png new file mode 100644 index 0000000..b13411e Binary files /dev/null and b/public/social/apple-music.png differ diff --git a/public/social/bandcamp.png b/public/social/bandcamp.png new file mode 100644 index 0000000..8e56458 Binary files /dev/null and b/public/social/bandcamp.png differ diff --git a/public/social/beatport.png b/public/social/beatport.png new file mode 100644 index 0000000..6272403 Binary files /dev/null and b/public/social/beatport.png differ diff --git a/public/social/facebook.png b/public/social/facebook.png new file mode 100644 index 0000000..03c625d Binary files /dev/null and b/public/social/facebook.png differ diff --git a/public/social/instagram.png b/public/social/instagram.png new file mode 100644 index 0000000..b2180fc Binary files /dev/null and b/public/social/instagram.png differ diff --git a/public/social/soundcloud.png b/public/social/soundcloud.png new file mode 100644 index 0000000..6bee18e Binary files /dev/null and b/public/social/soundcloud.png differ diff --git a/public/social/spotify.png b/public/social/spotify.png new file mode 100644 index 0000000..65c811f Binary files /dev/null and b/public/social/spotify.png differ diff --git a/public/social/tidal.png b/public/social/tidal.png new file mode 100644 index 0000000..40f45cb Binary files /dev/null and b/public/social/tidal.png differ diff --git a/src/App.css b/src/App.css index 439cabe..cc5ad3a 100644 --- a/src/App.css +++ b/src/App.css @@ -7,7 +7,7 @@ } .panel { - background: linear-gradient(180deg, rgba(17, 24, 39, 0.92), rgba(7, 11, 20, 0.96)); + background: linear-gradient(180deg, rgba(17, 24, 39, 0.8), rgba(7, 11, 20, 0.82)); border: 1px solid rgba(148, 163, 184, 0.18); border-radius: 24px; box-shadow: 0 18px 50px rgba(2, 6, 23, 0.35); @@ -69,6 +69,44 @@ display: grid; gap: 8px; color: #cbd5e1; + font-size: 0.7rem; +} + +.hero-social-links { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 10px; +} + +.hero-social-link { + width: 40px; + height: 40px; + display: inline-flex; + align-items: center; + justify-content: center; + border-radius: 999px; + border: 0px; + background: radial-gradient(circle, #ffffff3b 0%, #ffffff00 80%); + opacity: 0.9; + transition: + transform 120ms ease, + border-color 120ms ease, + opacity 120ms ease, + background 120ms ease; +} + +.hero-social-link:hover, +.hero-social-link:focus-visible { + transform: translateY(-1px); + opacity: 1; + background: radial-gradient(circle, #7300ff3b 0%, #00a6ff41 80%); +} + +.hero-social-link img { + width: 32px; + height: 32px; + object-fit: contain; } .primary-button, @@ -463,6 +501,17 @@ input[type='checkbox'] { color: #cbd5e1; } +.track-title-link { + color: inherit; + text-decoration: underline; + text-underline-offset: 0.18em; +} + +.track-title-link:hover, +.track-title-link:focus-visible { + color: #e2e8f0; +} + .track-meta { display: flex; flex-wrap: wrap; diff --git a/src/App.tsx b/src/App.tsx index e70b7f2..b7aef02 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -588,8 +588,91 @@ function App() {
  • Reads Traktor collection.nml files and their backups locally in the browser.
  • Reads Rekordbox XML exports locally in the browser.
  • Understands Engine DJ, Traktor, and Rekordbox playlist relationships.
  • -
  • Exports both charts as PNG snapshots for notes or prep docs.
  • +
  • Exports both charts as PNG snapshots.
  • +
    + Find my music and socials: + + + + + + + + + + + + + + + + + + + + + + + + +
    @@ -788,7 +871,9 @@ function App() { return ( {track.artist} - {track.title} + + + {track.bpm === null ? 'No BPM' : track.bpm.toFixed(1)} {formatVisibleKey(track.camelotKey)} @@ -1092,8 +1177,8 @@ function App() {
    Transition {index + 1} - {fromTrack ? formatTrackLabel(fromTrack) : step.fromTrackId} →{' '} - {toTrack ? formatTrackLabel(toTrack) : step.toTrackId} + {fromTrack ? : step.fromTrackId}{' '} + → {toTrack ? : step.toTrackId}
    @@ -1221,7 +1306,9 @@ function App() {
  • {track.artist} - {track.title} + + +
    {track.bpm === null ? 'No BPM' : `${track.bpm.toFixed(1)} BPM`} @@ -1387,8 +1474,44 @@ function formatKeyRepresentationLabel(representation: KeyRepresentation): string } } -function formatTrackLabel(track: TrackRecord): string { - return `${track.artist} — ${track.title}` +function TrackTitleLink({ track }: { track: TrackRecord }) { + const externalTrackUrl = getExternalTrackUrl(track.path) + if (!externalTrackUrl) { + return <>{track.title} + } + + return ( + + {track.title} + + ) +} + +function TrackLabelWithLinkedTitle({ track }: { track: TrackRecord }) { + return ( + <> + {track.artist} — + + ) +} + +function getExternalTrackUrl(path: string): string | null { + try { + const parsed = new URL(path) + if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') { + return null + } + return parsed.toString() + } catch { + return null + } } function formatTrackSearchLabel(track: TrackRecord, keyRepresentation: KeyRepresentation): string { diff --git a/src/index.css b/src/index.css index 572a291..cdfe2dd 100644 --- a/src/index.css +++ b/src/index.css @@ -7,7 +7,8 @@ color: #e2e8f0; background: radial-gradient(circle at top, rgba(37, 99, 235, 0.16), transparent 30%), - linear-gradient(180deg, #020617, #0f172a 35%, #020617 100%); + linear-gradient(180deg, rgba(2, 6, 23, 0.5), rgba(15, 23, 42, 0.5) 35%, rgba(2, 6, 23, 0.5) 100%), + url('/bg.png') center / cover no-repeat fixed; font-synthesis: none; text-rendering: optimizeLegibility; -webkit-font-smoothing: antialiased; diff --git a/src/lib/mockData.ts b/src/lib/mockData.ts index fa4fc4c..9375e24 100644 --- a/src/lib/mockData.ts +++ b/src/lib/mockData.ts @@ -3,180 +3,310 @@ import type { LibraryData, PlaylistInfo, TrackRecord, TransitionRecord } from '. const tracks: TrackRecord[] = [ { id: 't-001', - title: 'Blue Twilight', - artist: 'Nori Lane', - bpm: 122, - rating: 4, - camelotKey: '8A', + title: 'Night Notes', + artist: 'Skonoks', + bpm: 125, + rating: 3, + camelotKey: '4A', rawKey: null, - path: '/mock/blue-twilight.wav', - playlists: ['p-1', 'p-2'], + path: 'https://skonoks.bandcamp.com/track/night-notes', + playlists: ['p-1'], }, { id: 't-002', - title: 'Lift Off', - artist: 'Signal Motel', - bpm: 124, - rating: 5, - camelotKey: '8B', + title: 'Another Round (Jay Strata rmx)', + artist: 'Skonoks', + bpm: 129, + rating: 3, + camelotKey: '4A', rawKey: null, - path: '/mock/lift-off.wav', + path: 'https://skonoks.bandcamp.com/track/another-round-jay-strata-rmx', playlists: ['p-1'], }, { id: 't-003', - title: 'Warehouse Bloom', - artist: 'Tess Vector', - bpm: 127, - rating: 4, - camelotKey: '9A', + title: 'Til I Get You (Skonoks Remix)', + artist: 'CLVPS', + bpm: 126, + rating: 3, + camelotKey: '4A', rawKey: null, - path: '/mock/warehouse-bloom.wav', + path: 'https://soundcloud.com/skonoks/til-i-get-you-skonoks-remix', playlists: ['p-1', 'p-3'], }, { id: 't-004', - title: 'Glass Steps', - artist: 'Ion Harbour', - bpm: 128, + title: 'Carnian Pluvial Episode (DX-9 under water rmx)', + artist: 'Skonoks', + bpm: 127, rating: 3, - camelotKey: '9B', + camelotKey: '5A', rawKey: null, - path: '/mock/glass-steps.wav', - playlists: ['p-1', 'p-3'], + path: 'https://skonoks.bandcamp.com/track/carnian-pluvial-episode-dx-9-underwater-rmx', + playlists: ['p-1'], }, { id: 't-005', - title: 'Afterlight', - artist: 'Mika Current', - bpm: 130, - rating: 5, - camelotKey: '10A', + title: 'Another Round (Skonoks 4:4 Mix)', + artist: 'Skonoks', + bpm: 126, + rating: 3, + camelotKey: '5A', rawKey: null, - path: '/mock/afterlight.wav', + path: 'https://skonoks.bandcamp.com/track/another-round-skonoks-4-4-mix', playlists: ['p-1'], }, { id: 't-006', - title: 'Velvet Relay', - artist: 'Ossia Drive', - bpm: 135, - rating: 4, - camelotKey: '10B', + title: 'Warm Embrace', + artist: 'Skonoks', + bpm: 126, + rating: 3, + camelotKey: '6A', rawKey: null, - path: '/mock/velvet-relay.wav', - playlists: ['p-2'], + path: 'https://skonoks.bandcamp.com/track/warm-embrace', + playlists: ['p-1'], }, { id: 't-007', - title: 'Neon Tides', - artist: 'Helix Bloom', - bpm: 138, - rating: 2, - camelotKey: '11A', + title: 'Crystals', + artist: 'Skonoks', + bpm: 125, + rating: 3, + camelotKey: '6A', rawKey: null, - path: '/mock/neon-tides.wav', - playlists: ['p-2'], + path: 'https://skonoks.bandcamp.com/track/crystals', + playlists: ['p-1'], }, { id: 't-008', - title: 'Shadowline', - artist: 'Rae Pacific', - bpm: 140, - rating: 4, - camelotKey: '11B', + title: 'Scatter Plot', + artist: 'Skonoks', + bpm: 125, + rating: 3, + camelotKey: '7B', rawKey: null, - path: '/mock/shadowline.wav', - playlists: ['p-2'], + path: 'https://skonoks.bandcamp.com/track/scatter-plot', + playlists: ['p-1'], }, { id: 't-009', - title: 'Circuit Prayer', - artist: 'Lumen Phase', - bpm: 110, - rating: 5, - camelotKey: '6A', + title: 'Oh hey', + artist: 'Skonoks', + bpm: 125, + rating: 3, + camelotKey: '8A', rawKey: null, - path: '/mock/circuit-prayer.wav', - playlists: ['p-3'], + path: 'https://soundcloud.com/skonoks/oh-hey-original-mix', + playlists: ['p-1', 'p-3'], }, { id: 't-010', - title: 'Sunrise Draft', - artist: 'Adi Quartz', - bpm: 116, - rating: 3, - camelotKey: '7A', + title: 'Fours', + artist: 'Skonoks', + bpm: 125, + rating: null, + camelotKey: '12A', rawKey: null, - path: '/mock/sunrise-draft.wav', - playlists: ['p-3'], + path: 'https://skonoks.bandcamp.com/track/fours', + playlists: ['p-1', 'p-4'], }, { id: 't-011', - title: 'Arc Signal', - artist: 'Mara Tone', - bpm: 121, - rating: 4, - camelotKey: '8A', + title: 'Threes', + artist: 'Skonoks', + bpm: 125, + rating: null, + camelotKey: '12A', rawKey: null, - path: '/mock/arc-signal.wav', - playlists: ['p-4'], + path: 'https://skonoks.bandcamp.com/track/threes', + playlists: ['p-1', 'p-4'], }, { id: 't-012', - title: 'Delta Lift', - artist: 'Mara Tone', - bpm: 122, - rating: 4, - camelotKey: '9A', + title: 'Out There', + artist: 'Skonoks', + bpm: 125, + rating: null, + camelotKey: '6A', rawKey: null, - path: '/mock/delta-lift.wav', - playlists: ['p-4'], + path: 'https://skonoks.bandcamp.com/track/out-there', + playlists: ['p-1'], }, { id: 't-013', - title: 'Mirror Rail', - artist: 'Mara Tone', - bpm: 123, - rating: 4, - camelotKey: '9B', + title: 'Pocket Riot', + artist: 'Skonoks', + bpm: 126, + rating: null, + camelotKey: '3A', rawKey: null, - path: '/mock/mirror-rail.wav', - playlists: ['p-4'], + path: 'https://skonoks.bandcamp.com/track/pocket-riot', + playlists: ['p-1'], }, { id: 't-014', - title: 'Copper Dawn', - artist: 'Mara Tone', + title: 'Crossing The Valley', + artist: 'Skonoks', + bpm: 125, + rating: null, + camelotKey: '7A', + rawKey: null, + path: 'https://skonoks.bandcamp.com/track/crossing-the-valley', + playlists: ['p-1'], + }, + { + id: 't-015', + title: 'Baseline Test', + artist: 'Skonoks', + bpm: 126, + rating: 3, + camelotKey: '12A', + rawKey: null, + path: 'https://skonoks.bandcamp.com/track/baseline-test', + playlists: ['p-2'], + }, + { + id: 't-016', + title: 'Supercritical', + artist: 'Skonoks', + bpm: 126, + rating: 3, + camelotKey: '5A', + rawKey: null, + path: 'https://skonoks.bandcamp.com/track/supercritical', + playlists: ['p-2'], + }, + { + id: 't-017', + title: "That Dot, That's Us!", + artist: 'Skonoks', + bpm: 124, + rating: 3, + camelotKey: '6A', + rawKey: null, + path: 'https://skonoks.bandcamp.com/track/that-dot-thats-us', + playlists: ['p-2'], + }, + { + id: 't-018', + title: 'See What Comes', + artist: 'Skonoks', + bpm: 125, + rating: 3, + camelotKey: '9A', + rawKey: null, + path: 'https://skonoks.bandcamp.com/track/see-what-comes', + playlists: ['p-2', 'p-5'], + }, + { + id: 't-019', + title: 'Ascending Glow', + artist: 'Skonoks', + bpm: 125, + rating: 3, + camelotKey: '10A', + rawKey: null, + path: 'https://skonoks.bandcamp.com/track/ascending-glow', + playlists: ['p-2'], + }, + { + id: 't-020', + title: 'Try Me', + artist: 'Skonoks', + bpm: 125, + rating: 3, + camelotKey: '3B', + rawKey: null, + path: 'https://skonoks.bandcamp.com/track/try-me', + playlists: ['p-2'], + }, + { + id: 't-021', + title: 'Tapa Ono', + artist: 'Skonoks', bpm: 124, - rating: 4, - camelotKey: '10B', + rating: 3, + camelotKey: '8B', + rawKey: null, + path: 'https://skonoks.bandcamp.com/track/tapa-ono', + playlists: ['p-2'], + }, + { + id: 't-022', + title: 'Resonating Fabric', + artist: 'Skonoks', + bpm: 126, + rating: 3, + camelotKey: '5A', + rawKey: null, + path: 'https://skonoks.bandcamp.com/track/resonating-fabric', + playlists: ['p-2'], + }, + { + id: 't-023', + title: 'Flyswatter 2025', + artist: 'Skonoks', + bpm: 127, + rating: 3, + camelotKey: '5A', + rawKey: null, + path: 'https://skonoks.bandcamp.com/track/flyswatter-2025', + playlists: ['p-2'], + }, + { + id: 't-024', + title: 'Machine Without Purpose', + artist: 'Skonoks', + bpm: 125, + rating: 3, + camelotKey: '6A', rawKey: null, - path: '/mock/copper-dawn.wav', - playlists: ['p-4'], + path: 'https://skonoks.bandcamp.com/track/machine-without-purpose', + playlists: ['p-2'], }, ] const playlists: PlaylistInfo[] = [ { id: 'p-1', - name: 'Peak-time ladders', - trackIds: ['t-001', 't-002', 't-003', 't-004', 't-005'], + name: 'Skonoks — 2024 selections', + trackIds: [ + 't-001', + 't-002', + 't-003', + 't-004', + 't-005', + 't-006', + 't-007', + 't-008', + 't-009', + 't-010', + 't-011', + 't-012', + 't-013', + 't-014', + ], }, { id: 'p-2', - name: 'Late-night pressure', - trackIds: ['t-001', 't-006', 't-007', 't-008'], + name: 'Skonoks — 2025 releases', + trackIds: ['t-015', 't-016', 't-017', 't-018', 't-019', 't-020', 't-021', 't-022', 't-023', 't-024'], }, { id: 'p-3', - name: 'Warm-up bridge', - trackIds: ['t-009', 't-010', 't-003', 't-004'], + name: 'North-South Connection EP', + trackIds: ['t-003', 't-009'], }, { id: 'p-4', - name: 'Path finder lane', - trackIds: ['t-001', 't-011', 't-012', 't-013', 't-014'], + name: 'Threes or Fours', + trackIds: ['t-010', 't-011'], + }, + { + id: 'p-5', + name: 'See What Comes', + trackIds: ['t-018'], }, ]