Spaces:
Sleeping
Sleeping
Upload 57 files
Browse files- engine/schemas/chapter.py +2 -1
- web/index.html +73 -54
engine/schemas/chapter.py
CHANGED
|
@@ -49,7 +49,8 @@ class Chapter(BaseModel):
|
|
| 49 |
canon_rules: list[str] = Field(default_factory=list)
|
| 50 |
r_cards: list[RCardRef] = Field(default_factory=list)
|
| 51 |
pocket_max_turns: int = 6 # κ°μ
ν¬μΌ μμ μ±ν
ν΄ μν (κ°μ μλ ΄)
|
| 52 |
-
page_target_chars: int =
|
|
|
|
| 53 |
|
| 54 |
|
| 55 |
class ChapterFile(BaseModel):
|
|
|
|
| 49 |
canon_rules: list[str] = Field(default_factory=list)
|
| 50 |
r_cards: list[RCardRef] = Field(default_factory=list)
|
| 51 |
pocket_max_turns: int = 6 # κ°μ
ν¬μΌ μμ μ±ν
ν΄ μν (κ°μ μλ ΄)
|
| 52 |
+
page_target_chars: int = 850 # νμ΄μ§ λ³ν© λͺ©ν κΈμ μ β ν
μ€νΈ νλ©΄μ κ°λ μ±μ°λ κΈ°μ€
|
| 53 |
+
# (νλ‘ νΈκ° λ·°ν¬νΈ μ€μΈ‘μΌλ‘ λ€μ νλ©΄ λΆν νλ―λ‘, ν¬κ² μ‘μμλ‘ νλ©΄μ΄ μμ°¨κ² μ±μμ§λ€)
|
| 54 |
|
| 55 |
|
| 56 |
class ChapterFile(BaseModel):
|
web/index.html
CHANGED
|
@@ -67,29 +67,26 @@
|
|
| 67 |
.vn-stage { position: relative; margin: 1.2em auto 0; height: min(76vh, 860px);
|
| 68 |
overflow: hidden; border: 1px solid rgba(201,168,106,.28);
|
| 69 |
box-shadow: 0 0 80px rgba(0,0,0,.9); }
|
| 70 |
-
.vn-stage.with-lead { height: min(62vh, 720px); } /* 리λμΈ μ§λ¬Έμ΄ μμ λΆλ νλ©΄ */
|
| 71 |
.vn-stage-art { position: absolute; inset: 0; width: 100%; height: 100%;
|
| 72 |
object-fit: cover; object-position: top center; }
|
| 73 |
.vn-stage::after { content: ""; position: absolute; inset: 0;
|
| 74 |
-
background: linear-gradient(rgba(11,14,20,.
|
| 75 |
-
transparent
|
| 76 |
.vn-stage-panel { position: absolute; left: 0; right: 0; bottom: 0; z-index: 1;
|
| 77 |
-
padding: 0
|
| 78 |
-
|
| 79 |
-
|
| 80 |
-
|
| 81 |
-
|
| 82 |
-
.vn-
|
| 83 |
-
|
| 84 |
-
|
| 85 |
-
|
| 86 |
-
.vn-
|
| 87 |
-
|
| 88 |
-
|
| 89 |
-
.vn-
|
| 90 |
-
.vn-
|
| 91 |
-
.vn-line { font-size: 1.06rem; color: #e5dfd2; line-height: 2.05;
|
| 92 |
-
word-break: keep-all; margin: .35em 0; }
|
| 93 |
.stage-dir { color: var(--text-dim); font-style: italic; font-size: .92rem;
|
| 94 |
text-align: center; margin: 1.4em 0; }
|
| 95 |
.result-block { margin: 1.6em 0; padding: 1em 1.2em; background: rgba(201,168,106,.05);
|
|
@@ -348,12 +345,20 @@ function charLines(g) { // μμ¬ μ λ°ν β μ’ νμλͺ
Β·
|
|
| 348 |
`<div class="char-line fade"><span class="spk">${esc(name)}</span>β${esc(t)}β</div>`).join('');
|
| 349 |
}
|
| 350 |
|
| 351 |
-
function
|
| 352 |
-
let h = `<div class="vn-
|
| 353 |
-
|
| 354 |
-
|
| 355 |
-
for (const t of
|
| 356 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 357 |
}
|
| 358 |
|
| 359 |
function groupNarration(blocks) {
|
|
@@ -377,9 +382,10 @@ function groupNarration(blocks) {
|
|
| 377 |
|
| 378 |
/* μλ² νμ΄μ§ β νλ©΄λ€ (μ€ν¬λ‘€ μλ λκΉ).
|
| 379 |
- νμ΄νμ μνΌμλ 첫 νμ΄μ§μ ν(θ©±) ν€λλ§ β μΉ΄λ μμ λͺ©μ νμνμ§ μλλ€ (μ±
μ νλ¦)
|
| 380 |
-
-
|
| 381 |
-
- μ§
|
| 382 |
-
|
|
|
|
| 383 |
function buildScreens(step, fresh) {
|
| 384 |
const c = step.card;
|
| 385 |
let head = '';
|
|
@@ -389,17 +395,15 @@ function buildScreens(step, fresh) {
|
|
| 389 |
|
| 390 |
const groups = groupNarration(step.narration);
|
| 391 |
const choices = choicesHtml(step);
|
| 392 |
-
|
|
|
|
| 393 |
if (step.can_chat || step.choices.length) {
|
| 394 |
-
for (let k = groups.length - 1; k >= 0; k--)
|
| 395 |
-
|
| 396 |
-
if (g.lines && g.portrait && !PLAYER_SPEAKERS.has(g.spk)) { stageIdx = k; break; }
|
| 397 |
-
}
|
| 398 |
}
|
| 399 |
-
const stageIsLast = stageIdx === groups.length - 1;
|
| 400 |
|
| 401 |
const meas = $('page-measure');
|
| 402 |
-
const budget = Math.max((window.innerHeight || 900) -
|
| 403 |
const measure = (html) => { meas.innerHTML = html; return meas.offsetHeight; };
|
| 404 |
|
| 405 |
const screens = [];
|
|
@@ -427,28 +431,43 @@ function buildScreens(step, fresh) {
|
|
| 427 |
seg = [];
|
| 428 |
};
|
| 429 |
|
| 430 |
-
|
| 431 |
-
|
| 432 |
-
|
| 433 |
-
|
| 434 |
-
let leadHtml = '';
|
| 435 |
-
const prev = groups[k - 1];
|
| 436 |
-
if (prev && prev.block && prev.block.role === 'lead_in'
|
| 437 |
-
&& seg.length && seg[seg.length - 1].html === block(prev.block)) {
|
| 438 |
-
leadHtml = seg.pop().html;
|
| 439 |
-
}
|
| 440 |
flushSeg();
|
| 441 |
-
screens.push(
|
| 442 |
-
|
|
|
|
|
|
|
| 443 |
}
|
| 444 |
-
const html = g.lines ? charLines(g) : block(g.block);
|
| 445 |
-
seg.push({html, h: measure(html)});
|
| 446 |
-
});
|
| 447 |
-
if (choices && !stageIsLast) {
|
| 448 |
-
const area = `<div id="choice-area">${choices}</div>`;
|
| 449 |
-
seg.push({html: area, h: measure(area)});
|
| 450 |
}
|
| 451 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 452 |
meas.innerHTML = '';
|
| 453 |
|
| 454 |
if (screens.length) screens[0] = head + screens[0]; // ν(θ©±) ν€λλ 첫 νλ©΄μ
|
|
|
|
| 67 |
.vn-stage { position: relative; margin: 1.2em auto 0; height: min(76vh, 860px);
|
| 68 |
overflow: hidden; border: 1px solid rgba(201,168,106,.28);
|
| 69 |
box-shadow: 0 0 80px rgba(0,0,0,.9); }
|
|
|
|
| 70 |
.vn-stage-art { position: absolute; inset: 0; width: 100%; height: 100%;
|
| 71 |
object-fit: cover; object-position: top center; }
|
| 72 |
.vn-stage::after { content: ""; position: absolute; inset: 0;
|
| 73 |
+
background: linear-gradient(rgba(11,14,20,.1) 0%, transparent 20%,
|
| 74 |
+
transparent 46%, rgba(11,14,20,.9) 88%); }
|
| 75 |
.vn-stage-panel { position: absolute; left: 0; right: 0; bottom: 0; z-index: 1;
|
| 76 |
+
padding: 0 14px 14px; }
|
| 77 |
+
/* μ¬κ° λ§νμ β μ’μΈ‘ νμλͺ
Β· μ°μΈ‘ λμ¬ */
|
| 78 |
+
.vn-box { display: flex; gap: 14px; align-items: flex-start; text-align: left;
|
| 79 |
+
background: rgba(11,14,20,.82); border: 1px solid rgba(201,168,106,.45);
|
| 80 |
+
border-radius: 2px; padding: 14px 18px; animation: fadein .5s ease both; }
|
| 81 |
+
.vn-box .spk { flex: 0 0 auto; color: var(--gold); font-size: .88rem;
|
| 82 |
+
letter-spacing: .1em; padding-top: 3px; max-width: 7em;
|
| 83 |
+
word-break: keep-all; }
|
| 84 |
+
.vn-box .lines { flex: 1; min-width: 0; }
|
| 85 |
+
.vn-box .vn-line { font-size: 1rem; color: #e5dfd2; line-height: 1.9;
|
| 86 |
+
word-break: keep-all; margin: .2em 0; }
|
| 87 |
+
.vn-box.narr .vn-line { color: var(--text-dim); font-style: normal; } /* μ§λ¬Έ μ ν λ¨κ³ */
|
| 88 |
+
.vn-stage-panel #choice-area { margin: 12px auto 0; max-width: 470px; }
|
| 89 |
+
.vn-stage-panel #choice-area button { padding: 12px 10px; background: rgba(11,14,20,.45); }
|
|
|
|
|
|
|
| 90 |
.stage-dir { color: var(--text-dim); font-style: italic; font-size: .92rem;
|
| 91 |
text-align: center; margin: 1.4em 0; }
|
| 92 |
.result-block { margin: 1.6em 0; padding: 1em 1.2em; background: rgba(201,168,106,.05);
|
|
|
|
| 345 |
`<div class="char-line fade"><span class="spk">${esc(name)}</span>β${esc(t)}β</div>`).join('');
|
| 346 |
}
|
| 347 |
|
| 348 |
+
function speechBox(name, lines, mode) { // μ¬κ° λ§νμ : μ’ νμλͺ
Β· μ° λμ¬. mode: speech|narr|center
|
| 349 |
+
let h = `<div class="vn-box${mode === 'narr' ? ' narr' : ''}">`;
|
| 350 |
+
if (name) h += `<span class="spk">${esc(name)}</span>`;
|
| 351 |
+
h += `<div class="lines">`;
|
| 352 |
+
for (const t of lines)
|
| 353 |
+
h += `<div class="vn-line">${mode === 'narr' ? esc(t) : 'β' + esc(t) + 'β'}</div>`;
|
| 354 |
+
return h + `</div></div>`;
|
| 355 |
+
}
|
| 356 |
+
|
| 357 |
+
function stageScreen(g, boxHtml, choices) { // μ λ©΄ μ΄λ―Έμ§ + λ§νμ (+μ νμ§)
|
| 358 |
+
return `<div class="vn-stage">` +
|
| 359 |
+
`<img class="vn-stage-art" src="${encodeURI(g.portrait)}" alt="${esc(g.spk)}">` +
|
| 360 |
+
`<div class="vn-stage-panel">${boxHtml}` +
|
| 361 |
+
`<div id="choice-area">${choices}</div></div></div>`;
|
| 362 |
}
|
| 363 |
|
| 364 |
function groupNarration(blocks) {
|
|
|
|
| 382 |
|
| 383 |
/* μλ² νμ΄μ§ β νλ©΄λ€ (μ€ν¬λ‘€ μλ λκΉ).
|
| 384 |
- νμ΄νμ μνΌμλ 첫 νμ΄μ§μ ν(θ©±) ν€λλ§ β μΉ΄λ μμ λͺ©μ νμνμ§ μλλ€ (μ±
μ νλ¦)
|
| 385 |
+
- μ΄μ μλ νμμ λ°νλ μ λΆ μ λ©΄ μ€ν
μ΄μ§(μ΄λ―Έμ§ + μ¬κ° λ§νμ ) λ¨λ
νλ©΄
|
| 386 |
+
- μΈν°λμ
νμ΄μ§μ λ§μ§λ§ μ΄μ λ°νλΆν°λ "λ§νμ λ¨κ³ μνμ€" β κ°μ μ΄λ―Έμ§ μμμ
|
| 387 |
+
λμ¬ β (μ΄ν μ§λ¬ΈΒ·λμ¬) β λ§μ§λ§ λ¨κ³μ μ νμ§. μλ¬Έ μμ 보쑴 + λ°νΒ·μ νμ§ μΈμ
|
| 388 |
+
- λλ¨Έμ§ μ§λ¬ΈΒ·λ°νλ μΈκ·Έλ¨ΌνΈλ³ μ€μΈ‘ κ· ν λΆν λ‘ νλ©΄μ μ΅λν μ±μ΄λ€ */
|
| 389 |
function buildScreens(step, fresh) {
|
| 390 |
const c = step.card;
|
| 391 |
let head = '';
|
|
|
|
| 395 |
|
| 396 |
const groups = groupNarration(step.narration);
|
| 397 |
const choices = choicesHtml(step);
|
| 398 |
+
const isPortraitG = (g) => g.lines && g.portrait && !PLAYER_SPEAKERS.has(g.spk);
|
| 399 |
+
let seqIdx = -1; // μΈν°λμ
νμ΄μ§: λ§μ§λ§ μ΄μ λ°ν = μνμ€ μμ
|
| 400 |
if (step.can_chat || step.choices.length) {
|
| 401 |
+
for (let k = groups.length - 1; k >= 0; k--)
|
| 402 |
+
if (isPortraitG(groups[k])) { seqIdx = k; break; }
|
|
|
|
|
|
|
| 403 |
}
|
|
|
|
| 404 |
|
| 405 |
const meas = $('page-measure');
|
| 406 |
+
const budget = Math.max((window.innerHeight || 900) - 130, 320); // μ΅λν κ°λ μ±μ
|
| 407 |
const measure = (html) => { meas.innerHTML = html; return meas.offsetHeight; };
|
| 408 |
|
| 409 |
const screens = [];
|
|
|
|
| 431 |
seg = [];
|
| 432 |
};
|
| 433 |
|
| 434 |
+
const upto = seqIdx >= 0 ? seqIdx : groups.length;
|
| 435 |
+
for (let k = 0; k < upto; k++) {
|
| 436 |
+
const g = groups[k];
|
| 437 |
+
if (isPortraitG(g)) { // μΌλ° μ΄μ λ°ν β μ λ©΄ μ€ν
μ΄μ§ 1μ₯ (μμ¬ μμΉ μ μ§)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 438 |
flushSeg();
|
| 439 |
+
screens.push(stageScreen(g, speechBox(g.spk, g.lines, 'speech'), ''));
|
| 440 |
+
} else {
|
| 441 |
+
const html = g.lines ? charLines(g) : block(g.block);
|
| 442 |
+
seg.push({html, h: measure(html)});
|
| 443 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 444 |
}
|
| 445 |
+
|
| 446 |
+
if (seqIdx < 0) { // μ€ν
μ΄μ§ μνμ€ μμ β μ νμ§λ λ§μ§λ§ νλ©΄ νλ¨
|
| 447 |
+
if (choices) {
|
| 448 |
+
const area = `<div id="choice-area">${choices}</div>`;
|
| 449 |
+
seg.push({html: area, h: measure(area)});
|
| 450 |
+
}
|
| 451 |
+
flushSeg();
|
| 452 |
+
} else { // λ§νμ λ¨κ³ μνμ€ (κ°μ μ΄λ―Έμ§, λ°μ€ λ΄μ©λ§ μ ν)
|
| 453 |
+
flushSeg();
|
| 454 |
+
const sg = groups[seqIdx];
|
| 455 |
+
const steps = [{name: sg.spk, lines: sg.lines, mode: 'speech'}];
|
| 456 |
+
for (let k = seqIdx + 1; k < groups.length; k++) {
|
| 457 |
+
const g = groups[k];
|
| 458 |
+
if (g.lines)
|
| 459 |
+
steps.push({name: PLAYER_SPEAKERS.has(g.spk) ? 'λ' : g.spk,
|
| 460 |
+
lines: g.lines, mode: 'speech'});
|
| 461 |
+
else if (g.block.type === 'dialogue_center')
|
| 462 |
+
steps.push({name: null, lines: [g.block.text], mode: 'center'});
|
| 463 |
+
else
|
| 464 |
+
steps.push({name: null, lines: [g.block.text], mode: 'narr'});
|
| 465 |
+
}
|
| 466 |
+
steps.forEach((s, i) => {
|
| 467 |
+
screens.push(stageScreen(sg, speechBox(s.name, s.lines, s.mode),
|
| 468 |
+
i === steps.length - 1 ? choices : ''));
|
| 469 |
+
});
|
| 470 |
+
}
|
| 471 |
meas.innerHTML = '';
|
| 472 |
|
| 473 |
if (screens.length) screens[0] = head + screens[0]; // ν(θ©±) ν€λλ 첫 νλ©΄μ
|