snice 3.6.0 → 3.8.0
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 +2 -2
- package/bin/snice.js +4 -5
- package/bin/templates/CLAUDE.md +25 -3
- package/bin/templates/pwa/README.md +188 -0
- package/bin/templates/pwa/global.d.ts +10 -0
- package/bin/templates/pwa/index.html +16 -0
- package/bin/templates/pwa/package.json +32 -0
- package/bin/templates/pwa/public/icons/.gitkeep +6 -0
- package/bin/templates/pwa/src/daemons/notifications.ts +148 -0
- package/bin/templates/pwa/src/fetcher.ts +15 -0
- package/bin/templates/pwa/src/guards/auth.ts +12 -0
- package/bin/templates/pwa/src/main.ts +42 -0
- package/bin/templates/pwa/src/middleware/auth.ts +16 -0
- package/bin/templates/pwa/src/middleware/error.ts +36 -0
- package/bin/templates/pwa/src/middleware/retry.ts +31 -0
- package/bin/templates/pwa/src/pages/dashboard.ts +143 -0
- package/bin/templates/pwa/src/pages/login.ts +161 -0
- package/bin/templates/pwa/src/pages/notifications.ts +156 -0
- package/bin/templates/pwa/src/pages/profile.ts +164 -0
- package/bin/templates/pwa/src/router.ts +20 -0
- package/bin/templates/pwa/src/services/auth.ts +48 -0
- package/bin/templates/pwa/src/services/jwt.ts +35 -0
- package/bin/templates/pwa/src/services/storage.ts +24 -0
- package/bin/templates/pwa/src/styles/global.css +55 -0
- package/bin/templates/pwa/src/types/auth.ts +21 -0
- package/bin/templates/pwa/src/types/notifications.ts +9 -0
- package/bin/templates/pwa/tests/helpers/test-utils.ts +84 -0
- package/bin/templates/pwa/tests/middleware/auth.test.ts +67 -0
- package/bin/templates/pwa/tests/middleware/error.test.ts +105 -0
- package/bin/templates/pwa/tests/middleware/retry.test.ts +103 -0
- package/bin/templates/pwa/tests/services/auth.test.ts +89 -0
- package/bin/templates/pwa/tests/services/jwt.test.ts +76 -0
- package/bin/templates/pwa/tests/services/storage.test.ts +69 -0
- package/bin/templates/{social → pwa}/tsconfig.json +11 -10
- package/bin/templates/pwa/vite.config.ts +94 -0
- package/bin/templates/{social/vite.config.ts → pwa/vitest.config.ts} +12 -17
- package/dist/components/music-player/snice-music-player.d.ts +72 -0
- package/dist/components/music-player/snice-music-player.js +730 -0
- package/dist/components/music-player/snice-music-player.js.map +1 -0
- package/dist/components/music-player/snice-music-player.types.d.ts +43 -0
- package/dist/components/timer/snice-timer.d.ts +27 -0
- package/dist/components/timer/snice-timer.js +197 -0
- package/dist/components/timer/snice-timer.js.map +1 -0
- package/dist/components/timer/snice-timer.types.d.ts +10 -0
- package/dist/fetcher.d.ts +65 -0
- package/dist/index.cjs +92 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.esm.js +92 -4
- package/dist/index.esm.js.map +1 -1
- package/dist/index.iife.js +92 -3
- package/dist/index.iife.js.map +1 -1
- package/dist/symbols.cjs +1 -1
- package/dist/symbols.esm.js +1 -1
- package/dist/transitions.cjs +1 -1
- package/dist/transitions.esm.js +1 -1
- package/dist/types/context.d.ts +7 -1
- package/dist/types/router-options.d.ts +6 -0
- package/docs/ai/api.md +33 -1
- package/docs/ai/components/music-player.md +134 -0
- package/docs/ai/components/timer.md +43 -0
- package/docs/ai/patterns.md +48 -1
- package/docs/components/music-player.md +314 -0
- package/docs/components/timer.md +143 -0
- package/docs/fetcher.md +447 -0
- package/docs/routing.md +11 -8
- package/package.json +2 -1
- package/bin/templates/social/README.md +0 -42
- package/bin/templates/social/global.d.ts +0 -14
- package/bin/templates/social/index.html +0 -13
- package/bin/templates/social/package.json +0 -21
- package/bin/templates/social/src/main.ts +0 -33
- package/bin/templates/social/src/pages/feed-page.ts +0 -111
- package/bin/templates/social/src/pages/messages-page.ts +0 -102
- package/bin/templates/social/src/pages/not-found-page.ts +0 -46
- package/bin/templates/social/src/pages/profile-page.ts +0 -99
- package/bin/templates/social/src/pages/settings-page.ts +0 -119
- package/bin/templates/social/src/router.ts +0 -9
- package/bin/templates/social/src/styles/global.css +0 -156
- /package/bin/templates/{social → pwa}/public/vite.svg +0 -0
package/dist/symbols.cjs
CHANGED
package/dist/symbols.esm.js
CHANGED
package/dist/transitions.cjs
CHANGED
package/dist/transitions.esm.js
CHANGED
package/dist/types/context.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { NavContext } from './nav-context';
|
|
|
3
3
|
import { Placard } from './placard';
|
|
4
4
|
import { RouteParams } from './route-params';
|
|
5
5
|
import { REGISTERED_ELEMENTS, IS_UPDATING, CONTEXT_REGISTER, CONTEXT_UNREGISTER, CONTEXT_NOTIFY_ELEMENT } from '../symbols';
|
|
6
|
+
import type { Fetcher } from '../fetcher';
|
|
6
7
|
declare const REGISTERED_ELEMENTS_SET: unique symbol;
|
|
7
8
|
/**
|
|
8
9
|
* Represents the bundled router state that can notify registered elements of changes
|
|
@@ -38,7 +39,12 @@ export declare class Context {
|
|
|
38
39
|
* Navigation state
|
|
39
40
|
*/
|
|
40
41
|
navigation: NavContext;
|
|
41
|
-
|
|
42
|
+
/**
|
|
43
|
+
* Fetch function with optional middleware support
|
|
44
|
+
* Bound to this Context instance, allowing middleware to access application and navigation state
|
|
45
|
+
*/
|
|
46
|
+
fetch: typeof globalThis.fetch;
|
|
47
|
+
constructor(context?: AppContext, placards?: Placard[], currentRoute?: string, routeParams?: RouteParams, fetcher?: Fetcher);
|
|
42
48
|
/**
|
|
43
49
|
* Update the context and notify all registered elements
|
|
44
50
|
* Prevents infinite loops by tracking update state
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Transition } from './transition';
|
|
2
|
+
import type { Fetcher } from '../fetcher';
|
|
2
3
|
export interface RouterOptions {
|
|
3
4
|
/**
|
|
4
5
|
* The target element selector where the page element will be instantiated.
|
|
@@ -29,4 +30,9 @@ export interface RouterOptions {
|
|
|
29
30
|
* Default layout element tag name for all pages
|
|
30
31
|
*/
|
|
31
32
|
layout?: string;
|
|
33
|
+
/**
|
|
34
|
+
* Optional fetcher for context-aware HTTP requests with middleware support
|
|
35
|
+
* If not provided, Context.fetch will default to native fetch
|
|
36
|
+
*/
|
|
37
|
+
fetcher?: Fetcher;
|
|
32
38
|
}
|
package/docs/ai/api.md
CHANGED
|
@@ -147,13 +147,45 @@ html`
|
|
|
147
147
|
// ctx.update(); // notify all subscribers
|
|
148
148
|
// }
|
|
149
149
|
|
|
150
|
-
Router({ target, context?, layout? })
|
|
150
|
+
Router({ target, context?, layout?, fetcher? })
|
|
151
151
|
// Returns: { page, navigate, initialize }
|
|
152
152
|
// page() - decorator factory
|
|
153
153
|
// navigate(path) - programmatic navigation
|
|
154
154
|
// initialize() - start router
|
|
155
155
|
```
|
|
156
156
|
|
|
157
|
+
## Fetcher
|
|
158
|
+
|
|
159
|
+
```typescript
|
|
160
|
+
class ContextAwareFetcher implements Fetcher {
|
|
161
|
+
use(type: 'request', middleware: RequestMiddleware): void
|
|
162
|
+
use(type: 'response', middleware: ResponseMiddleware): void
|
|
163
|
+
create(ctx: Context): typeof globalThis.fetch
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
type RequestMiddleware = (
|
|
167
|
+
this: Context,
|
|
168
|
+
request: Request,
|
|
169
|
+
next: () => Promise<Response>
|
|
170
|
+
) => Promise<Response>
|
|
171
|
+
|
|
172
|
+
type ResponseMiddleware = (
|
|
173
|
+
this: Context,
|
|
174
|
+
response: Response,
|
|
175
|
+
next: () => Promise<Response>
|
|
176
|
+
) => Promise<Response>
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
**Usage:**
|
|
180
|
+
- Create fetcher, add middleware via `.use('request', fn)` and `.use('response', fn)`
|
|
181
|
+
- Request middleware runs before fetch, response middleware after
|
|
182
|
+
- Middleware `this` bound to Context instance
|
|
183
|
+
- Access `this.application` and `this.navigation` in middleware
|
|
184
|
+
- Pass to Router via `fetcher` option
|
|
185
|
+
- Use via `ctx.fetch()` in pages/components
|
|
186
|
+
- Optional - defaults to native fetch if not provided
|
|
187
|
+
```
|
|
188
|
+
|
|
157
189
|
## Templates
|
|
158
190
|
|
|
159
191
|
```typescript
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# snice-music-player
|
|
2
|
+
|
|
3
|
+
Full-featured audio player with playlist, shuffle, repeat, and volume control.
|
|
4
|
+
|
|
5
|
+
## Properties
|
|
6
|
+
|
|
7
|
+
```typescript
|
|
8
|
+
tracks: Track[] = [];
|
|
9
|
+
currentTrackIndex: number = 0;
|
|
10
|
+
currentTrack: string = ''; // reflected attribute
|
|
11
|
+
currentTime: number = 0; // read-only, use seek()
|
|
12
|
+
duration: number = 0;
|
|
13
|
+
volume: number = 1;
|
|
14
|
+
muted: boolean = false;
|
|
15
|
+
shuffle: boolean = false;
|
|
16
|
+
repeat: 'off'|'all'|'one' = 'off';
|
|
17
|
+
state: 'playing'|'paused'|'stopped'|'loading'|'error' = 'stopped';
|
|
18
|
+
autoplay: boolean = false;
|
|
19
|
+
showPlaylist: boolean = true;
|
|
20
|
+
showControls: boolean = true;
|
|
21
|
+
showVolume: boolean = true;
|
|
22
|
+
showArtwork: boolean = true;
|
|
23
|
+
showTrackInfo: boolean = true;
|
|
24
|
+
compact: boolean = false;
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Methods
|
|
28
|
+
|
|
29
|
+
```typescript
|
|
30
|
+
play(): Promise<void>
|
|
31
|
+
pause(): void
|
|
32
|
+
stop(): void
|
|
33
|
+
next(): void
|
|
34
|
+
previous(): void
|
|
35
|
+
seek(time: number): void
|
|
36
|
+
setVolume(volume: number): void
|
|
37
|
+
toggleShuffle(): void
|
|
38
|
+
setRepeat(mode: 'off'|'all'|'one'): void
|
|
39
|
+
loadTrack(index: number): Promise<void>
|
|
40
|
+
getCurrentTrack(): Track | null
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Track Interface
|
|
44
|
+
|
|
45
|
+
```typescript
|
|
46
|
+
interface Track {
|
|
47
|
+
id: string;
|
|
48
|
+
title: string;
|
|
49
|
+
artist?: string;
|
|
50
|
+
album?: string;
|
|
51
|
+
artwork?: string;
|
|
52
|
+
src: string;
|
|
53
|
+
duration?: number;
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Events
|
|
58
|
+
|
|
59
|
+
- `@snice/player-play` - Playback started
|
|
60
|
+
- `@snice/player-pause` - Playback paused
|
|
61
|
+
- `@snice/player-stop` - Playback stopped
|
|
62
|
+
- `@snice/player-track-change` - Track changed
|
|
63
|
+
- `@snice/player-track-ended` - Track ended
|
|
64
|
+
- `@snice/player-shuffle-change` - Shuffle changed
|
|
65
|
+
- `@snice/player-repeat-change` - Repeat mode changed
|
|
66
|
+
- `@snice/player-volume-change` - Volume changed
|
|
67
|
+
- `@snice/player-time-update` - Time updated
|
|
68
|
+
- `@snice/player-error` - Error occurred
|
|
69
|
+
|
|
70
|
+
## Usage
|
|
71
|
+
|
|
72
|
+
```javascript
|
|
73
|
+
// Set tracks
|
|
74
|
+
player.tracks = [
|
|
75
|
+
{
|
|
76
|
+
id: '1',
|
|
77
|
+
title: 'Song Title',
|
|
78
|
+
artist: 'Artist Name',
|
|
79
|
+
album: 'Album Name',
|
|
80
|
+
artwork: 'https://example.com/art.jpg',
|
|
81
|
+
src: 'https://example.com/audio.mp3',
|
|
82
|
+
duration: 180
|
|
83
|
+
}
|
|
84
|
+
];
|
|
85
|
+
|
|
86
|
+
// Controls
|
|
87
|
+
await player.play();
|
|
88
|
+
player.pause();
|
|
89
|
+
player.stop();
|
|
90
|
+
player.next();
|
|
91
|
+
player.previous();
|
|
92
|
+
player.seek(30);
|
|
93
|
+
|
|
94
|
+
// Volume
|
|
95
|
+
player.setVolume(0.5);
|
|
96
|
+
|
|
97
|
+
// Shuffle & repeat
|
|
98
|
+
player.toggleShuffle();
|
|
99
|
+
player.setRepeat('all');
|
|
100
|
+
|
|
101
|
+
// Load track
|
|
102
|
+
await player.loadTrack(2);
|
|
103
|
+
|
|
104
|
+
// Get current
|
|
105
|
+
const track = player.getCurrentTrack();
|
|
106
|
+
|
|
107
|
+
// Get/set via currentTrack attribute
|
|
108
|
+
console.log(player.currentTrack); // track ID
|
|
109
|
+
player.currentTrack = 'track-2'; // loads track by ID
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
```html
|
|
113
|
+
<snice-music-player
|
|
114
|
+
autoplay
|
|
115
|
+
shuffle
|
|
116
|
+
compact
|
|
117
|
+
show-playlist="false">
|
|
118
|
+
</snice-music-player>
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## Features
|
|
122
|
+
|
|
123
|
+
- HTML5 Audio API
|
|
124
|
+
- Playlist support
|
|
125
|
+
- Play/pause/stop/next/previous
|
|
126
|
+
- Shuffle with randomization
|
|
127
|
+
- Repeat modes: off, all, one
|
|
128
|
+
- Volume control (vertical slider)
|
|
129
|
+
- Progress bar with seek
|
|
130
|
+
- Real-time updates
|
|
131
|
+
- Track artwork & metadata
|
|
132
|
+
- Compact mode
|
|
133
|
+
- Event-driven
|
|
134
|
+
- Clickable playlist
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# Timer
|
|
2
|
+
|
|
3
|
+
```html
|
|
4
|
+
<snice-timer mode="stopwatch"></snice-timer>
|
|
5
|
+
<snice-timer mode="timer" initial-time="60"></snice-timer>
|
|
6
|
+
```
|
|
7
|
+
|
|
8
|
+
## Properties
|
|
9
|
+
- `mode`: 'stopwatch' | 'timer' (default: 'stopwatch')
|
|
10
|
+
- `initial-time`: number (default: 0) - Starting time in seconds for timer mode
|
|
11
|
+
- `running`: boolean (read-only)
|
|
12
|
+
|
|
13
|
+
## Methods
|
|
14
|
+
- `start()`: Start timer
|
|
15
|
+
- `stop()`: Stop/pause timer
|
|
16
|
+
- `reset()`: Reset to initial state
|
|
17
|
+
- `getTime()`: Get current time in seconds
|
|
18
|
+
|
|
19
|
+
## Events
|
|
20
|
+
- `@snice/timer-start`: { timer, time }
|
|
21
|
+
- `@snice/timer-stop`: { timer, time }
|
|
22
|
+
- `@snice/timer-reset`: { timer, time }
|
|
23
|
+
- `@snice/timer-complete`: { timer } - Countdown reached 0
|
|
24
|
+
|
|
25
|
+
## Implementation
|
|
26
|
+
- Uses requestAnimationFrame for smooth updates
|
|
27
|
+
- Direct DOM manipulation for display to avoid re-renders
|
|
28
|
+
- Stopwatch: counts up from 0
|
|
29
|
+
- Timer: counts down from initial-time
|
|
30
|
+
- Auto-stops at 0 in timer mode
|
|
31
|
+
|
|
32
|
+
## Display Format
|
|
33
|
+
- Under 1 hour: `M:SS.D` (e.g., "2:05.3")
|
|
34
|
+
- Over 1 hour: `H:MM:SS` (e.g., "1:05:30")
|
|
35
|
+
|
|
36
|
+
## Theme Integration
|
|
37
|
+
Uses standard theme tokens:
|
|
38
|
+
- Background: `--snice-color-background-element`
|
|
39
|
+
- Borders: `--snice-color-border`
|
|
40
|
+
- Text: `--snice-color-text`
|
|
41
|
+
- Start button: `--snice-color-success`
|
|
42
|
+
- Pause button: `--snice-color-warning`
|
|
43
|
+
- Reset button: `--snice-color-neutral`
|
package/docs/ai/patterns.md
CHANGED
|
@@ -56,7 +56,7 @@ class UserPage extends HTMLElement {
|
|
|
56
56
|
private appContext?: AppContext;
|
|
57
57
|
|
|
58
58
|
@context()
|
|
59
|
-
handleContext(ctx: Context
|
|
59
|
+
handleContext(ctx: Context) {
|
|
60
60
|
this.appContext = ctx.application;
|
|
61
61
|
this.requestRender();
|
|
62
62
|
}
|
|
@@ -115,6 +115,53 @@ class UserProfile extends HTMLElement {
|
|
|
115
115
|
}
|
|
116
116
|
```
|
|
117
117
|
|
|
118
|
+
## Fetch with Middleware
|
|
119
|
+
|
|
120
|
+
```typescript
|
|
121
|
+
import { Router, ContextAwareFetcher } from 'snice';
|
|
122
|
+
|
|
123
|
+
const fetcher = new ContextAwareFetcher();
|
|
124
|
+
|
|
125
|
+
// Request middleware - modify request before fetch
|
|
126
|
+
fetcher.use('request', function(request, next) {
|
|
127
|
+
const token = this.application.user?.token;
|
|
128
|
+
if (token) {
|
|
129
|
+
request.headers.set('Authorization', `Bearer ${token}`);
|
|
130
|
+
}
|
|
131
|
+
return next();
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
// Response middleware - handle response after fetch
|
|
135
|
+
fetcher.use('response', async function(response, next) {
|
|
136
|
+
if (!response.ok) {
|
|
137
|
+
throw new Error(`HTTP ${response.status}`);
|
|
138
|
+
}
|
|
139
|
+
return next();
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
const router = Router({
|
|
143
|
+
target: '#app',
|
|
144
|
+
context: { user: null },
|
|
145
|
+
fetcher
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
// In pages
|
|
149
|
+
@page({ tag: 'user-page', routes: ['/users/:id'] })
|
|
150
|
+
class UserPage extends HTMLElement {
|
|
151
|
+
private ctx: Context;
|
|
152
|
+
|
|
153
|
+
@context()
|
|
154
|
+
handleContext(ctx: Context) {
|
|
155
|
+
this.ctx = ctx;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
@ready()
|
|
159
|
+
async load() {
|
|
160
|
+
const user = await this.ctx.fetch('/api/users/123').then(r => r.json());
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
```
|
|
164
|
+
|
|
118
165
|
## Conditional Rendering
|
|
119
166
|
```typescript
|
|
120
167
|
html`
|
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
# Music Player Component
|
|
2
|
+
|
|
3
|
+
A full-featured audio player with playlist support, shuffle, repeat modes, and volume control.
|
|
4
|
+
|
|
5
|
+
## Basic Usage
|
|
6
|
+
|
|
7
|
+
```html
|
|
8
|
+
<snice-music-player id="player"></snice-music-player>
|
|
9
|
+
|
|
10
|
+
<script>
|
|
11
|
+
const player = document.getElementById('player');
|
|
12
|
+
|
|
13
|
+
// Set tracks
|
|
14
|
+
player.tracks = [
|
|
15
|
+
{
|
|
16
|
+
id: 'track-1',
|
|
17
|
+
title: 'Song Title',
|
|
18
|
+
artist: 'Artist Name',
|
|
19
|
+
album: 'Album Name',
|
|
20
|
+
artwork: 'https://example.com/artwork.jpg',
|
|
21
|
+
src: 'https://example.com/audio.mp3',
|
|
22
|
+
duration: 180
|
|
23
|
+
}
|
|
24
|
+
];
|
|
25
|
+
|
|
26
|
+
// Listen for events
|
|
27
|
+
player.addEventListener('@snice/player-play', (e) => {
|
|
28
|
+
console.log('Playing:', e.detail.track.title);
|
|
29
|
+
});
|
|
30
|
+
</script>
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Properties
|
|
34
|
+
|
|
35
|
+
| Property | Type | Default | Description |
|
|
36
|
+
|----------|------|---------|-------------|
|
|
37
|
+
| `tracks` | `Track[]` | `[]` | Array of tracks |
|
|
38
|
+
| `currentTrackIndex` | `number` | `0` | Current track index |
|
|
39
|
+
| `currentTrack` | `string` | `''` | Current track ID (reflected attribute) |
|
|
40
|
+
| `currentTime` | `number` | `0` | Current playback time (read-only, use seek() to set) |
|
|
41
|
+
| `duration` | `number` | `0` | Track duration (seconds) |
|
|
42
|
+
| `volume` | `number` | `1` | Volume (0-1) |
|
|
43
|
+
| `muted` | `boolean` | `false` | Mute state |
|
|
44
|
+
| `shuffle` | `boolean` | `false` | Shuffle mode |
|
|
45
|
+
| `repeat` | `RepeatMode` | `'off'` | Repeat mode |
|
|
46
|
+
| `state` | `PlayerState` | `'stopped'` | Playback state |
|
|
47
|
+
| `autoplay` | `boolean` | `false` | Auto-play on load |
|
|
48
|
+
| `showPlaylist` | `boolean` | `true` | Show playlist |
|
|
49
|
+
| `showControls` | `boolean` | `true` | Show control buttons |
|
|
50
|
+
| `showVolume` | `boolean` | `true` | Show volume control |
|
|
51
|
+
| `showArtwork` | `boolean` | `true` | Show artwork |
|
|
52
|
+
| `showTrackInfo` | `boolean` | `true` | Show track info |
|
|
53
|
+
| `compact` | `boolean` | `false` | Compact mode |
|
|
54
|
+
|
|
55
|
+
## Track Interface
|
|
56
|
+
|
|
57
|
+
```typescript
|
|
58
|
+
interface Track {
|
|
59
|
+
id: string;
|
|
60
|
+
title: string;
|
|
61
|
+
artist?: string;
|
|
62
|
+
album?: string;
|
|
63
|
+
artwork?: string;
|
|
64
|
+
src: string;
|
|
65
|
+
duration?: number;
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Repeat Modes
|
|
70
|
+
|
|
71
|
+
- `'off'` - No repeat
|
|
72
|
+
- `'all'` - Repeat all tracks
|
|
73
|
+
- `'one'` - Repeat current track
|
|
74
|
+
|
|
75
|
+
## Player States
|
|
76
|
+
|
|
77
|
+
- `'playing'` - Currently playing
|
|
78
|
+
- `'paused'` - Paused
|
|
79
|
+
- `'stopped'` - Stopped
|
|
80
|
+
- `'loading'` - Loading track
|
|
81
|
+
- `'error'` - Error occurred
|
|
82
|
+
|
|
83
|
+
## Methods
|
|
84
|
+
|
|
85
|
+
### `play(): Promise<void>`
|
|
86
|
+
Start or resume playback.
|
|
87
|
+
|
|
88
|
+
### `pause(): void`
|
|
89
|
+
Pause playback.
|
|
90
|
+
|
|
91
|
+
### `stop(): void`
|
|
92
|
+
Stop playback and reset position.
|
|
93
|
+
|
|
94
|
+
### `next(): void`
|
|
95
|
+
Skip to next track.
|
|
96
|
+
|
|
97
|
+
### `previous(): void`
|
|
98
|
+
Skip to previous track.
|
|
99
|
+
|
|
100
|
+
### `seek(time: number): void`
|
|
101
|
+
Seek to specific time (seconds).
|
|
102
|
+
|
|
103
|
+
### `setVolume(volume: number): void`
|
|
104
|
+
Set volume (0-1).
|
|
105
|
+
|
|
106
|
+
### `toggleShuffle(): void`
|
|
107
|
+
Toggle shuffle mode.
|
|
108
|
+
|
|
109
|
+
### `setRepeat(mode: RepeatMode): void`
|
|
110
|
+
Set repeat mode.
|
|
111
|
+
|
|
112
|
+
### `loadTrack(index: number): Promise<void>`
|
|
113
|
+
Load track by index.
|
|
114
|
+
|
|
115
|
+
### `getCurrentTrack(): Track | null`
|
|
116
|
+
Get current track.
|
|
117
|
+
|
|
118
|
+
## Events
|
|
119
|
+
|
|
120
|
+
- `@snice/player-play` - Playback started
|
|
121
|
+
- `@snice/player-pause` - Playback paused
|
|
122
|
+
- `@snice/player-stop` - Playback stopped
|
|
123
|
+
- `@snice/player-track-change` - Track changed
|
|
124
|
+
- `@snice/player-track-ended` - Track ended
|
|
125
|
+
- `@snice/player-shuffle-change` - Shuffle changed
|
|
126
|
+
- `@snice/player-repeat-change` - Repeat mode changed
|
|
127
|
+
- `@snice/player-volume-change` - Volume changed
|
|
128
|
+
- `@snice/player-time-update` - Playback time updated
|
|
129
|
+
- `@snice/player-error` - Error occurred
|
|
130
|
+
|
|
131
|
+
## Examples
|
|
132
|
+
|
|
133
|
+
### Full Player with Playlist
|
|
134
|
+
|
|
135
|
+
```html
|
|
136
|
+
<snice-music-player id="player"></snice-music-player>
|
|
137
|
+
|
|
138
|
+
<script>
|
|
139
|
+
const player = document.getElementById('player');
|
|
140
|
+
|
|
141
|
+
player.tracks = [
|
|
142
|
+
{
|
|
143
|
+
id: '1',
|
|
144
|
+
title: 'Summer Breeze',
|
|
145
|
+
artist: 'The Acoustic Collective',
|
|
146
|
+
album: 'Peaceful Moments',
|
|
147
|
+
artwork: 'https://example.com/art1.jpg',
|
|
148
|
+
src: 'https://example.com/track1.mp3',
|
|
149
|
+
duration: 360
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
id: '2',
|
|
153
|
+
title: 'Midnight Jazz',
|
|
154
|
+
artist: 'Smooth Notes Ensemble',
|
|
155
|
+
album: 'Late Night Sessions',
|
|
156
|
+
artwork: 'https://example.com/art2.jpg',
|
|
157
|
+
src: 'https://example.com/track2.mp3',
|
|
158
|
+
duration: 420
|
|
159
|
+
}
|
|
160
|
+
];
|
|
161
|
+
</script>
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### Compact Player (No Playlist)
|
|
165
|
+
|
|
166
|
+
```html
|
|
167
|
+
<snice-music-player
|
|
168
|
+
show-playlist="false"
|
|
169
|
+
compact>
|
|
170
|
+
</snice-music-player>
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### Minimal Player
|
|
174
|
+
|
|
175
|
+
```html
|
|
176
|
+
<snice-music-player
|
|
177
|
+
show-playlist="false"
|
|
178
|
+
show-artwork="false"
|
|
179
|
+
show-track-info="false">
|
|
180
|
+
</snice-music-player>
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
### Programmatic Control
|
|
184
|
+
|
|
185
|
+
```javascript
|
|
186
|
+
const player = document.querySelector('snice-music-player');
|
|
187
|
+
|
|
188
|
+
// Play/Pause
|
|
189
|
+
await player.play();
|
|
190
|
+
player.pause();
|
|
191
|
+
player.stop();
|
|
192
|
+
|
|
193
|
+
// Navigation
|
|
194
|
+
player.next();
|
|
195
|
+
player.previous();
|
|
196
|
+
|
|
197
|
+
// Seek
|
|
198
|
+
player.seek(30); // Jump to 30 seconds
|
|
199
|
+
|
|
200
|
+
// Volume
|
|
201
|
+
player.setVolume(0.5); // 50% volume
|
|
202
|
+
|
|
203
|
+
// Shuffle
|
|
204
|
+
player.toggleShuffle();
|
|
205
|
+
|
|
206
|
+
// Repeat
|
|
207
|
+
player.setRepeat('all');
|
|
208
|
+
player.setRepeat('one');
|
|
209
|
+
player.setRepeat('off');
|
|
210
|
+
|
|
211
|
+
// Load track
|
|
212
|
+
await player.loadTrack(2); // Load third track
|
|
213
|
+
|
|
214
|
+
// Get current track
|
|
215
|
+
const track = player.getCurrentTrack();
|
|
216
|
+
console.log(track.title, track.artist);
|
|
217
|
+
|
|
218
|
+
// Get/set via currentTrack attribute
|
|
219
|
+
console.log(player.currentTrack); // Returns track ID
|
|
220
|
+
player.currentTrack = 'track-2'; // Loads track with this ID
|
|
221
|
+
|
|
222
|
+
// Change time via seek (currentTime is read-only)
|
|
223
|
+
player.seek(30); // Jump to 30 seconds
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
### Event Handling
|
|
227
|
+
|
|
228
|
+
```javascript
|
|
229
|
+
const player = document.querySelector('snice-music-player');
|
|
230
|
+
|
|
231
|
+
player.addEventListener('@snice/player-play', (e) => {
|
|
232
|
+
console.log('Playing:', e.detail.track);
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
player.addEventListener('@snice/player-pause', (e) => {
|
|
236
|
+
console.log('Paused:', e.detail.track);
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
player.addEventListener('@snice/player-track-change', (e) => {
|
|
240
|
+
console.log('Track changed:', e.detail.track);
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
player.addEventListener('@snice/player-track-ended', (e) => {
|
|
244
|
+
console.log('Track ended:', e.detail.track);
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
player.addEventListener('@snice/player-time-update', (e) => {
|
|
248
|
+
console.log('Time:', e.detail.currentTime, '/', e.detail.duration);
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
player.addEventListener('@snice/player-error', (e) => {
|
|
252
|
+
console.error('Error:', e.detail.error);
|
|
253
|
+
});
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
### Auto-play
|
|
257
|
+
|
|
258
|
+
```html
|
|
259
|
+
<snice-music-player autoplay></snice-music-player>
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
**Note:** Autoplay may be blocked by browser policies. User interaction is usually required.
|
|
263
|
+
|
|
264
|
+
### Dynamic Track Loading
|
|
265
|
+
|
|
266
|
+
```javascript
|
|
267
|
+
const player = document.querySelector('snice-music-player');
|
|
268
|
+
|
|
269
|
+
// Load tracks from API
|
|
270
|
+
const response = await fetch('/api/tracks');
|
|
271
|
+
const tracks = await response.json();
|
|
272
|
+
|
|
273
|
+
player.tracks = tracks;
|
|
274
|
+
await player.loadTrack(0);
|
|
275
|
+
await player.play();
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
### Playlist Click Handler
|
|
279
|
+
|
|
280
|
+
```javascript
|
|
281
|
+
// Tracks in playlist are clickable by default
|
|
282
|
+
// Listen for track changes
|
|
283
|
+
player.addEventListener('@snice/player-track-change', (e) => {
|
|
284
|
+
console.log('User selected:', e.detail.track.title);
|
|
285
|
+
});
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
## Features
|
|
289
|
+
|
|
290
|
+
- Full-featured audio player with HTML5 Audio API
|
|
291
|
+
- Playlist support with clickable tracks
|
|
292
|
+
- Play, pause, stop, next, previous controls
|
|
293
|
+
- Shuffle mode with randomization
|
|
294
|
+
- Repeat modes: off, all, one
|
|
295
|
+
- Volume control with vertical slider
|
|
296
|
+
- Progress bar with seek support
|
|
297
|
+
- Real-time progress updates
|
|
298
|
+
- Track artwork and metadata display
|
|
299
|
+
- Compact mode for smaller layouts
|
|
300
|
+
- Fully customizable with show/hide options
|
|
301
|
+
- Event-driven architecture
|
|
302
|
+
- TypeScript support
|
|
303
|
+
|
|
304
|
+
## Browser Support
|
|
305
|
+
|
|
306
|
+
- Modern browsers with HTML5 Audio support
|
|
307
|
+
- Requires JavaScript enabled
|
|
308
|
+
|
|
309
|
+
## Accessibility
|
|
310
|
+
|
|
311
|
+
- Keyboard navigation support
|
|
312
|
+
- ARIA labels on controls
|
|
313
|
+
- Semantic HTML structure
|
|
314
|
+
- Focus indicators
|