@slopmachine/svelte 0.1.10 → 0.1.13
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/Agents.md +45 -0
- package/README.md +2 -2
- package/package.json +2 -2
- package/src/SlopImage.svelte +85 -46
- package/src/SlopVideo.svelte +296 -0
- package/src/index.ts +8 -1
package/Agents.md
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Svelte SDK Coding Conventions
|
|
2
|
+
|
|
3
|
+
This document outlines the coding conventions, architecture, and patterns used in the `@slopmachine/svelte` SDK package. Future agents and contributors should adhere to these guidelines when making changes.
|
|
4
|
+
|
|
5
|
+
## 1. Svelte 5 Runes
|
|
6
|
+
|
|
7
|
+
The SDK utilizes **Svelte 5** and exclusively relies on **runes** for state management and reactivity.
|
|
8
|
+
|
|
9
|
+
- `$props()`: Used for defining component properties. Ensure default values are handled here, and `...restProps` is utilized to spread unused properties onto underlying DOM elements (e.g., `<img>`).
|
|
10
|
+
- `$state()`: Used for localized component state (e.g., loading states, URLs).
|
|
11
|
+
- `$derived()`: Used for computed values that depend on state or props (e.g., computing an image URL based on options).
|
|
12
|
+
- `$effect()`: Used for side effects, such as tracking prop changes over time, fetching, or debouncing.
|
|
13
|
+
|
|
14
|
+
## 2. TypeScript and Typing
|
|
15
|
+
|
|
16
|
+
- All Svelte components must use `<script lang="ts">`.
|
|
17
|
+
- Props should be strictly typed. Interfaces should extend the base option types provided by the core package. For example, `SlopImageProps` extends `SlopImageOptions` from `@slopmachine/core`.
|
|
18
|
+
- Custom markup passed as props (e.g., a custom loader) should utilize the Svelte 5 `Snippet` type imported from `svelte`.
|
|
19
|
+
|
|
20
|
+
## 3. Dependency Management
|
|
21
|
+
|
|
22
|
+
- **Core SDK Logic**: Do not duplicate core logic (like URL generation). Always import utility functions and base types from the `@slopmachine/core` package.
|
|
23
|
+
- **Peer Dependencies**: The Svelte package lists `svelte` (version `^5.0.0`) as a peer dependency.
|
|
24
|
+
|
|
25
|
+
## 4. Component Structure
|
|
26
|
+
|
|
27
|
+
Components should follow a clear top-to-bottom structure:
|
|
28
|
+
|
|
29
|
+
1. **Block Comments**: A descriptive JSDoc/HTML block comment at the top of the file outlining the `@component`, its description, `@example`, and `@version`.
|
|
30
|
+
2. **Script Tag**: `<script lang="ts">` containing imports, type definitions, runes (`$props`, `$state`, etc.), and local functions.
|
|
31
|
+
3. **Markup**: The Svelte HTML template using Svelte 5 logic blocks (`{#if}`, `{@render snippet()}`).
|
|
32
|
+
4. **Styles**: A `<style>` tag scoped to the component. Use CSS variables with sensible fallbacks (e.g., `var(--muted, #f3f4f6)`) to allow consumer theming while maintaining a good default appearance.
|
|
33
|
+
|
|
34
|
+
## 5. Exporting Patterns
|
|
35
|
+
|
|
36
|
+
- **No Bundling**: The package is published as uncompiled source code to let the consuming project's bundler handle the Svelte compilation.
|
|
37
|
+
- **package.json Setup**:
|
|
38
|
+
- Exposes `"type": "module"`.
|
|
39
|
+
- The `"svelte"`, `"main"`, and `"exports"` fields point directly to `./src/index.ts`.
|
|
40
|
+
- Component exports should be simple and direct via `src/index.ts`.
|
|
41
|
+
|
|
42
|
+
## 6. Error Handling and Loading
|
|
43
|
+
|
|
44
|
+
- Implement clean fallbacks for failing resources (like broken image links). The `SlopImage` component demonstrates using `fetch` to proactively capture error statuses, falling back to an error state or logging.
|
|
45
|
+
- Support default loading indicators (spinners/shimmer effects) alongside customizable slots/snippets (e.g., `loader`) for a better user experience.
|
package/README.md
CHANGED
|
@@ -14,13 +14,13 @@ pnpm add @slopmachine/svelte
|
|
|
14
14
|
|
|
15
15
|
## Demo
|
|
16
16
|
|
|
17
|
-
https://slopmachine-dev.github.io/slopmachine-sdk/svelte/
|
|
17
|
+
https://slopmachine-dev.github.io/slopmachine-sdk/demo-svelte/
|
|
18
18
|
|
|
19
19
|
## Usage
|
|
20
20
|
|
|
21
21
|
```svelte
|
|
22
22
|
<script lang="ts">
|
|
23
|
-
import { SlopImage } from
|
|
23
|
+
import { SlopImage } from "@slopmachine/svelte";
|
|
24
24
|
</script>
|
|
25
25
|
|
|
26
26
|
<div style="width: 400px; height: 400px;">
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@slopmachine/svelte",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "Svelte
|
|
3
|
+
"version": "0.1.13",
|
|
4
|
+
"description": "The official Svelte SDK for Slop Machine",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.ts",
|
|
7
7
|
"svelte": "src/index.ts",
|
package/src/SlopImage.svelte
CHANGED
|
@@ -1,9 +1,46 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
@component
|
|
3
|
+
Renders an image generated by Slop Machine.
|
|
4
|
+
|
|
5
|
+
This component automatically constructs the correct URL for the Slop Machine API,
|
|
6
|
+
displays a loading skeleton or custom loader while fetching, and handles errors.
|
|
7
|
+
|
|
8
|
+
@example
|
|
9
|
+
```svelte
|
|
10
|
+
<SlopImage
|
|
11
|
+
bucketId="my-bucket"
|
|
12
|
+
resultId="some-result"
|
|
13
|
+
aspectRatio="16:9"
|
|
14
|
+
class="rounded-lg"
|
|
15
|
+
/>
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
@version 0.1.13
|
|
19
|
+
-->
|
|
1
20
|
<script lang="ts">
|
|
2
|
-
import {
|
|
21
|
+
import {
|
|
22
|
+
buildImageUrl,
|
|
23
|
+
type SlopImageOptions,
|
|
24
|
+
type ImageAspectRatio,
|
|
25
|
+
} from "@slopmachine/core";
|
|
3
26
|
import type { Snippet } from "svelte";
|
|
4
27
|
|
|
5
|
-
interface
|
|
28
|
+
export interface SlopImageProps extends Omit<
|
|
29
|
+
SlopImageOptions,
|
|
30
|
+
"aspectRatio"
|
|
31
|
+
> {
|
|
32
|
+
/**
|
|
33
|
+
* The aspect ratio of the generated image. Defaults to "1:1".
|
|
34
|
+
*/
|
|
35
|
+
aspectRatio?: ImageAspectRatio;
|
|
36
|
+
/**
|
|
37
|
+
* Additional CSS classes to apply to the wrapper element.
|
|
38
|
+
*/
|
|
6
39
|
class?: string;
|
|
40
|
+
/**
|
|
41
|
+
* Custom Svelte snippet to display while the image is loading.
|
|
42
|
+
* If not provided, a default spinner and shimmer effect will be shown.
|
|
43
|
+
*/
|
|
7
44
|
loader?: Snippet;
|
|
8
45
|
}
|
|
9
46
|
|
|
@@ -18,7 +55,7 @@
|
|
|
18
55
|
class: className = "",
|
|
19
56
|
loader,
|
|
20
57
|
...restProps
|
|
21
|
-
}:
|
|
58
|
+
}: SlopImageProps = $props();
|
|
22
59
|
|
|
23
60
|
let isLoading = $state(true);
|
|
24
61
|
|
|
@@ -96,51 +133,53 @@
|
|
|
96
133
|
}
|
|
97
134
|
</script>
|
|
98
135
|
|
|
99
|
-
<div
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
{#if
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
{:else}
|
|
109
|
-
<div class="loading-overlay">
|
|
110
|
-
<div class="spinner-container">
|
|
111
|
-
<svg
|
|
112
|
-
class="spinner"
|
|
113
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
114
|
-
fill="none"
|
|
115
|
-
viewBox="0 0 24 24"
|
|
116
|
-
>
|
|
117
|
-
<circle
|
|
118
|
-
cx="12"
|
|
119
|
-
cy="12"
|
|
120
|
-
r="10"
|
|
121
|
-
stroke="currentColor"
|
|
122
|
-
stroke-width="4"
|
|
123
|
-
></circle>
|
|
124
|
-
<path
|
|
125
|
-
fill="currentColor"
|
|
126
|
-
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
|
|
127
|
-
></path>
|
|
128
|
-
</svg>
|
|
129
|
-
<span>Loading...</span>
|
|
136
|
+
<div class={className}>
|
|
137
|
+
<div
|
|
138
|
+
class="slop-wrapper"
|
|
139
|
+
style="aspect-ratio: {aspectRatio?.replace(':', '/')};"
|
|
140
|
+
>
|
|
141
|
+
{#if isLoading}
|
|
142
|
+
{#if loader}
|
|
143
|
+
<div class="custom-loader-wrapper">
|
|
144
|
+
{@render loader()}
|
|
130
145
|
</div>
|
|
131
|
-
|
|
132
|
-
|
|
146
|
+
{:else}
|
|
147
|
+
<div class="loading-overlay">
|
|
148
|
+
<div class="spinner-container">
|
|
149
|
+
<svg
|
|
150
|
+
class="spinner"
|
|
151
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
152
|
+
fill="none"
|
|
153
|
+
viewBox="0 0 24 24"
|
|
154
|
+
>
|
|
155
|
+
<circle
|
|
156
|
+
cx="12"
|
|
157
|
+
cy="12"
|
|
158
|
+
r="10"
|
|
159
|
+
stroke="currentColor"
|
|
160
|
+
stroke-width="4"
|
|
161
|
+
></circle>
|
|
162
|
+
<path
|
|
163
|
+
fill="currentColor"
|
|
164
|
+
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
|
|
165
|
+
></path>
|
|
166
|
+
</svg>
|
|
167
|
+
<span>Loading...</span>
|
|
168
|
+
</div>
|
|
169
|
+
</div>
|
|
170
|
+
<div class="shimmer-effect"></div>
|
|
171
|
+
{/if}
|
|
133
172
|
{/if}
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
173
|
+
|
|
174
|
+
<img
|
|
175
|
+
{src}
|
|
176
|
+
{alt}
|
|
177
|
+
onload={handleLoad}
|
|
178
|
+
onerror={handleError}
|
|
179
|
+
class:loaded={!isLoading}
|
|
180
|
+
{...restProps}
|
|
181
|
+
/>
|
|
182
|
+
</div>
|
|
144
183
|
</div>
|
|
145
184
|
|
|
146
185
|
<style>
|
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
@component
|
|
3
|
+
Renders a video generated by Slop Machine.
|
|
4
|
+
|
|
5
|
+
This component automatically constructs the correct URL for the Slop Machine API,
|
|
6
|
+
displays a loading skeleton or custom loader while fetching, and handles errors.
|
|
7
|
+
|
|
8
|
+
@example
|
|
9
|
+
```svelte
|
|
10
|
+
<SlopVideo
|
|
11
|
+
bucketId="my-bucket"
|
|
12
|
+
resultId="some-result"
|
|
13
|
+
aspectRatio="16:9"
|
|
14
|
+
class="rounded-lg"
|
|
15
|
+
autoplay
|
|
16
|
+
loop
|
|
17
|
+
muted
|
|
18
|
+
/>
|
|
19
|
+
```
|
|
20
|
+
-->
|
|
21
|
+
<script lang="ts">
|
|
22
|
+
import {
|
|
23
|
+
buildVideoUrl,
|
|
24
|
+
type SlopVideoOptions,
|
|
25
|
+
type VideoAspectRatio,
|
|
26
|
+
} from "@slopmachine/core";
|
|
27
|
+
import type { Snippet } from "svelte";
|
|
28
|
+
|
|
29
|
+
export interface SlopVideoProps extends Omit<
|
|
30
|
+
SlopVideoOptions,
|
|
31
|
+
"aspectRatio"
|
|
32
|
+
> {
|
|
33
|
+
/**
|
|
34
|
+
* The aspect ratio of the generated video. Defaults to "16:9".
|
|
35
|
+
*/
|
|
36
|
+
aspectRatio?: VideoAspectRatio;
|
|
37
|
+
/**
|
|
38
|
+
* Additional CSS classes to apply to the wrapper element.
|
|
39
|
+
*/
|
|
40
|
+
class?: string;
|
|
41
|
+
/**
|
|
42
|
+
* Custom Svelte snippet to display while the video is loading.
|
|
43
|
+
* If not provided, a default spinner and shimmer effect will be shown.
|
|
44
|
+
*/
|
|
45
|
+
loader?: Snippet;
|
|
46
|
+
autoplay?: boolean;
|
|
47
|
+
loop?: boolean;
|
|
48
|
+
muted?: boolean;
|
|
49
|
+
playsinline?: boolean;
|
|
50
|
+
[key: string]: any;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
let {
|
|
54
|
+
bucketId,
|
|
55
|
+
aspectRatio = "16:9",
|
|
56
|
+
version,
|
|
57
|
+
resultId,
|
|
58
|
+
model,
|
|
59
|
+
variables = {},
|
|
60
|
+
duration,
|
|
61
|
+
baseUrl = undefined,
|
|
62
|
+
class: className = "",
|
|
63
|
+
loader,
|
|
64
|
+
autoplay = true,
|
|
65
|
+
loop = true,
|
|
66
|
+
muted = true,
|
|
67
|
+
playsinline = true,
|
|
68
|
+
...restProps
|
|
69
|
+
}: SlopVideoProps = $props();
|
|
70
|
+
|
|
71
|
+
let isLoading = $state(true);
|
|
72
|
+
|
|
73
|
+
let computedSrc = $derived(
|
|
74
|
+
buildVideoUrl({
|
|
75
|
+
bucketId,
|
|
76
|
+
aspectRatio,
|
|
77
|
+
version,
|
|
78
|
+
resultId,
|
|
79
|
+
variables,
|
|
80
|
+
duration,
|
|
81
|
+
baseUrl,
|
|
82
|
+
model,
|
|
83
|
+
}),
|
|
84
|
+
);
|
|
85
|
+
let src = $state("");
|
|
86
|
+
let prevSrc = $state("");
|
|
87
|
+
|
|
88
|
+
$effect(() => {
|
|
89
|
+
const currentComputedSrc = computedSrc;
|
|
90
|
+
const timeout = setTimeout(() => {
|
|
91
|
+
if (src !== currentComputedSrc) {
|
|
92
|
+
src = currentComputedSrc;
|
|
93
|
+
}
|
|
94
|
+
}, 50);
|
|
95
|
+
|
|
96
|
+
return () => clearTimeout(timeout);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
$effect(() => {
|
|
100
|
+
if (src !== prevSrc) {
|
|
101
|
+
isLoading = true;
|
|
102
|
+
prevSrc = src;
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
$effect(() => {
|
|
107
|
+
if (src) {
|
|
108
|
+
const abortController = new AbortController();
|
|
109
|
+
fetch(src, { method: "HEAD", signal: abortController.signal })
|
|
110
|
+
.then(async (res) => {
|
|
111
|
+
if (!res.ok) {
|
|
112
|
+
try {
|
|
113
|
+
const errRes = await fetch(src, {
|
|
114
|
+
signal: abortController.signal,
|
|
115
|
+
});
|
|
116
|
+
const errJson = await errRes.json();
|
|
117
|
+
console.error("SlopVideo error:", res.status, errJson.error);
|
|
118
|
+
} catch (e: any) {
|
|
119
|
+
if (e.name !== "AbortError") {
|
|
120
|
+
// Failed to fetch or parse error details
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
isLoading = false;
|
|
124
|
+
}
|
|
125
|
+
})
|
|
126
|
+
.catch((err) => {
|
|
127
|
+
if (err.name !== "AbortError") {
|
|
128
|
+
console.error(`Error fetching video from ${src}:`, err);
|
|
129
|
+
isLoading = false;
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
return () => abortController.abort();
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
function handleLoadedData() {
|
|
138
|
+
isLoading = false;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function handleError() {
|
|
142
|
+
isLoading = false;
|
|
143
|
+
}
|
|
144
|
+
</script>
|
|
145
|
+
|
|
146
|
+
<div class={className}>
|
|
147
|
+
<div
|
|
148
|
+
class="slop-wrapper"
|
|
149
|
+
style="aspect-ratio: {aspectRatio?.replace(':', '/')};"
|
|
150
|
+
>
|
|
151
|
+
{#if isLoading}
|
|
152
|
+
{#if loader}
|
|
153
|
+
<div class="custom-loader-wrapper">
|
|
154
|
+
{@render loader()}
|
|
155
|
+
</div>
|
|
156
|
+
{:else}
|
|
157
|
+
<div class="loading-overlay">
|
|
158
|
+
<div class="spinner-container">
|
|
159
|
+
<svg
|
|
160
|
+
class="spinner"
|
|
161
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
162
|
+
fill="none"
|
|
163
|
+
viewBox="0 0 24 24"
|
|
164
|
+
>
|
|
165
|
+
<circle
|
|
166
|
+
cx="12"
|
|
167
|
+
cy="12"
|
|
168
|
+
r="10"
|
|
169
|
+
stroke="currentColor"
|
|
170
|
+
stroke-width="4"
|
|
171
|
+
></circle>
|
|
172
|
+
<path
|
|
173
|
+
fill="currentColor"
|
|
174
|
+
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
|
|
175
|
+
></path>
|
|
176
|
+
</svg>
|
|
177
|
+
<span>Loading...</span>
|
|
178
|
+
</div>
|
|
179
|
+
</div>
|
|
180
|
+
<div class="shimmer-effect"></div>
|
|
181
|
+
{/if}
|
|
182
|
+
{/if}
|
|
183
|
+
|
|
184
|
+
<!-- svelte-ignore a11y_media_has_caption -->
|
|
185
|
+
<video
|
|
186
|
+
{src}
|
|
187
|
+
{autoplay}
|
|
188
|
+
{loop}
|
|
189
|
+
{muted}
|
|
190
|
+
{playsinline}
|
|
191
|
+
onloadeddata={handleLoadedData}
|
|
192
|
+
onerror={handleError}
|
|
193
|
+
class:loaded={!isLoading}
|
|
194
|
+
{...restProps}
|
|
195
|
+
></video>
|
|
196
|
+
</div>
|
|
197
|
+
</div>
|
|
198
|
+
|
|
199
|
+
<style>
|
|
200
|
+
.slop-wrapper {
|
|
201
|
+
position: relative;
|
|
202
|
+
overflow: hidden;
|
|
203
|
+
background-color: var(--muted, #f3f4f6);
|
|
204
|
+
width: 100%;
|
|
205
|
+
height: 100%;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
.loading-overlay,
|
|
209
|
+
.custom-loader-wrapper {
|
|
210
|
+
position: absolute;
|
|
211
|
+
top: 0;
|
|
212
|
+
left: 0;
|
|
213
|
+
right: 0;
|
|
214
|
+
bottom: 0;
|
|
215
|
+
z-index: 10;
|
|
216
|
+
display: flex;
|
|
217
|
+
align-items: center;
|
|
218
|
+
justify-content: center;
|
|
219
|
+
transition: opacity 300ms ease-in-out;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
.loading-overlay {
|
|
223
|
+
background-color: var(--muted, #f3f4f6);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
.spinner-container {
|
|
227
|
+
display: flex;
|
|
228
|
+
flex-direction: column;
|
|
229
|
+
align-items: center;
|
|
230
|
+
gap: 0.5rem;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
.spinner {
|
|
234
|
+
width: 24px;
|
|
235
|
+
height: 24px;
|
|
236
|
+
color: var(--muted-foreground, #6b7280);
|
|
237
|
+
animation: slop-spin 1s linear infinite;
|
|
238
|
+
}
|
|
239
|
+
.spinner circle {
|
|
240
|
+
opacity: 0.25;
|
|
241
|
+
}
|
|
242
|
+
.spinner path {
|
|
243
|
+
opacity: 0.75;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
.spinner-container span {
|
|
247
|
+
font-size: 0.75rem;
|
|
248
|
+
color: var(--muted-foreground, #6b7280);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
.shimmer-effect {
|
|
252
|
+
position: absolute;
|
|
253
|
+
top: 0;
|
|
254
|
+
left: 0;
|
|
255
|
+
right: 0;
|
|
256
|
+
bottom: 0;
|
|
257
|
+
background: linear-gradient(
|
|
258
|
+
90deg,
|
|
259
|
+
var(--muted, #f3f4f6) 0%,
|
|
260
|
+
var(--muted-foreground, #e5e7eb) 50%,
|
|
261
|
+
var(--muted, #f3f4f6) 100%
|
|
262
|
+
);
|
|
263
|
+
background-size: 200% 100%;
|
|
264
|
+
animation: slop-shimmer 2s ease-in-out infinite;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
video {
|
|
268
|
+
height: 100%;
|
|
269
|
+
width: 100%;
|
|
270
|
+
object-fit: cover;
|
|
271
|
+
transition: opacity 500ms;
|
|
272
|
+
opacity: 0;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
video.loaded {
|
|
276
|
+
opacity: 1;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
@keyframes slop-shimmer {
|
|
280
|
+
0% {
|
|
281
|
+
background-position: 200% 0;
|
|
282
|
+
}
|
|
283
|
+
100% {
|
|
284
|
+
background-position: -200% 0;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
@keyframes slop-spin {
|
|
289
|
+
from {
|
|
290
|
+
transform: rotate(0deg);
|
|
291
|
+
}
|
|
292
|
+
to {
|
|
293
|
+
transform: rotate(360deg);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
</style>
|
package/src/index.ts
CHANGED
|
@@ -1 +1,8 @@
|
|
|
1
|
-
export { default as SlopImage } from "./SlopImage.svelte";
|
|
1
|
+
export { default as SlopImage, type SlopImageProps } from "./SlopImage.svelte";
|
|
2
|
+
export { default as SlopVideo, type SlopVideoProps } from "./SlopVideo.svelte";
|
|
3
|
+
export type {
|
|
4
|
+
ImageAspectRatio,
|
|
5
|
+
VideoAspectRatio,
|
|
6
|
+
SlopImageOptions,
|
|
7
|
+
SlopVideoOptions,
|
|
8
|
+
} from "@slopmachine/core";
|