snice 3.5.0 → 3.7.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/bin/snice.js +2 -1
- package/bin/templates/CLAUDE.md +24 -2
- package/bin/templates/pwa/README.md +173 -0
- package/bin/templates/pwa/global.d.ts +10 -0
- package/bin/templates/pwa/index.html +17 -0
- package/bin/templates/pwa/package.json +25 -0
- package/bin/templates/pwa/public/icons/.gitkeep +6 -0
- package/bin/templates/pwa/public/manifest.json +24 -0
- package/bin/templates/pwa/public/vite.svg +1 -0
- package/bin/templates/pwa/src/daemons/notifications.ts +148 -0
- package/bin/templates/pwa/src/guards/auth.ts +10 -0
- package/bin/templates/pwa/src/main.ts +42 -0
- package/bin/templates/pwa/src/middleware/auth.ts +15 -0
- package/bin/templates/pwa/src/middleware/error.ts +25 -0
- package/bin/templates/pwa/src/middleware/retry.ts +27 -0
- package/bin/templates/pwa/src/pages/dashboard.ts +142 -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 +163 -0
- package/bin/templates/pwa/src/router.ts +16 -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/src/utils/fetch.ts +39 -0
- package/bin/templates/pwa/tsconfig.json +23 -0
- package/bin/templates/pwa/vite.config.ts +94 -0
- package/dist/components/audio-recorder/snice-audio-recorder.d.ts +14 -4
- package/dist/components/audio-recorder/snice-audio-recorder.js +248 -71
- package/dist/components/audio-recorder/snice-audio-recorder.js.map +1 -1
- package/dist/components/audio-recorder/snice-audio-recorder.types.d.ts +2 -0
- 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/terminal.md +147 -0
- package/docs/ai/components/timer.md +43 -0
- package/docs/ai/patterns.md +47 -0
- package/docs/components/music-player.md +314 -0
- package/docs/components/terminal.md +451 -0
- package/docs/components/timer.md +143 -0
- package/docs/fetcher.md +447 -0
- package/docs/routing.md +2 -0
- package/package.json +2 -1
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,147 @@
|
|
|
1
|
+
# snice-terminal
|
|
2
|
+
|
|
3
|
+
Shell terminal emulator with command execution, history, ANSI colors, and keyboard navigation.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
```html
|
|
8
|
+
<snice-terminal prompt="$ " cwd="~"></snice-terminal>
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Properties
|
|
12
|
+
|
|
13
|
+
- `prompt: string` - Terminal prompt (default: `"$ "`)
|
|
14
|
+
- `cwd: string` - Current working directory (default: `"~"`)
|
|
15
|
+
- `readonly: boolean` - Disable input (default: `false`)
|
|
16
|
+
- `maxLines: number` - Max lines in history (default: `1000`)
|
|
17
|
+
- `showTimestamps: boolean` - Show line timestamps (default: `false`)
|
|
18
|
+
|
|
19
|
+
## Methods
|
|
20
|
+
|
|
21
|
+
- `write(content: string, type?: TerminalLineType): void` - Write without newline
|
|
22
|
+
- `writeln(content: string, type?: TerminalLineType): void` - Write with newline
|
|
23
|
+
- `writeLines(lines: Array<{ content: string; type?: TerminalLineType }>): void` - Write multiple lines
|
|
24
|
+
- `writeError(content: string): void` - Write error line
|
|
25
|
+
- `clear(): void` - Clear terminal
|
|
26
|
+
- `focus(): void` - Focus input
|
|
27
|
+
- `getHistory(): string[]` - Get command history
|
|
28
|
+
- `clearHistory(): void` - Clear command history
|
|
29
|
+
|
|
30
|
+
## Events
|
|
31
|
+
|
|
32
|
+
- `@snice/terminal-command: CustomEvent<{ command: string; args: string[] }>` - Command entered
|
|
33
|
+
- `@snice/terminal-clear: CustomEvent<{}>` - Terminal cleared
|
|
34
|
+
- `@snice/terminal-ready: CustomEvent<{}>` - Terminal ready
|
|
35
|
+
|
|
36
|
+
## Request/Response Pattern
|
|
37
|
+
|
|
38
|
+
Terminal uses `@request('terminal-command')` decorator pattern:
|
|
39
|
+
|
|
40
|
+
```typescript
|
|
41
|
+
// Terminal component makes request
|
|
42
|
+
@request('terminal-command')
|
|
43
|
+
async *executeCommand(commandLine: string): any {
|
|
44
|
+
const response = await (yield payload);
|
|
45
|
+
return response;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Controller responds
|
|
49
|
+
@respond('terminal-command')
|
|
50
|
+
async handleCommand(payload: TerminalCommandRequest) {
|
|
51
|
+
const { command, args, cwd } = payload;
|
|
52
|
+
// Execute command
|
|
53
|
+
return { output: 'result', exitCode: 0 };
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Line Types
|
|
58
|
+
|
|
59
|
+
- `input` - User input
|
|
60
|
+
- `output` - Command output
|
|
61
|
+
- `error` - Error message
|
|
62
|
+
- `info` - Info message
|
|
63
|
+
- `success` - Success message
|
|
64
|
+
- `warning` - Warning message
|
|
65
|
+
|
|
66
|
+
## Keyboard Shortcuts
|
|
67
|
+
|
|
68
|
+
- `Enter` - Execute command
|
|
69
|
+
- `↑/↓` - Navigate history
|
|
70
|
+
- `Ctrl+C` - Cancel input
|
|
71
|
+
- `Ctrl+L` - Clear terminal
|
|
72
|
+
- `Tab` - Command completion (TODO)
|
|
73
|
+
|
|
74
|
+
## ANSI Color Support
|
|
75
|
+
|
|
76
|
+
Supports ANSI escape codes for colors:
|
|
77
|
+
- `30-37` - Standard colors
|
|
78
|
+
- `90-97` - Bright colors
|
|
79
|
+
|
|
80
|
+
Special output `\x1B[CLEAR]` clears terminal.
|
|
81
|
+
|
|
82
|
+
## CSS Variables
|
|
83
|
+
|
|
84
|
+
```css
|
|
85
|
+
--snice-terminal-background
|
|
86
|
+
--snice-terminal-foreground
|
|
87
|
+
--snice-terminal-border
|
|
88
|
+
--snice-terminal-scrollbar
|
|
89
|
+
--snice-terminal-scrollbar-thumb
|
|
90
|
+
--snice-terminal-input-color
|
|
91
|
+
--snice-terminal-output-color
|
|
92
|
+
--snice-terminal-error-color
|
|
93
|
+
--snice-terminal-info-color
|
|
94
|
+
--snice-terminal-success-color
|
|
95
|
+
--snice-terminal-warning-color
|
|
96
|
+
--snice-terminal-prompt-color
|
|
97
|
+
--snice-terminal-selection
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Types
|
|
101
|
+
|
|
102
|
+
```typescript
|
|
103
|
+
type TerminalLineType = 'input' | 'output' | 'error' | 'info' | 'success' | 'warning';
|
|
104
|
+
|
|
105
|
+
interface TerminalCommandRequest {
|
|
106
|
+
command: string;
|
|
107
|
+
args: string[];
|
|
108
|
+
cwd?: string;
|
|
109
|
+
history?: string[];
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
interface TerminalCommandResponse {
|
|
113
|
+
output?: string;
|
|
114
|
+
error?: string;
|
|
115
|
+
exitCode?: number;
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## Example
|
|
120
|
+
|
|
121
|
+
```javascript
|
|
122
|
+
const terminal = document.querySelector('snice-terminal');
|
|
123
|
+
|
|
124
|
+
// Listen for commands (event pattern)
|
|
125
|
+
terminal.addEventListener('@snice/terminal-command', (e) => {
|
|
126
|
+
console.log('Command:', e.detail.command, e.detail.args);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
// Handle commands (@respond pattern)
|
|
130
|
+
class TerminalController extends HTMLElement {
|
|
131
|
+
@respond('terminal-command')
|
|
132
|
+
async handleCommand(req) {
|
|
133
|
+
if (req.command === 'echo') {
|
|
134
|
+
return { output: req.args.join(' '), exitCode: 0 };
|
|
135
|
+
}
|
|
136
|
+
if (req.command === 'clear') {
|
|
137
|
+
return { output: '\x1B[CLEAR]' };
|
|
138
|
+
}
|
|
139
|
+
return { error: `Command not found: ${req.command}`, exitCode: 127 };
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Write to terminal
|
|
144
|
+
terminal.writeln('Welcome to the terminal!', 'info');
|
|
145
|
+
terminal.writeln('\x1b[32mGreen text\x1b[0m', 'output');
|
|
146
|
+
terminal.writeError('Error: Something went wrong');
|
|
147
|
+
```
|
|
@@ -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
|
@@ -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`
|