@slopmachine/react 0.1.13 → 0.1.15
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/dist/index.d.mts +35 -3
- package/dist/index.d.ts +35 -3
- package/dist/index.js +233 -4
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +232 -4
- package/dist/index.mjs.map +1 -1
- package/package.json +6 -2
- package/src/components/SlopImage.tsx +13 -2
- package/src/components/SlopText.tsx +237 -0
- package/src/components/SlopVideo.tsx +5 -0
- package/src/index.ts +2 -0
- /package/{Agents.md → AGENTS.md} +0 -0
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
import React, { useEffect, useState } from "react";
|
|
2
|
+
import { buildTextUrl, type SlopTextOptions } from "@slopmachine/core";
|
|
3
|
+
import { marked } from "marked";
|
|
4
|
+
|
|
5
|
+
export interface SlopTextProps
|
|
6
|
+
extends
|
|
7
|
+
Omit<React.HTMLAttributes<HTMLDivElement>, "children">,
|
|
8
|
+
SlopTextOptions {
|
|
9
|
+
/**
|
|
10
|
+
* Optional loading component to show while the text is being generated
|
|
11
|
+
*/
|
|
12
|
+
fallback?: React.ReactNode;
|
|
13
|
+
/**
|
|
14
|
+
* Optional error component to show if the text generation fails
|
|
15
|
+
*/
|
|
16
|
+
errorFallback?: React.ReactNode;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Renders text generated by Slop Machine.
|
|
21
|
+
*
|
|
22
|
+
* This component automatically constructs the correct URL for the Slop Machine API,
|
|
23
|
+
* fetches the generated text, and displays it with optional loading and error states.
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```tsx
|
|
27
|
+
* <SlopText
|
|
28
|
+
* bucketId="my-text-bucket"
|
|
29
|
+
* fallback={<div>Loading story...</div>}
|
|
30
|
+
* errorFallback={<div>Failed to load story</div>}
|
|
31
|
+
* className="text-lg text-gray-800"
|
|
32
|
+
* />
|
|
33
|
+
* ```
|
|
34
|
+
*
|
|
35
|
+
* @version 0.1.15
|
|
36
|
+
*/
|
|
37
|
+
export const SlopText = React.forwardRef<HTMLDivElement, SlopTextProps>(
|
|
38
|
+
(
|
|
39
|
+
{
|
|
40
|
+
bucketId,
|
|
41
|
+
version,
|
|
42
|
+
resultId,
|
|
43
|
+
model,
|
|
44
|
+
variables,
|
|
45
|
+
baseUrl,
|
|
46
|
+
fallback,
|
|
47
|
+
errorFallback,
|
|
48
|
+
...props
|
|
49
|
+
},
|
|
50
|
+
ref,
|
|
51
|
+
) => {
|
|
52
|
+
const [text, setText] = useState<string | null>(null);
|
|
53
|
+
const [loading, setLoading] = useState(true);
|
|
54
|
+
const [error, setError] = useState<Error | null>(null);
|
|
55
|
+
|
|
56
|
+
useEffect(() => {
|
|
57
|
+
let isMounted = true;
|
|
58
|
+
|
|
59
|
+
const fetchText = async () => {
|
|
60
|
+
try {
|
|
61
|
+
setLoading(true);
|
|
62
|
+
setError(null);
|
|
63
|
+
|
|
64
|
+
const url = buildTextUrl({
|
|
65
|
+
bucketId,
|
|
66
|
+
version,
|
|
67
|
+
resultId,
|
|
68
|
+
model,
|
|
69
|
+
variables,
|
|
70
|
+
baseUrl,
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
// Fetch the URL to get the content, following redirects automatically
|
|
74
|
+
const response = await fetch(url);
|
|
75
|
+
if (!response.ok) {
|
|
76
|
+
throw new Error(`Failed to fetch text: ${response.statusText}`);
|
|
77
|
+
}
|
|
78
|
+
const content = await response.text();
|
|
79
|
+
if (isMounted) {
|
|
80
|
+
setText(content);
|
|
81
|
+
}
|
|
82
|
+
} catch (err) {
|
|
83
|
+
if (isMounted) {
|
|
84
|
+
setError(
|
|
85
|
+
err instanceof Error ? err : new Error("Failed to generate text"),
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
} finally {
|
|
89
|
+
if (isMounted) {
|
|
90
|
+
setLoading(false);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
fetchText();
|
|
96
|
+
|
|
97
|
+
return () => {
|
|
98
|
+
isMounted = false;
|
|
99
|
+
};
|
|
100
|
+
}, [
|
|
101
|
+
bucketId,
|
|
102
|
+
version,
|
|
103
|
+
resultId,
|
|
104
|
+
model,
|
|
105
|
+
JSON.stringify(variables),
|
|
106
|
+
baseUrl,
|
|
107
|
+
]);
|
|
108
|
+
|
|
109
|
+
if (error && errorFallback) {
|
|
110
|
+
return <>{errorFallback}</>;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (loading) {
|
|
114
|
+
if (fallback) {
|
|
115
|
+
return <>{fallback}</>;
|
|
116
|
+
}
|
|
117
|
+
return (
|
|
118
|
+
<div className={props.className} style={props.style}>
|
|
119
|
+
<style>{`
|
|
120
|
+
@keyframes slop-shimmer {
|
|
121
|
+
0% { background-position: 200% 0; }
|
|
122
|
+
100% { background-position: -200% 0; }
|
|
123
|
+
}
|
|
124
|
+
@keyframes slop-spin {
|
|
125
|
+
from { transform: rotate(0deg); }
|
|
126
|
+
to { transform: rotate(360deg); }
|
|
127
|
+
}
|
|
128
|
+
`}</style>
|
|
129
|
+
<div
|
|
130
|
+
className="slop-wrapper relative overflow-hidden w-full"
|
|
131
|
+
style={{
|
|
132
|
+
position: "relative",
|
|
133
|
+
overflow: "hidden",
|
|
134
|
+
width: "100%",
|
|
135
|
+
minHeight: "100px",
|
|
136
|
+
backgroundColor: "var(--muted, #f3f4f6)",
|
|
137
|
+
borderRadius: "0.375rem",
|
|
138
|
+
}}
|
|
139
|
+
>
|
|
140
|
+
{/* Loading overlay */}
|
|
141
|
+
<div
|
|
142
|
+
className="absolute inset-0 z-10 flex items-center justify-center bg-muted transition-opacity duration-300"
|
|
143
|
+
style={{
|
|
144
|
+
position: "absolute",
|
|
145
|
+
top: 0,
|
|
146
|
+
left: 0,
|
|
147
|
+
right: 0,
|
|
148
|
+
bottom: 0,
|
|
149
|
+
zIndex: 10,
|
|
150
|
+
display: "flex",
|
|
151
|
+
alignItems: "center",
|
|
152
|
+
justifyContent: "center",
|
|
153
|
+
backgroundColor: "var(--muted, #f3f4f6)",
|
|
154
|
+
}}
|
|
155
|
+
>
|
|
156
|
+
<div
|
|
157
|
+
className="flex flex-col items-center gap-2"
|
|
158
|
+
style={{
|
|
159
|
+
display: "flex",
|
|
160
|
+
flexDirection: "column",
|
|
161
|
+
alignItems: "center",
|
|
162
|
+
gap: "0.5rem",
|
|
163
|
+
}}
|
|
164
|
+
>
|
|
165
|
+
<svg
|
|
166
|
+
className="spinner size-6 text-muted-foreground"
|
|
167
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
168
|
+
fill="none"
|
|
169
|
+
viewBox="0 0 24 24"
|
|
170
|
+
style={{
|
|
171
|
+
width: "24px",
|
|
172
|
+
height: "24px",
|
|
173
|
+
color: "var(--muted-foreground, #6b7280)",
|
|
174
|
+
animation: "slop-spin 1s linear infinite",
|
|
175
|
+
}}
|
|
176
|
+
>
|
|
177
|
+
<circle
|
|
178
|
+
className="opacity-25"
|
|
179
|
+
cx="12"
|
|
180
|
+
cy="12"
|
|
181
|
+
r="10"
|
|
182
|
+
stroke="currentColor"
|
|
183
|
+
strokeWidth="4"
|
|
184
|
+
style={{ opacity: 0.25 }}
|
|
185
|
+
/>
|
|
186
|
+
<path
|
|
187
|
+
className="opacity-75"
|
|
188
|
+
fill="currentColor"
|
|
189
|
+
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"
|
|
190
|
+
style={{ opacity: 0.75 }}
|
|
191
|
+
/>
|
|
192
|
+
</svg>
|
|
193
|
+
<span
|
|
194
|
+
className="text-xs text-muted-foreground"
|
|
195
|
+
style={{
|
|
196
|
+
fontSize: "0.75rem",
|
|
197
|
+
color: "var(--muted-foreground, #6b7280)",
|
|
198
|
+
}}
|
|
199
|
+
>
|
|
200
|
+
Loading...
|
|
201
|
+
</span>
|
|
202
|
+
</div>
|
|
203
|
+
</div>
|
|
204
|
+
|
|
205
|
+
{/* Shimmer effect underneath */}
|
|
206
|
+
<div
|
|
207
|
+
className="shimmer-effect absolute inset-0 bg-gradient-to-r from-muted via-muted/50 to-muted"
|
|
208
|
+
style={{
|
|
209
|
+
position: "absolute",
|
|
210
|
+
top: 0,
|
|
211
|
+
left: 0,
|
|
212
|
+
right: 0,
|
|
213
|
+
bottom: 0,
|
|
214
|
+
background:
|
|
215
|
+
"linear-gradient(90deg, var(--muted, #f3f4f6) 0%, var(--muted-foreground, #e5e7eb) 50%, var(--muted, #f3f4f6) 100%)",
|
|
216
|
+
backgroundSize: "200% 100%",
|
|
217
|
+
animation: "slop-shimmer 2s ease-in-out infinite",
|
|
218
|
+
}}
|
|
219
|
+
/>
|
|
220
|
+
</div>
|
|
221
|
+
</div>
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
return (
|
|
226
|
+
<div
|
|
227
|
+
ref={ref}
|
|
228
|
+
{...props}
|
|
229
|
+
dangerouslySetInnerHTML={{
|
|
230
|
+
__html: text ? (marked.parse(text) as string) : "",
|
|
231
|
+
}}
|
|
232
|
+
/>
|
|
233
|
+
);
|
|
234
|
+
},
|
|
235
|
+
);
|
|
236
|
+
|
|
237
|
+
SlopText.displayName = "SlopText";
|
|
@@ -46,6 +46,8 @@ export interface SlopVideoProps
|
|
|
46
46
|
* muted
|
|
47
47
|
* />
|
|
48
48
|
* ```
|
|
49
|
+
*
|
|
50
|
+
* @version 0.1.15
|
|
49
51
|
*/
|
|
50
52
|
export const SlopVideo: React.FC<SlopVideoProps> = ({
|
|
51
53
|
bucketId,
|
|
@@ -54,6 +56,7 @@ export const SlopVideo: React.FC<SlopVideoProps> = ({
|
|
|
54
56
|
version,
|
|
55
57
|
resultId,
|
|
56
58
|
model,
|
|
59
|
+
quality = "fast",
|
|
57
60
|
variables = {},
|
|
58
61
|
duration,
|
|
59
62
|
baseUrl,
|
|
@@ -76,6 +79,7 @@ export const SlopVideo: React.FC<SlopVideoProps> = ({
|
|
|
76
79
|
duration,
|
|
77
80
|
baseUrl,
|
|
78
81
|
model,
|
|
82
|
+
quality,
|
|
79
83
|
}),
|
|
80
84
|
[
|
|
81
85
|
bucketId,
|
|
@@ -86,6 +90,7 @@ export const SlopVideo: React.FC<SlopVideoProps> = ({
|
|
|
86
90
|
duration,
|
|
87
91
|
baseUrl,
|
|
88
92
|
model,
|
|
93
|
+
quality,
|
|
89
94
|
],
|
|
90
95
|
);
|
|
91
96
|
|
package/src/index.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export { SlopImage, type SlopImageProps } from "./components/SlopImage";
|
|
2
2
|
export { SlopVideo, type SlopVideoProps } from "./components/SlopVideo";
|
|
3
|
+
export { SlopText, type SlopTextProps } from "./components/SlopText";
|
|
4
|
+
|
|
3
5
|
export type {
|
|
4
6
|
ImageAspectRatio,
|
|
5
7
|
VideoAspectRatio,
|
/package/{Agents.md → AGENTS.md}
RENAMED
|
File without changes
|