Spaces:
Sleeping
Sleeping
Upload 140 files
Browse files- engine/__pycache__/router.cpython-312.pyc +0 -0
- engine/__pycache__/router.cpython-314.pyc +0 -0
- engine/instructions/character_carmilla.md +5 -1
- engine/router.py +4 -3
- engine/schemas/__pycache__/chapter.cpython-312.pyc +0 -0
- engine/schemas/__pycache__/validate.cpython-312.pyc +0 -0
- engine/schemas/__pycache__/validate.cpython-314.pyc +0 -0
- engine/services/__pycache__/paging.cpython-312.pyc +0 -0
- engine/services/__pycache__/paging.cpython-314.pyc +0 -0
- web/index.html +21 -52
engine/__pycache__/router.cpython-312.pyc
CHANGED
|
Binary files a/engine/__pycache__/router.cpython-312.pyc and b/engine/__pycache__/router.cpython-312.pyc differ
|
|
|
engine/__pycache__/router.cpython-314.pyc
CHANGED
|
Binary files a/engine/__pycache__/router.cpython-314.pyc and b/engine/__pycache__/router.cpython-314.pyc differ
|
|
|
engine/instructions/character_carmilla.md
CHANGED
|
@@ -20,4 +20,8 @@
|
|
| 20 |
[๊ฐ๋ ์ง์ โ ์ด๋ฒ ํด์ ๋ฐ๋์ ๋ฐ์]
|
| 21 |
{guard_directive}
|
| 22 |
|
| 23 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 20 |
[๊ฐ๋ ์ง์ โ ์ด๋ฒ ํด์ ๋ฐ๋์ ๋ฐ์]
|
| 21 |
{guard_directive}
|
| 22 |
|
| 23 |
+
[์ถ๋ ฅ ํ์ โ ๋์ฌ๋ง (์๋ฐ์ ๊ฒฐํจ์ด๋ค)]
|
| 24 |
+
- ์ถ๋ ฅ์ ์นด๋ฅด๋ฐ๋ผ๊ฐ ์
๋ฐ์ ๋ด๋ ๋ง ๊ทธ ์์ฒด๋ง. ๋ฐ์ดํยท๊ดํธ ์์ด ๋์ฌ ํ
์คํธ๋ง ์ด๋ค.
|
| 25 |
+
- ํ๋ ๋ฌ์ฌ(๋ฏธ์ ์ง๋๋ค, ์์ ๋ป๋๋ค ๋ฑ)ยทํ์ ยท์ง๋ฌธยท๊ดํธ ์ฐ์ถยท์๋ง์ ์์ ๊ธ์ง.
|
| 26 |
+
๊ฐ์ ๊ณผ ์จ๋๋ ์ค์ง ๋งํฌยท๋จ์ด ์ ํยท๋ฌธ์ฅ์ ํธํก์ผ๋ก๋ง ๋๋ฌ๋ธ๋ค.
|
| 27 |
+
- ์๋ต์ ํ๊ตญ์ด, 3~6๋ฌธ์ฅ. ๊ณผ์ฅ ์์ด ์๋ํ๊ณ ๋๋ฅธํ๊ฒ.
|
engine/router.py
CHANGED
|
@@ -69,11 +69,12 @@ app = FastAPI(title="carmilla-demo")
|
|
| 69 |
|
| 70 |
_repo = ContentRepository(ROOT / "content")
|
| 71 |
|
| 72 |
-
# ํ์ด์ง ๋ณํฉ โ ์ฌ๋ฌ ์นด๋๋ฅผ ํ ํ๋ฉด์ผ๋ก (๋ฐ๋: chapter.page_target_chars
|
| 73 |
-
# ์ฐ์ถ ์ปทยท์ฑํ
์ต์ปค๋ ๋จ๋
ํ์ด์ง
|
|
|
|
| 74 |
_CUT_IDS = {cid for cid in IMAGE_MAP if _repo.has_card(cid)}
|
| 75 |
_SOLO_IDS = _CUT_IDS | {c.id for c in _repo.all_cards() if c.can_chat}
|
| 76 |
-
_pager = Pager(_repo, solo_ids=_SOLO_IDS, cut_ids=
|
| 77 |
|
| 78 |
|
| 79 |
def _partition_backgrounds() -> list[Optional[str]]:
|
|
|
|
| 69 |
|
| 70 |
_repo = ContentRepository(ROOT / "content")
|
| 71 |
|
| 72 |
+
# ํ์ด์ง ๋ณํฉ โ ์ฌ๋ฌ ์นด๋๋ฅผ ํ ํ๋ฉด์ผ๋ก (๋ฐ๋: chapter.page_target_chars).
|
| 73 |
+
# ์ฐ์ถ ์ปทยท์ฑํ
์ต์ปค๋ ๋จ๋
ํ์ด์ง. ์ปท๋ ํ์ 1ํ์ด์ง(ํ๋ก ํธ๊ฐ ์ด๋ฏธ์ง ์บ๋ฒ์ค ๋จ๊ณ๋ก ๋ ๋ โ
|
| 74 |
+
# ๋จ๋
์บก์
ํ๋ฉด ์์). ํํฐ์
์ ๊ฒฐ์ ์ (๊ฒฝ๋ก ๋ฌด๊ด)
|
| 75 |
_CUT_IDS = {cid for cid in IMAGE_MAP if _repo.has_card(cid)}
|
| 76 |
_SOLO_IDS = _CUT_IDS | {c.id for c in _repo.all_cards() if c.can_chat}
|
| 77 |
+
_pager = Pager(_repo, solo_ids=_SOLO_IDS, cut_ids=set())
|
| 78 |
|
| 79 |
|
| 80 |
def _partition_backgrounds() -> list[Optional[str]]:
|
engine/schemas/__pycache__/chapter.cpython-312.pyc
CHANGED
|
Binary files a/engine/schemas/__pycache__/chapter.cpython-312.pyc and b/engine/schemas/__pycache__/chapter.cpython-312.pyc differ
|
|
|
engine/schemas/__pycache__/validate.cpython-312.pyc
CHANGED
|
Binary files a/engine/schemas/__pycache__/validate.cpython-312.pyc and b/engine/schemas/__pycache__/validate.cpython-312.pyc differ
|
|
|
engine/schemas/__pycache__/validate.cpython-314.pyc
CHANGED
|
Binary files a/engine/schemas/__pycache__/validate.cpython-314.pyc and b/engine/schemas/__pycache__/validate.cpython-314.pyc differ
|
|
|
engine/services/__pycache__/paging.cpython-312.pyc
CHANGED
|
Binary files a/engine/services/__pycache__/paging.cpython-312.pyc and b/engine/services/__pycache__/paging.cpython-312.pyc differ
|
|
|
engine/services/__pycache__/paging.cpython-314.pyc
CHANGED
|
Binary files a/engine/services/__pycache__/paging.cpython-314.pyc and b/engine/services/__pycache__/paging.cpython-314.pyc differ
|
|
|
web/index.html
CHANGED
|
@@ -45,12 +45,6 @@
|
|
| 45 |
min-height: 100%; position: relative; }
|
| 46 |
#page-measure { position: absolute; left: 24px; right: 24px; top: 54px;
|
| 47 |
visibility: hidden; pointer-events: none; }
|
| 48 |
-
.ep-header { text-align: center; color: var(--gold-dim); font-size: .82rem;
|
| 49 |
-
letter-spacing: .35em; margin: 26px 0 6px; }
|
| 50 |
-
.card-title { text-align: center; color: var(--gold); font-size: 1.05rem;
|
| 51 |
-
letter-spacing: .18em; margin-bottom: 44px; }
|
| 52 |
-
.card-title::after { content: ""; display: block; width: 42px; height: 1px;
|
| 53 |
-
background: var(--gold-dim); margin: 20px auto 0; opacity: .6; }
|
| 54 |
.prose { margin: 0 0 1.6em; font-size: 1.02rem; text-align: justify;
|
| 55 |
word-break: keep-all; white-space: pre-line; }
|
| 56 |
.center-line { text-align: center; color: var(--gold); font-size: 1.12rem;
|
|
@@ -135,17 +129,6 @@
|
|
| 135 |
body.browsing #browse-ribbon { display: block; }
|
| 136 |
body.browsing #choice-area { pointer-events: none; opacity: .45; }
|
| 137 |
|
| 138 |
-
/* โโ ์ฐ์ถ ์ปท ํ์ด์ง (์ด๋ฏธ์ง = ๋
๋ฆฝ ํ์ด์ง) โโ */
|
| 139 |
-
.page-cut { display: flex; flex-direction: column; align-items: center;
|
| 140 |
-
justify-content: center; min-height: 64vh; }
|
| 141 |
-
.page-cut img { max-width: min(92vw, 620px); max-height: 66vh; object-fit: contain;
|
| 142 |
-
box-shadow: 0 0 80px rgba(0,0,0,.9); border: 1px solid rgba(201,168,106,.25);
|
| 143 |
-
animation: still 6s ease both; }
|
| 144 |
-
@keyframes still { from { opacity: 0; transform: scale(1.015); } 20% { opacity: 1; }
|
| 145 |
-
to { opacity: 1; transform: scale(1); } }
|
| 146 |
-
.cut-caption { color: var(--gold); letter-spacing: .2em; margin-top: 26px;
|
| 147 |
-
font-size: .95rem; animation: fadein 2.2s ease both .8s; }
|
| 148 |
-
|
| 149 |
/* โโ ์ฑํ
(ํ๋จ ๋๋ก์ด ์ ์ง) โโ */
|
| 150 |
#chat-wrap { position: fixed; left: 0; right: 0; bottom: 0; z-index: 30;
|
| 151 |
background: linear-gradient(transparent, var(--ink) 30%);
|
|
@@ -244,7 +227,7 @@
|
|
| 244 |
</div>
|
| 245 |
|
| 246 |
<script>
|
| 247 |
-
let SID = null, STEP = null,
|
| 248 |
let history = [], cursor = -1; // ๋ทฐ ํ์คํ ๋ฆฌ โ {html, progress, ep, bg} (์ฝ๊ธฐ ์ ์ฉ ๋๋์๋ณด๊ธฐ)
|
| 249 |
let pendingStep = null; // ๋ค์ ๋๊น์ ํ์ํ step (์ ํ ๊ฒฐ๊ณผ ๋ค ยท ์ฐ์ถ ์ปท ๋ค ๋ณธ๋ฌธ)
|
| 250 |
let pendingScreens = []; // ํ์ฌ ์๋ฒ ํ์ด์ง์ ๋จ์ ํ๋ฉด๋ค (๋ทฐํฌํธ ๋ถํ ๋ถ)
|
|
@@ -388,22 +371,14 @@ function groupNarration(blocks) {
|
|
| 388 |
return groups;
|
| 389 |
}
|
| 390 |
|
| 391 |
-
function epHeader(step, fresh) {
|
| 392 |
-
const c = step.card;
|
| 393 |
-
const h = (fresh || c.episode !== lastEpisode)
|
| 394 |
-
? `<div class="ep-header fade">${esc(c.episode_title || c.episode)}</div>` : '';
|
| 395 |
-
lastEpisode = c.episode;
|
| 396 |
-
return h;
|
| 397 |
-
}
|
| 398 |
-
|
| 399 |
/* ์๋ฒ ํ์ด์ง โ ํ๋ฉด๋ค {html, prog} (์คํฌ๋กค ์๋ ๋๊น).
|
|
|
|
| 400 |
- ์ด์ ํ์(์นด๋ฅด๋ฐ๋ผยท์๋ฒ์ง)์ ๋ฐํ = ์ ๋ฉด ์คํ
์ด์ง. ๊ฐ์ ์นด๋ ์์ ํ์ ์ง๋ฌธยท
|
| 401 |
๋ฌด์ด์ ๋ฐํยท์ค์ ๋์ฌ๋ ๊ทธ ์ด๋ฏธ์ง ์ ๋งํ์ ๋จ๊ณ๋ก ํ์น (์๋ฌธ ์์ ๋ณด์กด)
|
| 402 |
- ์ง์ ์ ์งง์ ๋ฆฌ๋์ธ(role=lead_in, ๊ฐ์ ์นด๋)์ ์คํ
์ด์ง์ ์ฒซ ๋จ๊ณ(์ง๋ฌธ ๋ฐ์ค)๋ก
|
| 403 |
- ๋ง์ง๋ง ์ฅ๋ฉด์ด ์คํ
์ด์ง๋ฉด ์ ํ์ง๋ ์ต์ข
๋จ๊ณ์, ์๋๋ฉด ํ
์คํธ ํ๋ฉด ํ๋จ์
|
| 404 |
- ํ
์คํธ๋ ์ธ๊ทธ๋จผํธ๋ณ ์ค์ธก ๊ท ํ ๋ถํ ๋ก ํ๋ฉด์ ์ต๋ํ ์ฑ์ด๋ค */
|
| 405 |
-
function buildScreens(step
|
| 406 |
-
const head = epHeader(step, fresh);
|
| 407 |
const groups = groupNarration(step.narration);
|
| 408 |
const choices = choicesHtml(step);
|
| 409 |
const isP = (g) => g.lines && g.portrait && !PLAYER_SPEAKERS.has(g.spk);
|
|
@@ -419,12 +394,13 @@ function buildScreens(step, fresh) {
|
|
| 419 |
continue;
|
| 420 |
}
|
| 421 |
const steps = [];
|
| 422 |
-
|
| 423 |
-
|
| 424 |
-
|
|
|
|
|
|
|
| 425 |
if (lg.block && lg.block.role === 'lead_in' && lg.card === g.card) {
|
| 426 |
-
|
| 427 |
-
if (!prev.groups.length) scenes.pop();
|
| 428 |
steps.push({mode: 'narr', name: null, lines: [lg.block.text]});
|
| 429 |
}
|
| 430 |
}
|
|
@@ -486,29 +462,22 @@ function buildScreens(step, fresh) {
|
|
| 486 |
});
|
| 487 |
meas.innerHTML = '';
|
| 488 |
|
| 489 |
-
if (screens.length) screens
|
| 490 |
-
else screens.push(head);
|
| 491 |
return screens.map((html) => ({html, prog: step.progress}));
|
| 492 |
}
|
| 493 |
|
| 494 |
-
/* ์ฐ์ถ ์ปท(์ด๋ฏธ์ง ์นด๋) โ ์บ๋ฒ์ค ํ๋ฉด๋ค:
|
| 495 |
-
|
| 496 |
-
function buildCutScreens(step
|
| 497 |
-
const head = epHeader(step, fresh);
|
| 498 |
-
const cut = `<div class="page-cut"><img src="${encodeURI(step.image)}" alt="">` +
|
| 499 |
-
`<div class="cut-caption">${esc(step.card.title)}</div></div>`;
|
| 500 |
-
const screens = [{html: head + cut,
|
| 501 |
-
prog: {index: step.progress.index - 1, total: step.progress.total}}];
|
| 502 |
const steps = groupNarration(step.narration).map(groupToStep);
|
| 503 |
const choices = choicesHtml(step);
|
| 504 |
-
if (!steps.length
|
| 505 |
-
steps.
|
| 506 |
-
|
| 507 |
-
|
| 508 |
-
|
| 509 |
-
|
| 510 |
-
});
|
| 511 |
-
return screens;
|
| 512 |
}
|
| 513 |
|
| 514 |
function choicesHtml(step) {
|
|
@@ -529,7 +498,7 @@ function setProgress(p, epLabel) {
|
|
| 529 |
function showStep(step, fresh) {
|
| 530 |
STEP = step; pendingStep = null; pendingScreens = [];
|
| 531 |
if (step.ending) return renderEnding(step.ending);
|
| 532 |
-
const screens = step.image ? buildCutScreens(step
|
| 533 |
const first = screens.shift();
|
| 534 |
pushEntry(first.html, first.prog, step.card.episode_title, step.background);
|
| 535 |
pendingScreens = screens; // ๋๋จธ์ง๋ ํ์ด์ง ๋๊น์ผ๋ก
|
|
|
|
| 45 |
min-height: 100%; position: relative; }
|
| 46 |
#page-measure { position: absolute; left: 24px; right: 24px; top: 54px;
|
| 47 |
visibility: hidden; pointer-events: none; }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 48 |
.prose { margin: 0 0 1.6em; font-size: 1.02rem; text-align: justify;
|
| 49 |
word-break: keep-all; white-space: pre-line; }
|
| 50 |
.center-line { text-align: center; color: var(--gold); font-size: 1.12rem;
|
|
|
|
| 129 |
body.browsing #browse-ribbon { display: block; }
|
| 130 |
body.browsing #choice-area { pointer-events: none; opacity: .45; }
|
| 131 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 132 |
/* โโ ์ฑํ
(ํ๋จ ๋๋ก์ด ์ ์ง) โโ */
|
| 133 |
#chat-wrap { position: fixed; left: 0; right: 0; bottom: 0; z-index: 30;
|
| 134 |
background: linear-gradient(transparent, var(--ink) 30%);
|
|
|
|
| 227 |
</div>
|
| 228 |
|
| 229 |
<script>
|
| 230 |
+
let SID = null, STEP = null, busy = false;
|
| 231 |
let history = [], cursor = -1; // ๋ทฐ ํ์คํ ๋ฆฌ โ {html, progress, ep, bg} (์ฝ๊ธฐ ์ ์ฉ ๋๋์๋ณด๊ธฐ)
|
| 232 |
let pendingStep = null; // ๋ค์ ๋๊น์ ํ์ํ step (์ ํ ๊ฒฐ๊ณผ ๋ค ยท ์ฐ์ถ ์ปท ๋ค ๋ณธ๋ฌธ)
|
| 233 |
let pendingScreens = []; // ํ์ฌ ์๋ฒ ํ์ด์ง์ ๋จ์ ํ๋ฉด๋ค (๋ทฐํฌํธ ๋ถํ ๋ถ)
|
|
|
|
| 371 |
return groups;
|
| 372 |
}
|
| 373 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 374 |
/* ์๋ฒ ํ์ด์ง โ ํ๋ฉด๋ค {html, prog} (์คํฌ๋กค ์๋ ๋๊น).
|
| 375 |
+
ํ(่ฉฑ) ํ๊ธฐ๋ ์๋จ ์ค์ ์์ ๋ผ๋ฒจ์ด ๋ด๋น โ ๋ณธ๋ฌธ ํค๋๋ ๋ฃ์ง ์๋๋ค.
|
| 376 |
- ์ด์ ํ์(์นด๋ฅด๋ฐ๋ผยท์๋ฒ์ง)์ ๋ฐํ = ์ ๋ฉด ์คํ
์ด์ง. ๊ฐ์ ์นด๋ ์์ ํ์ ์ง๋ฌธยท
|
| 377 |
๋ฌด์ด์ ๋ฐํยท์ค์ ๋์ฌ๋ ๊ทธ ์ด๋ฏธ์ง ์ ๋งํ์ ๋จ๊ณ๋ก ํ์น (์๋ฌธ ์์ ๋ณด์กด)
|
| 378 |
- ์ง์ ์ ์งง์ ๋ฆฌ๋์ธ(role=lead_in, ๊ฐ์ ์นด๋)์ ์คํ
์ด์ง์ ์ฒซ ๋จ๊ณ(์ง๋ฌธ ๋ฐ์ค)๋ก
|
| 379 |
- ๋ง์ง๋ง ์ฅ๋ฉด์ด ์คํ
์ด์ง๋ฉด ์ ํ์ง๋ ์ต์ข
๋จ๊ณ์, ์๋๋ฉด ํ
์คํธ ํ๋ฉด ํ๋จ์
|
| 380 |
- ํ
์คํธ๋ ์ธ๊ทธ๋จผํธ๋ณ ์ค์ธก ๊ท ํ ๋ถํ ๋ก ํ๋ฉด์ ์ต๋ํ ์ฑ์ด๋ค */
|
| 381 |
+
function buildScreens(step) {
|
|
|
|
| 382 |
const groups = groupNarration(step.narration);
|
| 383 |
const choices = choicesHtml(step);
|
| 384 |
const isP = (g) => g.lines && g.portrait && !PLAYER_SPEAKERS.has(g.spk);
|
|
|
|
| 394 |
continue;
|
| 395 |
}
|
| 396 |
const steps = [];
|
| 397 |
+
// ๋ฆฌ๋์ธ โ ์ฒซ ๋จ๊ณ๋ก ์ด๋. ๋จ, ๋ฆฌ๋์ธ์ด ์ ํ
์คํธ ์ฅ๋ฉด์ "์ ์ผํ" ๋ด์ฉ์ผ ๋๋ง โ
|
| 398 |
+
// ์์ ๋ค๋ฅธ ์ง๋ฌธ์ด ์์ผ๋ฉด ๊ทธ ํ๋ฆ์ ํฉ์ณ ๋๋ ๊ฒ ์์ฐ์ค๋ฝ๋ค (๊ณ ์ ํ๋ฉด์ผ ๋๋ง ๊ตฌ์ )
|
| 399 |
+
const prev = scenes[scenes.length - 1];
|
| 400 |
+
if (prev && prev.type === 'text' && prev.groups.length === 1) {
|
| 401 |
+
const lg = prev.groups[0];
|
| 402 |
if (lg.block && lg.block.role === 'lead_in' && lg.card === g.card) {
|
| 403 |
+
scenes.pop();
|
|
|
|
| 404 |
steps.push({mode: 'narr', name: null, lines: [lg.block.text]});
|
| 405 |
}
|
| 406 |
}
|
|
|
|
| 462 |
});
|
| 463 |
meas.innerHTML = '';
|
| 464 |
|
| 465 |
+
if (!screens.length) screens.push('');
|
|
|
|
| 466 |
return screens.map((html) => ({html, prog: step.progress}));
|
| 467 |
}
|
| 468 |
|
| 469 |
+
/* ์ฐ์ถ ์ปท(์ด๋ฏธ์ง ์นด๋) โ ์บ๋ฒ์ค ํ๋ฉด๋ค: ์ด๋ฏธ์ง ์ ์ง๋ฌธยท๋ฐํ ๋งํ์ ๋จ๊ณ โ ์ ํ์ง.
|
| 470 |
+
๋จ๋
์บก์
ํ๋ฉด์ ๋์ง ์๋๋ค (๊ฐ์ ์ด๋ฏธ์ง๊ฐ ๋ฐ๋ก ์บ๋ฒ์ค๋ก ์ด์ด์ง๋ฏ๋ก ๊ตฐ๋๋๊ธฐ) */
|
| 471 |
+
function buildCutScreens(step) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 472 |
const steps = groupNarration(step.narration).map(groupToStep);
|
| 473 |
const choices = choicesHtml(step);
|
| 474 |
+
if (!steps.length) steps.push({mode: 'narr', name: null, lines: []});
|
| 475 |
+
return steps.map((s, i) => ({
|
| 476 |
+
html: stageScreen(step.image, step.card.title,
|
| 477 |
+
s.lines.length ? speechBox(s.name, s.lines, s.mode) : '',
|
| 478 |
+
i === steps.length - 1 ? choices : ''),
|
| 479 |
+
prog: step.progress,
|
| 480 |
+
}));
|
|
|
|
| 481 |
}
|
| 482 |
|
| 483 |
function choicesHtml(step) {
|
|
|
|
| 498 |
function showStep(step, fresh) {
|
| 499 |
STEP = step; pendingStep = null; pendingScreens = [];
|
| 500 |
if (step.ending) return renderEnding(step.ending);
|
| 501 |
+
const screens = step.image ? buildCutScreens(step) : buildScreens(step);
|
| 502 |
const first = screens.shift();
|
| 503 |
pushEntry(first.html, first.prog, step.card.episode_title, step.background);
|
| 504 |
pendingScreens = screens; // ๋๋จธ์ง๋ ํ์ด์ง ๋๊น์ผ๋ก
|