spotify-now-playing-card 1.0.0 ā 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +53 -3
- package/bin/setup.js +298 -0
- package/dist/SpotifyCard.d.ts.map +1 -1
- package/dist/index.esm.js +20 -19
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +20 -19
- package/dist/index.js.map +1 -1
- package/dist/styles.css +99 -0
- package/package.json +12 -7
package/README.md
CHANGED
|
@@ -22,6 +22,22 @@ yarn add spotify-now-playing-card
|
|
|
22
22
|
pnpm add spotify-now-playing-card
|
|
23
23
|
```
|
|
24
24
|
|
|
25
|
+
## Quick Setup
|
|
26
|
+
|
|
27
|
+
The easiest way to get started is using the setup script:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
npx spotify-now-playing-card-setup
|
|
31
|
+
# or if installed locally
|
|
32
|
+
npm run setup
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
This will:
|
|
36
|
+
- Detect your framework (Next.js Pages/App Router)
|
|
37
|
+
- Generate the API endpoint automatically
|
|
38
|
+
- Set up environment variables
|
|
39
|
+
- Guide you through the process
|
|
40
|
+
|
|
25
41
|
## Prerequisites
|
|
26
42
|
|
|
27
43
|
You need to set up a Spotify API endpoint that returns the currently playing track data. The component expects the API to return JSON in this format:
|
|
@@ -44,7 +60,9 @@ When not playing, the API should return:
|
|
|
44
60
|
}
|
|
45
61
|
```
|
|
46
62
|
|
|
47
|
-
### Setting up Spotify API
|
|
63
|
+
### Setting up Spotify API (Manual)
|
|
64
|
+
|
|
65
|
+
If you prefer to set up manually or the setup script doesn't work for your framework:
|
|
48
66
|
|
|
49
67
|
1. Go to [Spotify Developer Dashboard](https://developer.spotify.com/dashboard)
|
|
50
68
|
2. Create a new app
|
|
@@ -124,14 +142,19 @@ export default async function handler(req, res) {
|
|
|
124
142
|
|
|
125
143
|
### Basic Usage
|
|
126
144
|
|
|
145
|
+
**Important:** You need to import the CSS file for default styles to work:
|
|
146
|
+
|
|
127
147
|
```jsx
|
|
128
148
|
import { SpotifyCard } from 'spotify-now-playing-card'
|
|
149
|
+
import 'spotify-now-playing-card/dist/styles.css' // Required for default styles
|
|
129
150
|
|
|
130
151
|
function App() {
|
|
131
152
|
return <SpotifyCard apiUrl="/api/spotify" />
|
|
132
153
|
}
|
|
133
154
|
```
|
|
134
155
|
|
|
156
|
+
The component works **without Tailwind CSS** - it includes its own CSS file with default styles. If you're using Tailwind, you can still override styles using the `styles` prop.
|
|
157
|
+
|
|
135
158
|
### TypeScript Usage
|
|
136
159
|
|
|
137
160
|
The package includes full TypeScript definitions:
|
|
@@ -184,9 +207,36 @@ function App() {
|
|
|
184
207
|
|
|
185
208
|
### Styling
|
|
186
209
|
|
|
187
|
-
The component
|
|
210
|
+
The component includes its own CSS file with default styles, so **it works without Tailwind CSS**. Simply import the CSS:
|
|
188
211
|
|
|
189
|
-
|
|
212
|
+
```jsx
|
|
213
|
+
import 'spotify-now-playing-card/dist/styles.css'
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
### Using with Tailwind CSS
|
|
217
|
+
|
|
218
|
+
If you're using Tailwind CSS, you can override the default styles using the `styles` prop:
|
|
219
|
+
|
|
220
|
+
```jsx
|
|
221
|
+
<SpotifyCard
|
|
222
|
+
apiUrl="/api/spotify"
|
|
223
|
+
styles={{
|
|
224
|
+
container: "bg-gray-900 rounded-xl", // Tailwind classes
|
|
225
|
+
title: "text-white text-lg"
|
|
226
|
+
}}
|
|
227
|
+
/>
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
### Custom CSS
|
|
231
|
+
|
|
232
|
+
You can also override styles using your own CSS by targeting the component classes:
|
|
233
|
+
|
|
234
|
+
```css
|
|
235
|
+
.spotify-card-container {
|
|
236
|
+
background: #1a1a1a;
|
|
237
|
+
border-radius: 12px;
|
|
238
|
+
}
|
|
239
|
+
```
|
|
190
240
|
|
|
191
241
|
## Dependencies
|
|
192
242
|
|
package/bin/setup.js
ADDED
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Spotify Card Setup Script
|
|
5
|
+
* Automatically generates API endpoint for Next.js or other frameworks
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const fs = require('fs')
|
|
9
|
+
const path = require('path')
|
|
10
|
+
const readline = require('readline')
|
|
11
|
+
|
|
12
|
+
const rl = readline.createInterface({
|
|
13
|
+
input: process.stdin,
|
|
14
|
+
output: process.stdout
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
function question(query) {
|
|
18
|
+
return new Promise(resolve => rl.question(query, resolve))
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async function setup() {
|
|
22
|
+
console.log('\nšµ Spotify Now Playing Card - Setup\n')
|
|
23
|
+
console.log('This script will help you set up the API endpoint for the Spotify card component.\n')
|
|
24
|
+
|
|
25
|
+
// Detect framework
|
|
26
|
+
const hasNextApp = fs.existsSync('app') || fs.existsSync('pages')
|
|
27
|
+
const hasPages = fs.existsSync('pages')
|
|
28
|
+
const hasApp = fs.existsSync('app')
|
|
29
|
+
|
|
30
|
+
let framework = 'nextjs'
|
|
31
|
+
let apiPath = 'pages/api/spotify'
|
|
32
|
+
|
|
33
|
+
if (hasApp && !hasPages) {
|
|
34
|
+
framework = 'nextjs-app'
|
|
35
|
+
apiPath = 'app/api/spotify/route'
|
|
36
|
+
} else if (hasPages) {
|
|
37
|
+
framework = 'nextjs'
|
|
38
|
+
apiPath = 'pages/api/spotify'
|
|
39
|
+
} else {
|
|
40
|
+
console.log('ā ļø Could not detect Next.js. Please specify:')
|
|
41
|
+
const frameworkChoice = await question('Framework (nextjs/nextjs-app/other): ')
|
|
42
|
+
if (frameworkChoice === 'nextjs-app') {
|
|
43
|
+
framework = 'nextjs-app'
|
|
44
|
+
apiPath = 'app/api/spotify/route'
|
|
45
|
+
} else if (frameworkChoice === 'nextjs') {
|
|
46
|
+
framework = 'nextjs'
|
|
47
|
+
apiPath = 'pages/api/spotify'
|
|
48
|
+
} else {
|
|
49
|
+
console.log('ā ļø For other frameworks, you\'ll need to create the API endpoint manually.')
|
|
50
|
+
console.log(' Check the README.md for the API endpoint code.')
|
|
51
|
+
rl.close()
|
|
52
|
+
return
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
console.log(`\nš Detected framework: ${framework}`)
|
|
57
|
+
console.log(`š API path: ${apiPath}\n`)
|
|
58
|
+
|
|
59
|
+
// Get credentials
|
|
60
|
+
console.log('Please provide your Spotify API credentials:')
|
|
61
|
+
console.log('(You can get these from https://developer.spotify.com/dashboard)\n')
|
|
62
|
+
|
|
63
|
+
const clientId = await question('Spotify Client ID: ')
|
|
64
|
+
const clientSecret = await question('Spotify Client Secret: ')
|
|
65
|
+
const refreshToken = await question('Spotify Refresh Token: ')
|
|
66
|
+
|
|
67
|
+
if (!clientId || !clientSecret || !refreshToken) {
|
|
68
|
+
console.log('\nā All credentials are required!')
|
|
69
|
+
rl.close()
|
|
70
|
+
return
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Create directory if it doesn't exist
|
|
74
|
+
const apiDir = path.dirname(apiPath)
|
|
75
|
+
if (!fs.existsSync(apiDir)) {
|
|
76
|
+
fs.mkdirSync(apiDir, { recursive: true })
|
|
77
|
+
console.log(`ā
Created directory: ${apiDir}`)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Generate API endpoint code
|
|
81
|
+
let apiCode = ''
|
|
82
|
+
|
|
83
|
+
if (framework === 'nextjs-app') {
|
|
84
|
+
// Next.js App Router
|
|
85
|
+
apiCode = `import { NextResponse } from 'next/server';
|
|
86
|
+
|
|
87
|
+
const SPOTIFY_CLIENT_ID = process.env.SPOTIFY_CLIENT_ID;
|
|
88
|
+
const SPOTIFY_CLIENT_SECRET = process.env.SPOTIFY_CLIENT_SECRET;
|
|
89
|
+
const SPOTIFY_REFRESH_TOKEN = process.env.SPOTIFY_REFRESH_TOKEN;
|
|
90
|
+
|
|
91
|
+
// Cache for access token
|
|
92
|
+
let accessToken: string | null = null;
|
|
93
|
+
let tokenExpiry: number = 0;
|
|
94
|
+
|
|
95
|
+
async function getAccessToken(): Promise<string> {
|
|
96
|
+
// Return cached token if still valid
|
|
97
|
+
if (accessToken && Date.now() < tokenExpiry) {
|
|
98
|
+
return accessToken;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (!SPOTIFY_CLIENT_ID || !SPOTIFY_CLIENT_SECRET || !SPOTIFY_REFRESH_TOKEN) {
|
|
102
|
+
throw new Error('Spotify credentials not configured');
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Get new access token using refresh token
|
|
106
|
+
const response = await fetch('https://accounts.spotify.com/api/token', {
|
|
107
|
+
method: 'POST',
|
|
108
|
+
headers: {
|
|
109
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
110
|
+
Authorization: \`Basic \${Buffer.from(
|
|
111
|
+
\`\${SPOTIFY_CLIENT_ID}:\${SPOTIFY_CLIENT_SECRET}\`
|
|
112
|
+
).toString('base64')}\`,
|
|
113
|
+
},
|
|
114
|
+
body: new URLSearchParams({
|
|
115
|
+
grant_type: 'refresh_token',
|
|
116
|
+
refresh_token: SPOTIFY_REFRESH_TOKEN,
|
|
117
|
+
}),
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
if (!response.ok) {
|
|
121
|
+
const error = await response.text();
|
|
122
|
+
throw new Error(\`Failed to get access token: \${error}\`);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const data = await response.json();
|
|
126
|
+
accessToken = data.access_token;
|
|
127
|
+
// Set expiry to 55 minutes (tokens last 1 hour)
|
|
128
|
+
tokenExpiry = Date.now() + 55 * 60 * 1000;
|
|
129
|
+
|
|
130
|
+
if (!accessToken) {
|
|
131
|
+
throw new Error('Failed to get access token');
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return accessToken;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export async function GET() {
|
|
138
|
+
try {
|
|
139
|
+
const token = await getAccessToken();
|
|
140
|
+
|
|
141
|
+
// Get currently playing track
|
|
142
|
+
const response = await fetch(
|
|
143
|
+
'https://api.spotify.com/v1/me/player/currently-playing',
|
|
144
|
+
{
|
|
145
|
+
headers: {
|
|
146
|
+
Authorization: \`Bearer \${token}\`,
|
|
147
|
+
},
|
|
148
|
+
}
|
|
149
|
+
);
|
|
150
|
+
|
|
151
|
+
if (response.status === 204) {
|
|
152
|
+
// No content - not playing anything
|
|
153
|
+
return NextResponse.json({ isPlaying: false });
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (!response.ok) {
|
|
157
|
+
return NextResponse.json({ isPlaying: false });
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const data = await response.json();
|
|
161
|
+
|
|
162
|
+
if (!data.item) {
|
|
163
|
+
return NextResponse.json({ isPlaying: false });
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Return format expected by spotify-now-playing-card package
|
|
167
|
+
return NextResponse.json({
|
|
168
|
+
isPlaying: data.is_playing,
|
|
169
|
+
title: data.item.name,
|
|
170
|
+
artist: data.item.artists.map((a: any) => a.name).join(', '),
|
|
171
|
+
album: data.item.album.name,
|
|
172
|
+
albumImageUrl: data.item.album.images[0]?.url || data.item.album.images[1]?.url,
|
|
173
|
+
songUrl: data.item.external_urls.spotify,
|
|
174
|
+
});
|
|
175
|
+
} catch (error: any) {
|
|
176
|
+
console.error('Spotify API error:', error);
|
|
177
|
+
return NextResponse.json(
|
|
178
|
+
{ error: error.message || 'Failed to fetch Spotify data' },
|
|
179
|
+
{ status: 500 }
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
`
|
|
184
|
+
} else {
|
|
185
|
+
// Next.js Pages Router
|
|
186
|
+
apiCode = `import querystring from 'querystring'
|
|
187
|
+
import axios from 'axios'
|
|
188
|
+
|
|
189
|
+
const {
|
|
190
|
+
SPOTIFY_CLIENT_ID: client_id,
|
|
191
|
+
SPOTIFY_CLIENT_SECRET: client_secret,
|
|
192
|
+
SPOTIFY_REFRESH_TOKEN: refresh_token
|
|
193
|
+
} = process.env
|
|
194
|
+
|
|
195
|
+
const basic = Buffer.from(\`\${client_id}:\${client_secret}\`).toString('base64')
|
|
196
|
+
const NOW_PLAYING_ENDPOINT = 'https://api.spotify.com/v1/me/player/currently-playing'
|
|
197
|
+
const TOKEN_ENDPOINT = 'https://accounts.spotify.com/api/token'
|
|
198
|
+
|
|
199
|
+
const getAccessToken = async () => {
|
|
200
|
+
const response = await axios.post(
|
|
201
|
+
TOKEN_ENDPOINT,
|
|
202
|
+
querystring.stringify({
|
|
203
|
+
grant_type: 'refresh_token',
|
|
204
|
+
refresh_token
|
|
205
|
+
}),
|
|
206
|
+
{
|
|
207
|
+
headers: {
|
|
208
|
+
Authorization: \`Basic \${basic}\`,
|
|
209
|
+
'Content-Type': 'application/x-www-form-urlencoded'
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
return response.data
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
const spotifyApi = async (req, res) => {
|
|
218
|
+
try {
|
|
219
|
+
const { access_token } = await getAccessToken()
|
|
220
|
+
const response = await axios.get(NOW_PLAYING_ENDPOINT, {
|
|
221
|
+
headers: {
|
|
222
|
+
Authorization: \`Bearer \${access_token}\`
|
|
223
|
+
}
|
|
224
|
+
})
|
|
225
|
+
|
|
226
|
+
if (
|
|
227
|
+
response.status === 204 ||
|
|
228
|
+
response.status > 400 ||
|
|
229
|
+
(response.data && response.data.currently_playing_type !== 'track')
|
|
230
|
+
) {
|
|
231
|
+
return res.status(200).json({ isPlaying: false })
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
const data = {
|
|
235
|
+
isPlaying: response.data.is_playing,
|
|
236
|
+
title: response.data.item.name,
|
|
237
|
+
album: response.data.item.album.name,
|
|
238
|
+
artist: response.data.item.album.artists.map((artist) => artist.name).join(', '),
|
|
239
|
+
albumImageUrl: response.data.item.album.images[0].url,
|
|
240
|
+
songUrl: response.data.item.external_urls.spotify
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
res.status(200).json(data)
|
|
244
|
+
} catch (error) {
|
|
245
|
+
console.error('Spotify API error:', error)
|
|
246
|
+
res.status(200).json({ isPlaying: false })
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
export default spotifyApi
|
|
251
|
+
`
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// Write API file
|
|
255
|
+
const fileExtension = framework === 'nextjs-app' ? 'ts' : 'js'
|
|
256
|
+
const apiFilePath = framework === 'nextjs-app' ? `${apiPath}.ts` : `${apiPath}.${fileExtension}`
|
|
257
|
+
|
|
258
|
+
fs.writeFileSync(apiFilePath, apiCode)
|
|
259
|
+
console.log(`ā
Created API endpoint: ${apiFilePath}`)
|
|
260
|
+
|
|
261
|
+
// Update or create .env file
|
|
262
|
+
const envPath = '.env.local'
|
|
263
|
+
let envContent = ''
|
|
264
|
+
|
|
265
|
+
if (fs.existsSync(envPath)) {
|
|
266
|
+
envContent = fs.readFileSync(envPath, 'utf8')
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// Check if variables already exist
|
|
270
|
+
if (!envContent.includes('SPOTIFY_CLIENT_ID')) {
|
|
271
|
+
envContent += `\n# Spotify API Credentials\n`
|
|
272
|
+
envContent += `SPOTIFY_CLIENT_ID=${clientId}\n`
|
|
273
|
+
envContent += `SPOTIFY_CLIENT_SECRET=${clientSecret}\n`
|
|
274
|
+
envContent += `SPOTIFY_REFRESH_TOKEN=${refreshToken}\n`
|
|
275
|
+
fs.writeFileSync(envPath, envContent)
|
|
276
|
+
console.log(`ā
Updated ${envPath} with Spotify credentials`)
|
|
277
|
+
} else {
|
|
278
|
+
console.log(`ā ļø ${envPath} already contains Spotify credentials. Please update manually if needed.`)
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
console.log('\n⨠Setup complete!')
|
|
282
|
+
console.log('\nNext steps:')
|
|
283
|
+
console.log('1. Make sure your .env.local file is in .gitignore')
|
|
284
|
+
console.log('2. Import and use the component in your app:')
|
|
285
|
+
console.log(' import { SpotifyCard } from "spotify-now-playing-card"')
|
|
286
|
+
console.log(' import "spotify-now-playing-card/dist/styles.css"')
|
|
287
|
+
console.log('3. Use it: <SpotifyCard apiUrl="/api/spotify" />')
|
|
288
|
+
console.log('\n')
|
|
289
|
+
|
|
290
|
+
rl.close()
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
setup().catch(err => {
|
|
294
|
+
console.error('Error:', err)
|
|
295
|
+
rl.close()
|
|
296
|
+
process.exit(1)
|
|
297
|
+
})
|
|
298
|
+
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SpotifyCard.d.ts","sourceRoot":"","sources":["../src/SpotifyCard.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"SpotifyCard.d.ts","sourceRoot":"","sources":["../src/SpotifyCard.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AASzB,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,OAAO,CAAA;IAClB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAED,MAAM,WAAW,iBAAiB;IAChC,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,IAAI,CAAC,EAAE,MAAM,CAAA;CACd;AAED,MAAM,WAAW,gBAAgB;IAC/B,qDAAqD;IACrD,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,kEAAkE;IAClE,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,oDAAoD;IACpD,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,oDAAoD;IACpD,MAAM,CAAC,EAAE,iBAAiB,CAAA;IAC1B,iDAAiD;IACjD,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,yEAAyE;IACzE,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,6DAA6D;IAC7D,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,oDAAoD;IACpD,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAA;CACnB;AAED;;GAEG;AACH,QAAA,MAAM,WAAW,EAAE,KAAK,CAAC,EAAE,CAAC,gBAAgB,CAsF3C,CAAA;AAED,eAAe,WAAW,CAAA"}
|
package/dist/index.esm.js
CHANGED
|
@@ -3,11 +3,12 @@ import axios from 'axios';
|
|
|
3
3
|
import { SiSpotify } from 'react-icons/si';
|
|
4
4
|
import useSWR from 'swr';
|
|
5
5
|
|
|
6
|
+
// CSS will be imported by users: import 'spotify-now-playing-card/dist/styles.css'
|
|
6
7
|
const fetcher = (url) => axios.get(url).then(res => res.data);
|
|
7
8
|
/**
|
|
8
9
|
* SpotifyCard - A React component to display currently playing Spotify track
|
|
9
10
|
*/
|
|
10
|
-
const SpotifyCard = ({ apiUrl = '/api/spotify', fallbackUrl = 'https://open.spotify.com', className = '', styles = {}, showAlbum = true, notPlayingText = 'Currently not listening', refreshInterval =
|
|
11
|
+
const SpotifyCard = ({ apiUrl = '/api/spotify', fallbackUrl = 'https://open.spotify.com', className = '', styles = {}, showAlbum = true, notPlayingText = 'Currently not listening', refreshInterval = 10000, ...props }) => {
|
|
11
12
|
const { data, error } = useSWR(apiUrl, fetcher, {
|
|
12
13
|
refreshInterval: refreshInterval,
|
|
13
14
|
revalidateOnFocus: true
|
|
@@ -15,29 +16,29 @@ const SpotifyCard = ({ apiUrl = '/api/spotify', fallbackUrl = 'https://open.spot
|
|
|
15
16
|
if (error) {
|
|
16
17
|
console.error('SpotifyCard error:', error);
|
|
17
18
|
}
|
|
19
|
+
// Use CSS classes by default (works without Tailwind)
|
|
20
|
+
// Users can override with Tailwind classes via styles prop if they want
|
|
18
21
|
const defaultStyles = {
|
|
19
|
-
container: '
|
|
20
|
-
link: '
|
|
21
|
-
image: '
|
|
22
|
-
title: '
|
|
23
|
-
artist: '
|
|
24
|
-
album: '
|
|
25
|
-
icon: '
|
|
22
|
+
container: 'spotify-card-container',
|
|
23
|
+
link: 'spotify-card-link',
|
|
24
|
+
image: 'spotify-card-image',
|
|
25
|
+
title: 'spotify-card-title',
|
|
26
|
+
artist: 'spotify-card-artist',
|
|
27
|
+
album: 'spotify-card-album',
|
|
28
|
+
icon: 'spotify-card-icon'
|
|
26
29
|
};
|
|
27
30
|
const mergedStyles = {
|
|
28
|
-
container: `${defaultStyles.container} ${styles.container || ''} ${className}
|
|
29
|
-
link: `${defaultStyles.link} ${styles.link || ''}
|
|
30
|
-
image: `${defaultStyles.image} ${styles.image || ''}
|
|
31
|
-
title: `${defaultStyles.title} ${styles.title || ''}
|
|
32
|
-
artist: `${defaultStyles.artist} ${styles.artist || ''}
|
|
33
|
-
album: `${defaultStyles.album} ${styles.album || ''}
|
|
34
|
-
icon: `${defaultStyles.icon} ${styles.icon || ''}
|
|
31
|
+
container: `${defaultStyles.container} ${styles.container || ''} ${className}`.trim(),
|
|
32
|
+
link: `${defaultStyles.link} ${styles.link || ''}`.trim(),
|
|
33
|
+
image: `${defaultStyles.image} ${styles.image || ''}`.trim(),
|
|
34
|
+
title: `${defaultStyles.title} ${styles.title || ''}`.trim(),
|
|
35
|
+
artist: `${defaultStyles.artist} ${styles.artist || ''}`.trim(),
|
|
36
|
+
album: `${defaultStyles.album} ${styles.album || ''}`.trim(),
|
|
37
|
+
icon: `${defaultStyles.icon} ${styles.icon || ''}`.trim()
|
|
35
38
|
};
|
|
36
39
|
const isPlaying = data?.isPlaying;
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
: '';
|
|
40
|
-
return (jsx("div", { className: mergedStyles.container, ...props, children: jsxs("a", { target: '_blank', rel: 'noreferrer', href: isPlaying ? data?.songUrl : fallbackUrl, className: mergedStyles.link, children: [jsx("div", { className: 'w-15', children: isPlaying ? (jsx("img", { className: mergedStyles.image, src: data?.albumImageUrl, alt: data?.album || 'Album cover' })) : (jsx(SiSpotify, { size: 30, color: '#1ED760' })) }), jsxs("div", { className: 'flex-1', children: [jsx("p", { className: `${mergedStyles.title} ${titleColor}`, children: isPlaying ? data?.title : notPlayingText }), jsx("p", { className: mergedStyles.artist, children: isPlaying ? `Artist: ${data?.artist}` : 'Spotify' }), showAlbum && isPlaying && (jsxs("p", { className: mergedStyles.album, children: ["Album: ", data?.album] }))] }), isPlaying && (jsx("div", { className: mergedStyles.icon, children: jsx(SiSpotify, { size: 20, color: '#1ED760' }) }))] }) }));
|
|
40
|
+
const titleClass = isPlaying ? 'spotify-card-title-gradient' : '';
|
|
41
|
+
return (jsx("div", { className: mergedStyles.container, ...props, children: jsxs("a", { target: '_blank', rel: 'noreferrer', href: isPlaying ? data?.songUrl : fallbackUrl, className: mergedStyles.link, children: [jsx("div", { className: "spotify-card-image-container", children: isPlaying ? (jsx("img", { className: mergedStyles.image, src: data?.albumImageUrl, alt: data?.album || 'Album cover' })) : (jsx(SiSpotify, { size: 30, color: '#1ED760' })) }), jsxs("div", { className: "spotify-card-content", children: [jsx("p", { className: `${mergedStyles.title} ${titleClass}`.trim(), children: isPlaying ? data?.title : notPlayingText }), jsx("p", { className: mergedStyles.artist, children: isPlaying ? `Artist: ${data?.artist}` : 'Spotify' }), showAlbum && isPlaying && (jsxs("p", { className: mergedStyles.album, children: ["Album: ", data?.album] }))] }), isPlaying && (jsx("div", { className: mergedStyles.icon, children: jsx(SiSpotify, { size: 20, color: '#1ED760' }) }))] }) }));
|
|
41
42
|
};
|
|
42
43
|
|
|
43
44
|
export { SpotifyCard, SpotifyCard as default };
|
package/dist/index.esm.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.esm.js","sources":["../src/SpotifyCard.tsx"],"sourcesContent":[null],"names":["_jsx","_jsxs"],"mappings":";;;;;AAKA,MAAM,OAAO,GAAG,CAAC,GAAW,KAAK,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC;AAwCrE;;AAEG;AACH,MAAM,WAAW,GAA+B,CAAC,EAC/C,MAAM,GAAG,cAAc,EACvB,WAAW,GAAG,0BAA0B,EACxC,SAAS,GAAG,EAAE,EACd,MAAM,GAAG,EAAE,EACX,SAAS,GAAG,IAAI,EAChB,cAAc,GAAG,yBAAyB,EAC1C,eAAe,GAAG,KAAK,EACvB,GAAG,KAAK,EACT,KAAI;IACH,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,MAAM,CAAc,MAAM,EAAE,OAAO,EAAE;AAC3D,QAAA,eAAe,EAAE,eAAe;AAChC,QAAA,iBAAiB,EAAE;AACpB,KAAA,CAAC;IAEF,IAAI,KAAK,EAAE;AACT,QAAA,OAAO,CAAC,KAAK,CAAC,oBAAoB,EAAE,KAAK,CAAC;IAC5C
|
|
1
|
+
{"version":3,"file":"index.esm.js","sources":["../src/SpotifyCard.tsx"],"sourcesContent":[null],"names":["_jsx","_jsxs"],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -7,11 +7,12 @@ var axios = require('axios');
|
|
|
7
7
|
var si = require('react-icons/si');
|
|
8
8
|
var useSWR = require('swr');
|
|
9
9
|
|
|
10
|
+
// CSS will be imported by users: import 'spotify-now-playing-card/dist/styles.css'
|
|
10
11
|
const fetcher = (url) => axios.get(url).then(res => res.data);
|
|
11
12
|
/**
|
|
12
13
|
* SpotifyCard - A React component to display currently playing Spotify track
|
|
13
14
|
*/
|
|
14
|
-
const SpotifyCard = ({ apiUrl = '/api/spotify', fallbackUrl = 'https://open.spotify.com', className = '', styles = {}, showAlbum = true, notPlayingText = 'Currently not listening', refreshInterval =
|
|
15
|
+
const SpotifyCard = ({ apiUrl = '/api/spotify', fallbackUrl = 'https://open.spotify.com', className = '', styles = {}, showAlbum = true, notPlayingText = 'Currently not listening', refreshInterval = 10000, ...props }) => {
|
|
15
16
|
const { data, error } = useSWR(apiUrl, fetcher, {
|
|
16
17
|
refreshInterval: refreshInterval,
|
|
17
18
|
revalidateOnFocus: true
|
|
@@ -19,29 +20,29 @@ const SpotifyCard = ({ apiUrl = '/api/spotify', fallbackUrl = 'https://open.spot
|
|
|
19
20
|
if (error) {
|
|
20
21
|
console.error('SpotifyCard error:', error);
|
|
21
22
|
}
|
|
23
|
+
// Use CSS classes by default (works without Tailwind)
|
|
24
|
+
// Users can override with Tailwind classes via styles prop if they want
|
|
22
25
|
const defaultStyles = {
|
|
23
|
-
container: '
|
|
24
|
-
link: '
|
|
25
|
-
image: '
|
|
26
|
-
title: '
|
|
27
|
-
artist: '
|
|
28
|
-
album: '
|
|
29
|
-
icon: '
|
|
26
|
+
container: 'spotify-card-container',
|
|
27
|
+
link: 'spotify-card-link',
|
|
28
|
+
image: 'spotify-card-image',
|
|
29
|
+
title: 'spotify-card-title',
|
|
30
|
+
artist: 'spotify-card-artist',
|
|
31
|
+
album: 'spotify-card-album',
|
|
32
|
+
icon: 'spotify-card-icon'
|
|
30
33
|
};
|
|
31
34
|
const mergedStyles = {
|
|
32
|
-
container: `${defaultStyles.container} ${styles.container || ''} ${className}
|
|
33
|
-
link: `${defaultStyles.link} ${styles.link || ''}
|
|
34
|
-
image: `${defaultStyles.image} ${styles.image || ''}
|
|
35
|
-
title: `${defaultStyles.title} ${styles.title || ''}
|
|
36
|
-
artist: `${defaultStyles.artist} ${styles.artist || ''}
|
|
37
|
-
album: `${defaultStyles.album} ${styles.album || ''}
|
|
38
|
-
icon: `${defaultStyles.icon} ${styles.icon || ''}
|
|
35
|
+
container: `${defaultStyles.container} ${styles.container || ''} ${className}`.trim(),
|
|
36
|
+
link: `${defaultStyles.link} ${styles.link || ''}`.trim(),
|
|
37
|
+
image: `${defaultStyles.image} ${styles.image || ''}`.trim(),
|
|
38
|
+
title: `${defaultStyles.title} ${styles.title || ''}`.trim(),
|
|
39
|
+
artist: `${defaultStyles.artist} ${styles.artist || ''}`.trim(),
|
|
40
|
+
album: `${defaultStyles.album} ${styles.album || ''}`.trim(),
|
|
41
|
+
icon: `${defaultStyles.icon} ${styles.icon || ''}`.trim()
|
|
39
42
|
};
|
|
40
43
|
const isPlaying = data?.isPlaying;
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
: '';
|
|
44
|
-
return (jsxRuntime.jsx("div", { className: mergedStyles.container, ...props, children: jsxRuntime.jsxs("a", { target: '_blank', rel: 'noreferrer', href: isPlaying ? data?.songUrl : fallbackUrl, className: mergedStyles.link, children: [jsxRuntime.jsx("div", { className: 'w-15', children: isPlaying ? (jsxRuntime.jsx("img", { className: mergedStyles.image, src: data?.albumImageUrl, alt: data?.album || 'Album cover' })) : (jsxRuntime.jsx(si.SiSpotify, { size: 30, color: '#1ED760' })) }), jsxRuntime.jsxs("div", { className: 'flex-1', children: [jsxRuntime.jsx("p", { className: `${mergedStyles.title} ${titleColor}`, children: isPlaying ? data?.title : notPlayingText }), jsxRuntime.jsx("p", { className: mergedStyles.artist, children: isPlaying ? `Artist: ${data?.artist}` : 'Spotify' }), showAlbum && isPlaying && (jsxRuntime.jsxs("p", { className: mergedStyles.album, children: ["Album: ", data?.album] }))] }), isPlaying && (jsxRuntime.jsx("div", { className: mergedStyles.icon, children: jsxRuntime.jsx(si.SiSpotify, { size: 20, color: '#1ED760' }) }))] }) }));
|
|
44
|
+
const titleClass = isPlaying ? 'spotify-card-title-gradient' : '';
|
|
45
|
+
return (jsxRuntime.jsx("div", { className: mergedStyles.container, ...props, children: jsxRuntime.jsxs("a", { target: '_blank', rel: 'noreferrer', href: isPlaying ? data?.songUrl : fallbackUrl, className: mergedStyles.link, children: [jsxRuntime.jsx("div", { className: "spotify-card-image-container", children: isPlaying ? (jsxRuntime.jsx("img", { className: mergedStyles.image, src: data?.albumImageUrl, alt: data?.album || 'Album cover' })) : (jsxRuntime.jsx(si.SiSpotify, { size: 30, color: '#1ED760' })) }), jsxRuntime.jsxs("div", { className: "spotify-card-content", children: [jsxRuntime.jsx("p", { className: `${mergedStyles.title} ${titleClass}`.trim(), children: isPlaying ? data?.title : notPlayingText }), jsxRuntime.jsx("p", { className: mergedStyles.artist, children: isPlaying ? `Artist: ${data?.artist}` : 'Spotify' }), showAlbum && isPlaying && (jsxRuntime.jsxs("p", { className: mergedStyles.album, children: ["Album: ", data?.album] }))] }), isPlaying && (jsxRuntime.jsx("div", { className: mergedStyles.icon, children: jsxRuntime.jsx(si.SiSpotify, { size: 20, color: '#1ED760' }) }))] }) }));
|
|
45
46
|
};
|
|
46
47
|
|
|
47
48
|
exports.SpotifyCard = SpotifyCard;
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../src/SpotifyCard.tsx"],"sourcesContent":[null],"names":["_jsx","_jsxs","SiSpotify"],"mappings":";;;;;;;;;AAKA,MAAM,OAAO,GAAG,CAAC,GAAW,KAAK,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC;AAwCrE;;AAEG;AACH,MAAM,WAAW,GAA+B,CAAC,EAC/C,MAAM,GAAG,cAAc,EACvB,WAAW,GAAG,0BAA0B,EACxC,SAAS,GAAG,EAAE,EACd,MAAM,GAAG,EAAE,EACX,SAAS,GAAG,IAAI,EAChB,cAAc,GAAG,yBAAyB,EAC1C,eAAe,GAAG,KAAK,EACvB,GAAG,KAAK,EACT,KAAI;IACH,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,MAAM,CAAc,MAAM,EAAE,OAAO,EAAE;AAC3D,QAAA,eAAe,EAAE,eAAe;AAChC,QAAA,iBAAiB,EAAE;AACpB,KAAA,CAAC;IAEF,IAAI,KAAK,EAAE;AACT,QAAA,OAAO,CAAC,KAAK,CAAC,oBAAoB,EAAE,KAAK,CAAC;IAC5C
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../src/SpotifyCard.tsx"],"sourcesContent":[null],"names":["_jsx","_jsxs","SiSpotify"],"mappings":";;;;;;;;;AAKA;AAEA,MAAM,OAAO,GAAG,CAAC,GAAW,KAAK,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC;AAwCrE;;AAEG;AACH,MAAM,WAAW,GAA+B,CAAC,EAC/C,MAAM,GAAG,cAAc,EACvB,WAAW,GAAG,0BAA0B,EACxC,SAAS,GAAG,EAAE,EACd,MAAM,GAAG,EAAE,EACX,SAAS,GAAG,IAAI,EAChB,cAAc,GAAG,yBAAyB,EAC1C,eAAe,GAAG,KAAK,EACvB,GAAG,KAAK,EACT,KAAI;IACH,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,MAAM,CAAc,MAAM,EAAE,OAAO,EAAE;AAC3D,QAAA,eAAe,EAAE,eAAe;AAChC,QAAA,iBAAiB,EAAE;AACpB,KAAA,CAAC;IAEF,IAAI,KAAK,EAAE;AACT,QAAA,OAAO,CAAC,KAAK,CAAC,oBAAoB,EAAE,KAAK,CAAC;IAC5C;;;AAIA,IAAA,MAAM,aAAa,GAAsB;AACvC,QAAA,SAAS,EAAE,wBAAwB;AACnC,QAAA,IAAI,EAAE,mBAAmB;AACzB,QAAA,KAAK,EAAE,oBAAoB;AAC3B,QAAA,KAAK,EAAE,oBAAoB;AAC3B,QAAA,MAAM,EAAE,qBAAqB;AAC7B,QAAA,KAAK,EAAE,oBAAoB;AAC3B,QAAA,IAAI,EAAE;KACP;AAED,IAAA,MAAM,YAAY,GAAsB;AACtC,QAAA,SAAS,EAAE,CAAA,EAAG,aAAa,CAAC,SAAe,CAAA,CAAA,EAAI,MAAM,CAAC,SAAS,IAAI,EAAE,CAAA,CAAA,EAAI,SAAS,CAAA,CAAE,CAAC,IAAI,EAAE;AAC3F,QAAA,IAAI,EAAE,CAAA,EAAG,aAAa,CAAC,IAAU,CAAA,CAAA,EAAI,MAAM,CAAC,IAAI,IAAI,EAAE,CAAA,CAAE,CAAC,IAAI,EAAE;AAC/D,QAAA,KAAK,EAAE,CAAA,EAAG,aAAa,CAAC,KAAW,CAAA,CAAA,EAAI,MAAM,CAAC,KAAK,IAAI,EAAE,CAAA,CAAE,CAAC,IAAI,EAAE;AAClE,QAAA,KAAK,EAAE,CAAA,EAAG,aAAa,CAAC,KAAW,CAAA,CAAA,EAAI,MAAM,CAAC,KAAK,IAAI,EAAE,CAAA,CAAE,CAAC,IAAI,EAAE;AAClE,QAAA,MAAM,EAAE,CAAA,EAAG,aAAa,CAAC,MAAY,CAAA,CAAA,EAAI,MAAM,CAAC,MAAM,IAAI,EAAE,CAAA,CAAE,CAAC,IAAI,EAAE;AACrE,QAAA,KAAK,EAAE,CAAA,EAAG,aAAa,CAAC,KAAW,CAAA,CAAA,EAAI,MAAM,CAAC,KAAK,IAAI,EAAE,CAAA,CAAE,CAAC,IAAI,EAAE;AAClE,QAAA,IAAI,EAAE,CAAA,EAAG,aAAa,CAAC,IAAU,CAAA,CAAA,EAAI,MAAM,CAAC,IAAI,IAAI,EAAE,CAAA,CAAE,CAAC,IAAI;KAC9D;AAED,IAAA,MAAM,SAAS,GAAG,IAAI,EAAE,SAAS;IACjC,MAAM,UAAU,GAAG,SAAS,GAAG,6BAA6B,GAAG,EAAE;IAEjE,QACEA,wBAAK,SAAS,EAAE,YAAY,CAAC,SAAS,KAAM,KAAK,EAAA,QAAA,EAC/CC,uBACE,MAAM,EAAC,QAAQ,EACf,GAAG,EAAC,YAAY,EAChB,IAAI,EAAE,SAAS,GAAG,IAAI,EAAE,OAAO,GAAG,WAAW,EAC7C,SAAS,EAAE,YAAY,CAAC,IAAI,EAAA,QAAA,EAAA,CAE5BD,cAAA,CAAA,KAAA,EAAA,EAAK,SAAS,EAAC,8BAA8B,YAC1C,SAAS,IACRA,wBACE,SAAS,EAAE,YAAY,CAAC,KAAK,EAC7B,GAAG,EAAE,IAAI,EAAE,aAAa,EACxB,GAAG,EAAE,IAAI,EAAE,KAAK,IAAI,aAAa,EAAA,CACjC,KAEFA,cAAA,CAACE,YAAS,EAAA,EAAC,IAAI,EAAE,EAAE,EAAE,KAAK,EAAE,SAAS,GAAI,CAC1C,EAAA,CACG,EAEND,eAAA,CAAA,KAAA,EAAA,EAAK,SAAS,EAAC,sBAAsB,EAAA,QAAA,EAAA,CACnCD,sBAAG,SAAS,EAAE,GAAG,YAAY,CAAC,KAAK,CAAA,CAAA,EAAI,UAAU,EAAE,CAAC,IAAI,EAAE,EAAA,QAAA,EACvD,SAAS,GAAG,IAAI,EAAE,KAAK,GAAG,cAAc,EAAA,CACvC,EACJA,sBAAG,SAAS,EAAE,YAAY,CAAC,MAAM,YAC9B,SAAS,GAAG,CAAA,QAAA,EAAW,IAAI,EAAE,MAAM,EAAE,GAAG,SAAS,EAAA,CAChD,EACH,SAAS,IAAI,SAAS,KACrBC,uBAAG,SAAS,EAAE,YAAY,CAAC,KAAK,wBACtB,IAAI,EAAE,KAAK,CAAA,EAAA,CACjB,CACL,IACG,EAEL,SAAS,KACRD,cAAA,CAAA,KAAA,EAAA,EAAK,SAAS,EAAE,YAAY,CAAC,IAAI,EAAA,QAAA,EAC/BA,eAACE,YAAS,EAAA,EAAC,IAAI,EAAE,EAAE,EAAE,KAAK,EAAE,SAAS,EAAA,CAAI,EAAA,CACrC,CACP,CAAA,EAAA,CACC,EAAA,CACA;AAEV;;;;;"}
|
package/dist/styles.css
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/* Default styles for spotify-now-playing-card */
|
|
2
|
+
.spotify-card-container {
|
|
3
|
+
color: #94a3b8; /* slate-400 */
|
|
4
|
+
border: 1px solid #262626; /* neutral-800 */
|
|
5
|
+
border-radius: 0.5rem;
|
|
6
|
+
width: 100%;
|
|
7
|
+
margin: 0.5rem 1.25rem;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
.spotify-card-link {
|
|
11
|
+
position: relative;
|
|
12
|
+
display: flex;
|
|
13
|
+
align-items: center;
|
|
14
|
+
gap: 0.5rem;
|
|
15
|
+
padding: 1rem 0.5rem;
|
|
16
|
+
transition: box-shadow 0.2s;
|
|
17
|
+
text-decoration: none;
|
|
18
|
+
color: inherit;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.spotify-card-link:hover {
|
|
22
|
+
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.spotify-card-image-container {
|
|
26
|
+
width: 3.5rem;
|
|
27
|
+
flex-shrink: 0;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.spotify-card-image {
|
|
31
|
+
width: 3.5rem;
|
|
32
|
+
height: 3.5rem;
|
|
33
|
+
object-fit: cover;
|
|
34
|
+
border-radius: 0.25rem;
|
|
35
|
+
box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.spotify-card-content {
|
|
39
|
+
flex: 1;
|
|
40
|
+
min-width: 0;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.spotify-card-title {
|
|
44
|
+
font-weight: 700;
|
|
45
|
+
font-size: 1rem;
|
|
46
|
+
width: 12rem;
|
|
47
|
+
overflow: hidden;
|
|
48
|
+
text-overflow: ellipsis;
|
|
49
|
+
white-space: nowrap;
|
|
50
|
+
margin: 0;
|
|
51
|
+
line-height: 1.5;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
@media (min-width: 1024px) {
|
|
55
|
+
.spotify-card-title {
|
|
56
|
+
width: 18rem;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.spotify-card-title-gradient {
|
|
61
|
+
background: linear-gradient(to right, #8b5cf6, #3b82f6, #10b981);
|
|
62
|
+
-webkit-background-clip: text;
|
|
63
|
+
background-clip: text;
|
|
64
|
+
-webkit-text-fill-color: transparent;
|
|
65
|
+
color: transparent;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.spotify-card-artist {
|
|
69
|
+
font-size: 0.875rem;
|
|
70
|
+
font-weight: 600;
|
|
71
|
+
margin: 0.25rem 0 0 0;
|
|
72
|
+
line-height: 1.5;
|
|
73
|
+
color: #94a3b8;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
.spotify-card-album {
|
|
77
|
+
font-size: 0.75rem;
|
|
78
|
+
font-weight: 600;
|
|
79
|
+
width: 12rem;
|
|
80
|
+
overflow: hidden;
|
|
81
|
+
text-overflow: ellipsis;
|
|
82
|
+
white-space: nowrap;
|
|
83
|
+
margin: 0.25rem 0 0 0;
|
|
84
|
+
line-height: 1.5;
|
|
85
|
+
color: #94a3b8;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
@media (min-width: 1024px) {
|
|
89
|
+
.spotify-card-album {
|
|
90
|
+
width: 18rem;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
.spotify-card-icon {
|
|
95
|
+
position: absolute;
|
|
96
|
+
right: 0.375rem;
|
|
97
|
+
top: 0.375rem;
|
|
98
|
+
}
|
|
99
|
+
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "spotify-now-playing-card",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "A beautiful React component to display your currently playing Spotify track",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -8,12 +8,11 @@
|
|
|
8
8
|
"types": "dist/index.d.ts",
|
|
9
9
|
"files": [
|
|
10
10
|
"dist",
|
|
11
|
+
"bin",
|
|
11
12
|
"README.md"
|
|
12
13
|
],
|
|
13
|
-
"
|
|
14
|
-
"
|
|
15
|
-
"dev": "rollup -c -w",
|
|
16
|
-
"prepublishOnly": "npm run build"
|
|
14
|
+
"bin": {
|
|
15
|
+
"spotify-card-setup": "bin/setup.js"
|
|
17
16
|
},
|
|
18
17
|
"keywords": [
|
|
19
18
|
"spotify",
|
|
@@ -28,7 +27,7 @@
|
|
|
28
27
|
"license": "MIT",
|
|
29
28
|
"repository": {
|
|
30
29
|
"type": "git",
|
|
31
|
-
"url": "https://github.com/
|
|
30
|
+
"url": "git+https://github.com/basskibo/spotify-now-playing-card.git"
|
|
32
31
|
},
|
|
33
32
|
"peerDependencies": {
|
|
34
33
|
"react": ">=16.8.0",
|
|
@@ -48,7 +47,13 @@
|
|
|
48
47
|
"react": "^18.2.0",
|
|
49
48
|
"react-dom": "^18.2.0",
|
|
50
49
|
"rollup": "^4.6.1",
|
|
50
|
+
"rollup-plugin-copy": "^3.5.0",
|
|
51
51
|
"tslib": "^2.8.1",
|
|
52
52
|
"typescript": "^5.3.3"
|
|
53
|
+
},
|
|
54
|
+
"scripts": {
|
|
55
|
+
"build": "rollup -c",
|
|
56
|
+
"dev": "rollup -c -w",
|
|
57
|
+
"setup": "node bin/setup.js"
|
|
53
58
|
}
|
|
54
|
-
}
|
|
59
|
+
}
|