mirror of
https://github.com/nexu-io/open-design.git
synced 2026-09-20 06:15:06 +08:00
feat(prompt-templates): add dance storyboard and ancient-china MMO HUD templates (#187)
* feat(prompt-templates): add dance storyboard and ancient-china MMO HUD templates - social-media-post-sensational-girl-dance-storyboard-8-shots: 8-shot storyboard prompt set with shared global style tokens, negative prompt, and character lock, tuned for GPT-Image-2. Produces a continuous dance choreography as a coherent 8-frame sequence. - game-ui-ancient-china-open-world-mmo-hud: HUD mockup for a Black Myth: Wukong style ancient-China open-world MMO, centered on a female swordswoman protagonist. Covers character panel, minimap with bagua frame, skill hotbar, quest tracker, chat window, and world-space nameplates with Chinese typography rules. - scripts/import-prompt-templates.mjs: preserve hand-authored templates on re-run by keeping any JSON whose source.repo is not the upstream CC-BY corpus. Previously clearDir wiped the whole directory, which would delete first-party curated prompts on the next import. Both templates carry source attribution to nexu-io/open-design under Apache-2.0. Categories reuse the existing 'Social Media Post' and 'Game UI' enum values already present in the gallery. * feat(prompt-templates): add preview images for dance and MMO HUD templates - assets/prompt-templates/image/social-media-post-sensational-girl-dance-storyboard-8-shots.jpg: an 8-panel storyboard render produced from the template itself, downscaled to 1024x1536 JPEG for gallery thumbnails. - assets/prompt-templates/image/game-ui-ancient-china-open-world-mmo-hud.jpg: a reference HUD screenshot rendered from the template prompt via gpt-image-2, downscaled to 1536x1024 JPEG. - Both templates now reference these previews via a raw.githubusercontent.com URL pointing at nexu-io/open-design main, matching the pattern used by the existing YouMind-sourced seeds (cms-assets.youmind.com) so every card in the Prompt Gallery carries a thumbnail once the PR merges. --------- Co-authored-by: Joey <joey@open-design.local>
This commit is contained in:
Binary file not shown.
|
After Width: | Height: | Size: 425 KiB |
BIN
Binary file not shown.
|
After Width: | Height: | Size: 191 KiB |
File diff suppressed because one or more lines are too long
+25
File diff suppressed because one or more lines are too long
@@ -46,7 +46,7 @@
|
||||
* All output JSON carries a `source` block so attribution stays intact.
|
||||
*/
|
||||
|
||||
import { mkdir, writeFile, readdir, unlink } from 'node:fs/promises';
|
||||
import { mkdir, writeFile, readdir, unlink, readFile } from 'node:fs/promises';
|
||||
import path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
@@ -317,25 +317,46 @@ function inferTags(title, prompt, surface) {
|
||||
return Array.from(set).slice(0, lim);
|
||||
}
|
||||
|
||||
async function clearDir(dir) {
|
||||
// Remove previously generated JSON files. Hand-authored templates (those
|
||||
// whose `source.repo` is not the upstream CC-BY corpus we import from) are
|
||||
// preserved so first-party curated prompts aren't wiped on re-run.
|
||||
async function clearDir(dir, upstreamRepo) {
|
||||
try {
|
||||
const files = await readdir(dir);
|
||||
for (const f of files) {
|
||||
if (f.endsWith('.json')) {
|
||||
await unlink(path.join(dir, f));
|
||||
if (!f.endsWith('.json')) continue;
|
||||
const filePath = path.join(dir, f);
|
||||
let keep = false;
|
||||
try {
|
||||
const parsed = JSON.parse(await readFile(filePath, 'utf8'));
|
||||
const repo = parsed?.source?.repo;
|
||||
if (repo && repo !== upstreamRepo) keep = true;
|
||||
} catch {
|
||||
// Unparseable file — treat as generated and remove.
|
||||
}
|
||||
if (!keep) await unlink(filePath);
|
||||
}
|
||||
} catch {
|
||||
// missing dir is fine — created below.
|
||||
}
|
||||
}
|
||||
|
||||
async function writeAll(entries, outDir) {
|
||||
async function writeAll(entries, outDir, upstreamRepo) {
|
||||
await mkdir(outDir, { recursive: true });
|
||||
await clearDir(outDir);
|
||||
await clearDir(outDir, upstreamRepo);
|
||||
// De-dup on slug; if two entries collide, keep the first (which is the
|
||||
// featured one — always parsed before "All Prompts").
|
||||
// featured one — always parsed before "All Prompts"). Hand-authored
|
||||
// templates already on disk (preserved by clearDir) also take priority
|
||||
// so we never overwrite curated first-party prompts.
|
||||
const seen = new Set();
|
||||
try {
|
||||
const existing = await readdir(outDir);
|
||||
for (const f of existing) {
|
||||
if (f.endsWith('.json')) seen.add(f.replace(/\.json$/, ''));
|
||||
}
|
||||
} catch {
|
||||
// noop
|
||||
}
|
||||
let count = 0;
|
||||
for (const entry of entries) {
|
||||
if (seen.has(entry.id)) continue;
|
||||
@@ -366,7 +387,7 @@ async function main() {
|
||||
continue;
|
||||
}
|
||||
const outDir = ctx.surface === 'image' ? OUT_IMAGE : OUT_VIDEO;
|
||||
const written = await writeAll(entries, outDir);
|
||||
const written = await writeAll(entries, outDir, ctx.repo);
|
||||
if (ctx.surface === 'image') totalImage += written;
|
||||
else totalVideo += written;
|
||||
console.log(
|
||||
|
||||
Reference in New Issue
Block a user