feat: 圖示配色取自人格「最新一次登場」的官方視覺
新增 skill persona-icon:上網查出該人格最新的官方視覺 → 下載 →
**用 Read 親眼看過** → 取髮色/瞳色/服裝色 → 用那組配色繪製圖示,
來源網址與造型描述一併寫進 state/config.json 備查。
CLI:
icon generate --palette "hair=#..,eye=#..,accent=#..,secondary=#..,light=#.."
--source-url <網址> [--source-note <說明> --source-date <日期>]
* --palette 必須配 --source-url(配色要有出處,CLI 強制)。
* 配色與來源存進 config.icon,之後不帶 --palette 重畫會沿用,不會變回雜湊色。
* icon show 會列出調色盤與來源網址。
繪製:漸層=髮色→服裝主色、外框=瞳色、點陣紋=亮色、中央仍是編號前兩字。
一深一淺的極端配色(藍黑髮+淡粉洋裝)會把較亮端往較暗端壓到對比 ≥ 3.2,
確保字讀得到——顏色仍是照片來的,只是收斂色階。
為什麼不是把照片本身當圖示:
1) 環境裡沒有任何影像解碼器(rsvg/imagemagick/Pillow/ffmpeg 都沒有),
JPEG/WebP 讀不進來,無法轉成 PNG;
2) 把他人的美術作品原樣放進存取庫是散布,不是引用。
取配色是有依據又不搬運原圖的做法,這個取捨寫進了 README 與 skill。
已套用到兩個真實人格:
ASUNA-01 取自《Unanswered//butterfly》(2026) 官方主視覺(金栗髮/紅褐瞳/紅衣粉裙)
YUI-01 取自 AniList 官方角色圖(藍黑髮/暖棕瞳)+ Unital Ring 導航妖精造型
selftest 136 項全綠(新增第 ⑮ 節:調色盤解析、缺來源會被擋、格式錯誤會被擋、
來源寫入 config、重畫沿用配色、極端配色的對比保證)。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
+38
-6
@@ -1127,9 +1127,13 @@ commands.icon = async ({ flags, positional }) => {
|
||||
if (action === "show") {
|
||||
requireMember(slug, session, Boolean(flags["as-guest"]));
|
||||
const spec = ic.iconSpec(slug);
|
||||
const src = spec.source || {};
|
||||
emit({ persona: slug, spec, exists: ic.hasIcon(slug) }, flags.json, [
|
||||
`人格 \`${slug}\` 圖示:${ic.hasIcon(slug) ? "✔ 已產生" : "✘ 尚未產生(跑 `icon generate`)"}`,
|
||||
` 字母 ${spec.letters}|配色 ${JSON.stringify(spec.c1)} → ${JSON.stringify(spec.c2)}|seed ${spec.seed}`,
|
||||
` 字母 ${spec.letters}|配色 ${spec.palette ? "取自參考照片" : "由編號雜湊"}` +
|
||||
`:${JSON.stringify(spec.c1)} → ${JSON.stringify(spec.c2)}|seed ${spec.seed}`,
|
||||
...(spec.palette ? [` 調色盤:${ic.paletteToString(spec.palette)}`] : []),
|
||||
...(src.url ? [` 參考來源:${src.url}${src.note ? `(${src.note})` : ""}${src.date ? `|${src.date}` : ""}`] : []),
|
||||
` ${ic.iconSvgPath(slug)}`,
|
||||
` ${ic.iconPngPath(slug)}`,
|
||||
]);
|
||||
@@ -1138,16 +1142,41 @@ commands.icon = async ({ flags, positional }) => {
|
||||
if (action !== "generate") die(`未知 action:${action}(可用 generate/show)`);
|
||||
requireOwner(slug, session);
|
||||
if (ic.hasIcon(slug) && !flags.force) {
|
||||
die(`人格 \`${slug}\` 已經有圖示了。改過身分要重畫請加 --force。`);
|
||||
die(`人格 \`${slug}\` 已經有圖示了。改過身分或換了參考照片要重畫請加 --force。`);
|
||||
}
|
||||
const size = num(flags.size, ic.DEFAULT_SIZE);
|
||||
if (!Number.isFinite(size) || size < 16 || size > 2048) die("--size 只能是 16–2048。");
|
||||
const res = ic.generateIcon(slug, { size });
|
||||
let palette = null;
|
||||
if (flags.palette) {
|
||||
palette = ic.parsePalette(str(flags.palette));
|
||||
if (!palette) {
|
||||
die(
|
||||
"`--palette` 格式錯誤。要 `hair=#rrggbb,eye=#rrggbb,accent=#rrggbb,secondary=#rrggbb,light=#rrggbb`," +
|
||||
"其中 hair 與 accent 必填(顏色請取自你實際看過的參考照片)。",
|
||||
);
|
||||
}
|
||||
if (!str(flags["source-url"])) {
|
||||
die("用 `--palette` 就必須帶 `--source-url`:配色是從哪張圖取的要留得下來(可查證)。");
|
||||
}
|
||||
}
|
||||
const source = str(flags["source-url"])
|
||||
? {
|
||||
url: str(flags["source-url"]),
|
||||
note: str(flags["source-note"]) || null,
|
||||
date: str(flags["source-date"]) || pl.nowIso().slice(0, 10),
|
||||
}
|
||||
: null;
|
||||
const res = ic.generateIcon(slug, { size, palette, source });
|
||||
const lines = [
|
||||
`✔ 人格 \`${slug}\` 的圖示已產生(${size}×${size})。`,
|
||||
` ${res.svg}(${(res.bytes.svg / 1024).toFixed(1)} KB)`,
|
||||
` ${res.png}(${(res.bytes.png / 1024).toFixed(1)} KB)`,
|
||||
` 字母 ${res.spec.letters}|配色由編號 \`${res.spec.code}\`、名字與 emoji 決定(同一個人格永遠同一張圖)`,
|
||||
res.spec.palette
|
||||
? ` 字母 ${res.spec.letters}|配色取自參考照片:${ic.paletteToString(res.spec.palette)}`
|
||||
: ` 字母 ${res.spec.letters}|配色由編號 \`${res.spec.code}\`、名字與 emoji 決定(同一個人格永遠同一張圖)`,
|
||||
...(source?.url || res.spec.source?.url
|
||||
? [` 參考來源:${(source || res.spec.source).url}${(source || res.spec.source).note ? `\n ${(source || res.spec.source).note}` : ""}`]
|
||||
: []),
|
||||
];
|
||||
// 圖示屬於低頻的身分資料 → Wiki 區;順便設成 Gitea 存取庫的頭像
|
||||
if (!flags["no-gitea"] && !gt.giteaProblem() && gt.personaCode(slug)) {
|
||||
@@ -1312,8 +1341,11 @@ const HELP = `persona.mjs — jsc-persona 人格 / 記憶 / 情緒 / 關係圖 C
|
||||
|
||||
圖示(建立人格並補齊資料後跑):
|
||||
icon generate|show --session <id> [--size 512 --force --no-gitea]
|
||||
由編號/名字/emoji 決定配色與字母,產出 icon.svg + icon.png(同一人格永遠同一張圖),
|
||||
並設為 Gitea 存取庫頭像、同步到 Wiki 區。
|
||||
[--palette "hair=#..,eye=#..,accent=#..,secondary=#..,light=#.."]
|
||||
[--source-url <參考照片網址> --source-note <說明> --source-date <YYYY-MM-DD>]
|
||||
產出 icon.svg + icon.png,設為 Gitea 存取庫頭像並同步到 Wiki 區。
|
||||
沒帶 --palette → 配色由編號/名字/emoji 雜湊而來。
|
||||
帶了 --palette → 用「你實際看過的參考照片」萃取的顏色(必須同時帶 --source-url 存證)。
|
||||
|
||||
編號與 Gitea(存取庫名稱 = 人格編號):
|
||||
code show|assign|next --session <id> [--romaji <英文名> --code <ASUNA-01> --rename --force --public]
|
||||
|
||||
Reference in New Issue
Block a user