plotlink-ows 1.2.95 → 1.2.96
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/app/lib/cuts.ts +135 -18
- package/app/lib/lettering-status.ts +64 -6
- package/app/web/components/CutListPanel.tsx +1108 -436
- package/app/web/components/FinishEpisodePanel.tsx +57 -46
- package/app/web/components/LetteringEditor.tsx +845 -385
- package/app/web/components/PreviewPanel.tsx +1459 -845
- package/app/web/components/StoriesPage.tsx +981 -506
- package/app/web/dist/assets/{export-cut-che5mMWc.js → export-cut-BqZI0-Rv.js} +1 -1
- package/app/web/dist/assets/index-C43toXVm.js +141 -0
- package/app/web/dist/index.html +1 -1
- package/package.json +1 -1
- package/app/web/dist/assets/index-Dc2TQ3Ij.js +0 -143
|
@@ -85,67 +85,78 @@ export function FinishEpisodePanel({
|
|
|
85
85
|
? "Episode ready to publish"
|
|
86
86
|
: "Finish episode";
|
|
87
87
|
|
|
88
|
+
const outstandingCount = steps.filter((s) => s.status !== "done").length;
|
|
89
|
+
const issuesCount = groups.reduce((sum, g) => sum + g.lines.length, 0);
|
|
90
|
+
|
|
88
91
|
return (
|
|
89
92
|
<div
|
|
90
|
-
className="px-3 py-
|
|
93
|
+
className="px-3 py-1.5 border-b border-border bg-surface/50 flex-shrink-0"
|
|
91
94
|
data-testid="finish-episode-panel"
|
|
92
95
|
>
|
|
93
|
-
<div className="flex items-center
|
|
96
|
+
<div className="flex flex-wrap items-center gap-2">
|
|
94
97
|
<span className="text-[11px] font-medium text-foreground">Finish episode</span>
|
|
95
98
|
{checklist.nextStep && (
|
|
96
|
-
<span className="text-[10px] text-muted truncate" data-testid="finish-next-step">
|
|
99
|
+
<span className="min-w-0 flex-1 text-[10px] text-muted truncate" data-testid="finish-next-step">
|
|
97
100
|
Next: {checklist.nextStep}
|
|
98
101
|
</span>
|
|
99
102
|
)}
|
|
103
|
+
<button
|
|
104
|
+
onClick={onFinish}
|
|
105
|
+
disabled={finishing || !canFinish}
|
|
106
|
+
data-testid="finish-episode-btn"
|
|
107
|
+
title="Upload the exported final panels, then prepare the episode for publishing — picks up where it left off"
|
|
108
|
+
className="px-2.5 py-0.5 text-[11px] border border-accent/40 text-accent rounded hover:bg-accent/5 disabled:opacity-50"
|
|
109
|
+
>
|
|
110
|
+
{buttonLabel}
|
|
111
|
+
</button>
|
|
100
112
|
</div>
|
|
101
113
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
data-testid={`finish-step-${s.key}`}
|
|
108
|
-
data-status={s.status}
|
|
109
|
-
className={`flex items-center gap-1 rounded border px-1.5 py-0.5 text-[10px] ${
|
|
110
|
-
s.status === "current"
|
|
111
|
-
? "border-accent/40 bg-accent/10 text-accent"
|
|
112
|
-
: s.status === "done"
|
|
113
|
-
? "border-border bg-background/70 text-foreground"
|
|
114
|
-
: "border-border/70 bg-background/40 text-muted"
|
|
115
|
-
}`}
|
|
116
|
-
>
|
|
117
|
-
<span aria-hidden>{STATUS_MARK[s.status]}</span>
|
|
118
|
-
<span>{s.label}</span>
|
|
119
|
-
{s.detail && <span className="text-muted">· {s.detail}</span>}
|
|
120
|
-
</li>
|
|
121
|
-
))}
|
|
122
|
-
</ol>
|
|
114
|
+
<details className="mt-1" data-testid="finish-episode-details">
|
|
115
|
+
<summary className="cursor-pointer select-none text-[10px] text-muted hover:text-foreground">
|
|
116
|
+
{outstandingCount === 0 ? "Progress details" : `${outstandingCount} step${outstandingCount === 1 ? "" : "s"} left`}
|
|
117
|
+
{issuesCount > 0 ? ` · ${issuesCount} blocker${issuesCount === 1 ? "" : "s"}` : ""}
|
|
118
|
+
</summary>
|
|
123
119
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
120
|
+
<div className="mt-1.5 space-y-1.5">
|
|
121
|
+
{/* Writer-language step status — the exact webtoon production sequence. */}
|
|
122
|
+
<ol className="flex flex-wrap gap-1.5">
|
|
123
|
+
{steps.map((s) => (
|
|
124
|
+
<li
|
|
125
|
+
key={s.key}
|
|
126
|
+
data-testid={`finish-step-${s.key}`}
|
|
127
|
+
data-status={s.status}
|
|
128
|
+
className={`flex items-center gap-1 rounded border px-1.5 py-0.5 text-[10px] ${
|
|
129
|
+
s.status === "current"
|
|
130
|
+
? "border-accent/40 bg-accent/10 text-accent"
|
|
131
|
+
: s.status === "done"
|
|
132
|
+
? "border-border bg-background/70 text-foreground"
|
|
133
|
+
: "border-border/70 bg-background/40 text-muted"
|
|
134
|
+
}`}
|
|
135
|
+
>
|
|
136
|
+
<span aria-hidden>{STATUS_MARK[s.status]}</span>
|
|
137
|
+
<span>{s.label}</span>
|
|
138
|
+
{s.detail && <span className="text-muted">· {s.detail}</span>}
|
|
139
|
+
</li>
|
|
140
|
+
))}
|
|
141
|
+
</ol>
|
|
133
142
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
143
|
+
{/* Blockers grouped by the step that fixes them, not a flat red list. */}
|
|
144
|
+
{groups.length > 0 && (
|
|
145
|
+
<div className="space-y-1.5" data-testid="finish-issues">
|
|
146
|
+
{groups.map((g) => (
|
|
147
|
+
<div key={g.key} data-testid={`finish-issue-group-${g.key}`} className="text-[10px]">
|
|
148
|
+
<p className="font-medium text-amber-700">{g.title}</p>
|
|
149
|
+
<ul className="ml-3 list-disc text-muted">
|
|
150
|
+
{g.lines.map((line, i) => (
|
|
151
|
+
<li key={i}>{line}</li>
|
|
152
|
+
))}
|
|
153
|
+
</ul>
|
|
154
|
+
</div>
|
|
155
|
+
))}
|
|
145
156
|
</div>
|
|
146
|
-
)
|
|
157
|
+
)}
|
|
147
158
|
</div>
|
|
148
|
-
|
|
159
|
+
</details>
|
|
149
160
|
</div>
|
|
150
161
|
);
|
|
151
162
|
}
|