plotlink-ows 1.0.32 → 1.0.33
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.
|
@@ -42,8 +42,8 @@ For login, the passphrase is hashed with HMAC-SHA256 and compared against the st
|
|
|
42
42
|
| \`/api/publish/preflight\` | GET | Check wallet balance, Filebase config |
|
|
43
43
|
| \`/api/publish/file\` | POST | Publish story on-chain (SSE stream of progress events) |
|
|
44
44
|
| \`/api/publish/retry-index\` | POST | Retry indexing for a published file |
|
|
45
|
-
| \`/api/publish/upload-cover\` | POST | Upload cover image — FormData \`file\` field, **WebP or JPEG only**, max
|
|
46
|
-
| \`/api/publish/upload-plot-image\` | POST | Upload plot illustration — FormData \`file\` field, **WebP or JPEG only**, max
|
|
45
|
+
| \`/api/publish/upload-cover\` | POST | Upload cover image — FormData \`file\` field, **WebP or JPEG only**, max 1MB → returns \`{ cid }\` |
|
|
46
|
+
| \`/api/publish/upload-plot-image\` | POST | Upload plot illustration — FormData \`file\` field, **WebP or JPEG only**, max 1MB → returns \`{ cid, url }\` |
|
|
47
47
|
| \`/api/publish/update-storyline\` | POST | Update storyline metadata (coverCid, genre, language, isNsfw) |
|
|
48
48
|
|
|
49
49
|
**Publish flow:** Upload to IPFS → estimate gas → sign with OWS wallet → broadcast → confirm → index on plotlink.xyz (8s delay + 10 retries × 30s). Genesis files call \`createStoryline\`, plot files (\`plot-*.md\`) call \`chainPlot\`. Content limit: 10K chars.
|
package/app/routes/publish.ts
CHANGED
|
@@ -212,9 +212,9 @@ publish.post("/upload-cover", async (c) => {
|
|
|
212
212
|
return c.json({ error: "No image file provided" }, 400);
|
|
213
213
|
}
|
|
214
214
|
|
|
215
|
-
// Validate file size (
|
|
216
|
-
if (file.size >
|
|
217
|
-
return c.json({ error: "Image exceeds
|
|
215
|
+
// Validate file size (1MB max)
|
|
216
|
+
if (file.size > 1024 * 1024) {
|
|
217
|
+
return c.json({ error: "Image exceeds 1MB limit" }, 400);
|
|
218
218
|
}
|
|
219
219
|
|
|
220
220
|
// Validate file type — only WebP and JPEG accepted by the plotlink server
|
|
@@ -247,9 +247,9 @@ publish.post("/upload-plot-image", async (c) => {
|
|
|
247
247
|
return c.json({ error: "No image file provided" }, 400);
|
|
248
248
|
}
|
|
249
249
|
|
|
250
|
-
// Validate file size (
|
|
251
|
-
if (file.size >
|
|
252
|
-
return c.json({ error: "Image exceeds
|
|
250
|
+
// Validate file size (1MB max)
|
|
251
|
+
if (file.size > 1024 * 1024) {
|
|
252
|
+
return c.json({ error: "Image exceeds 1MB limit" }, 400);
|
|
253
253
|
}
|
|
254
254
|
|
|
255
255
|
// Validate file type — only WebP and JPEG accepted by the plotlink server
|
|
@@ -187,8 +187,8 @@ export function PreviewPanel({ storyName, fileName, authFetch, onPublish, publis
|
|
|
187
187
|
const handleCoverSelect = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
|
|
188
188
|
const file = e.target.files?.[0];
|
|
189
189
|
if (!file) return;
|
|
190
|
-
if (file.size >
|
|
191
|
-
setEditError("Image exceeds
|
|
190
|
+
if (file.size > 1024 * 1024) {
|
|
191
|
+
setEditError("Image exceeds 1MB limit");
|
|
192
192
|
return;
|
|
193
193
|
}
|
|
194
194
|
if (!file.type.startsWith("image/")) {
|
|
@@ -202,8 +202,8 @@ export function PreviewPanel({ storyName, fileName, authFetch, onPublish, publis
|
|
|
202
202
|
|
|
203
203
|
// Handle illustration image upload from File object
|
|
204
204
|
const uploadIllustration = useCallback(async (file: File) => {
|
|
205
|
-
if (file.size >
|
|
206
|
-
setIllustrationError("Image exceeds
|
|
205
|
+
if (file.size > 1024 * 1024) {
|
|
206
|
+
setIllustrationError("Image exceeds 1MB limit");
|
|
207
207
|
return;
|
|
208
208
|
}
|
|
209
209
|
const allowedTypes = ["image/webp", "image/jpeg"];
|
|
@@ -500,70 +500,6 @@ export function PreviewPanel({ storyName, fileName, authFetch, onPublish, publis
|
|
|
500
500
|
{saving ? "Saving..." : "Save"}
|
|
501
501
|
</button>
|
|
502
502
|
</div>
|
|
503
|
-
{/* Inline illustration upload for plot files */}
|
|
504
|
-
{isPlot && (
|
|
505
|
-
<div className="px-3 py-1.5 border-t border-border">
|
|
506
|
-
<label className="flex items-center gap-1.5 text-xs text-muted cursor-pointer">
|
|
507
|
-
<input
|
|
508
|
-
type="checkbox"
|
|
509
|
-
checked={showIllustrations}
|
|
510
|
-
onChange={(e) => setShowIllustrations(e.target.checked)}
|
|
511
|
-
className="rounded border-border"
|
|
512
|
-
/>
|
|
513
|
-
Add illustrations in the plot
|
|
514
|
-
</label>
|
|
515
|
-
{showIllustrations && (
|
|
516
|
-
<div className="mt-2 flex flex-col gap-2">
|
|
517
|
-
<div
|
|
518
|
-
className="border-2 border-dashed border-border rounded p-3 flex flex-col items-center gap-1.5 cursor-pointer hover:border-accent transition-colors"
|
|
519
|
-
onClick={() => illustrationInputRef.current?.click()}
|
|
520
|
-
onDragOver={(e) => { e.preventDefault(); e.stopPropagation(); }}
|
|
521
|
-
onDrop={(e) => {
|
|
522
|
-
e.preventDefault();
|
|
523
|
-
e.stopPropagation();
|
|
524
|
-
const file = e.dataTransfer.files?.[0];
|
|
525
|
-
if (file) uploadIllustration(file);
|
|
526
|
-
}}
|
|
527
|
-
>
|
|
528
|
-
<input
|
|
529
|
-
ref={illustrationInputRef}
|
|
530
|
-
type="file"
|
|
531
|
-
accept="image/webp,image/jpeg"
|
|
532
|
-
onChange={handleIllustrationInput}
|
|
533
|
-
className="hidden"
|
|
534
|
-
/>
|
|
535
|
-
<span className="text-xs text-muted">
|
|
536
|
-
{illustrationUploading ? "Uploading..." : "Drop image here or click to browse"}
|
|
537
|
-
</span>
|
|
538
|
-
<span className="text-xs text-muted">WebP/JPEG, max 500KB</span>
|
|
539
|
-
</div>
|
|
540
|
-
{illustrationError && (
|
|
541
|
-
<span className="text-error text-xs">{illustrationError}</span>
|
|
542
|
-
)}
|
|
543
|
-
{uploadedImages.map((img, i) => (
|
|
544
|
-
<div key={img.cid} className="border border-border rounded p-2 flex flex-col gap-1 bg-surface">
|
|
545
|
-
<span className="text-xs text-green-700">Image uploaded! Copy the markdown below and paste it where you want the illustration to appear in your plot:</span>
|
|
546
|
-
<div className="flex items-center gap-1.5">
|
|
547
|
-
<code className="flex-1 text-xs bg-background px-2 py-1 rounded font-mono break-all">
|
|
548
|
-

|
|
549
|
-
</code>
|
|
550
|
-
<button
|
|
551
|
-
onClick={() => {
|
|
552
|
-
navigator.clipboard.writeText(``);
|
|
553
|
-
setCopiedIndex(i);
|
|
554
|
-
setTimeout(() => setCopiedIndex(null), 2000);
|
|
555
|
-
}}
|
|
556
|
-
className="px-2 py-1 text-xs border border-border rounded hover:bg-surface shrink-0"
|
|
557
|
-
>
|
|
558
|
-
{copiedIndex === i ? "Copied!" : "Copy"}
|
|
559
|
-
</button>
|
|
560
|
-
</div>
|
|
561
|
-
</div>
|
|
562
|
-
))}
|
|
563
|
-
</div>
|
|
564
|
-
)}
|
|
565
|
-
</div>
|
|
566
|
-
)}
|
|
567
503
|
</div>
|
|
568
504
|
)}
|
|
569
505
|
|
|
@@ -717,7 +653,7 @@ export function PreviewPanel({ storyName, fileName, authFetch, onPublish, publis
|
|
|
717
653
|
onChange={handleCoverSelect}
|
|
718
654
|
className="text-xs"
|
|
719
655
|
/>
|
|
720
|
-
<span className="text-xs text-muted">WebP/JPEG, max
|
|
656
|
+
<span className="text-xs text-muted">WebP/JPEG, max 1MB, 600x900px recommended</span>
|
|
721
657
|
</div>
|
|
722
658
|
</div>
|
|
723
659
|
</div>
|
|
@@ -769,6 +705,70 @@ export function PreviewPanel({ storyName, fileName, authFetch, onPublish, publis
|
|
|
769
705
|
</div>
|
|
770
706
|
) : (
|
|
771
707
|
<div className="flex flex-col gap-2">
|
|
708
|
+
{/* Inline illustration upload for plot files (Preview tab only) */}
|
|
709
|
+
{isPlot && activeTab === "preview" && (
|
|
710
|
+
<div>
|
|
711
|
+
<label className="flex items-center gap-1.5 text-xs text-muted cursor-pointer">
|
|
712
|
+
<input
|
|
713
|
+
type="checkbox"
|
|
714
|
+
checked={showIllustrations}
|
|
715
|
+
onChange={(e) => setShowIllustrations(e.target.checked)}
|
|
716
|
+
className="rounded border-border"
|
|
717
|
+
/>
|
|
718
|
+
Add illustrations in the plot
|
|
719
|
+
</label>
|
|
720
|
+
{showIllustrations && (
|
|
721
|
+
<div className="mt-2 flex flex-col gap-2">
|
|
722
|
+
<div
|
|
723
|
+
className="border-2 border-dashed border-border rounded p-3 flex flex-col items-center gap-1.5 cursor-pointer hover:border-accent transition-colors"
|
|
724
|
+
onClick={() => illustrationInputRef.current?.click()}
|
|
725
|
+
onDragOver={(e) => { e.preventDefault(); e.stopPropagation(); }}
|
|
726
|
+
onDrop={(e) => {
|
|
727
|
+
e.preventDefault();
|
|
728
|
+
e.stopPropagation();
|
|
729
|
+
const file = e.dataTransfer.files?.[0];
|
|
730
|
+
if (file) uploadIllustration(file);
|
|
731
|
+
}}
|
|
732
|
+
>
|
|
733
|
+
<input
|
|
734
|
+
ref={illustrationInputRef}
|
|
735
|
+
type="file"
|
|
736
|
+
accept="image/webp,image/jpeg"
|
|
737
|
+
onChange={handleIllustrationInput}
|
|
738
|
+
className="hidden"
|
|
739
|
+
/>
|
|
740
|
+
<span className="text-xs text-muted">
|
|
741
|
+
{illustrationUploading ? "Uploading..." : "Drop image here or click to browse"}
|
|
742
|
+
</span>
|
|
743
|
+
<span className="text-xs text-muted">WebP/JPEG, max 1MB</span>
|
|
744
|
+
</div>
|
|
745
|
+
{illustrationError && (
|
|
746
|
+
<span className="text-error text-xs">{illustrationError}</span>
|
|
747
|
+
)}
|
|
748
|
+
{uploadedImages.map((img, i) => (
|
|
749
|
+
<div key={img.cid} className="border border-border rounded p-2 flex flex-col gap-1 bg-surface">
|
|
750
|
+
<span className="text-xs text-green-700">Image uploaded! Copy the markdown below and paste it where you want the illustration to appear in your plot:</span>
|
|
751
|
+
<div className="flex items-center gap-1.5">
|
|
752
|
+
<code className="flex-1 text-xs bg-background px-2 py-1 rounded font-mono break-all">
|
|
753
|
+

|
|
754
|
+
</code>
|
|
755
|
+
<button
|
|
756
|
+
onClick={() => {
|
|
757
|
+
navigator.clipboard.writeText(``);
|
|
758
|
+
setCopiedIndex(i);
|
|
759
|
+
setTimeout(() => setCopiedIndex(null), 2000);
|
|
760
|
+
}}
|
|
761
|
+
className="px-2 py-1 text-xs border border-border rounded hover:bg-surface shrink-0"
|
|
762
|
+
>
|
|
763
|
+
{copiedIndex === i ? "Copied!" : "Copy"}
|
|
764
|
+
</button>
|
|
765
|
+
</div>
|
|
766
|
+
</div>
|
|
767
|
+
))}
|
|
768
|
+
</div>
|
|
769
|
+
)}
|
|
770
|
+
</div>
|
|
771
|
+
)}
|
|
772
772
|
<div className="flex items-center gap-2">
|
|
773
773
|
{(isGenesis) && (
|
|
774
774
|
<>
|