Files
2026-09-09 14:47:29 +08:00

136 lines
3.7 KiB
JavaScript

const COMIC_READER_LAYOUT_CONTRACT = Object.freeze({
contractId: 'story-first-three-frame-v1',
columns: Object.freeze({
art: 3,
content: 2,
}),
frames: Object.freeze({
front: Object.freeze({
imageMode: 'aspectFit',
initialControls: 'none',
revealGesture: 'single-tap',
revealedControls: 'topbar-only',
}),
back: Object.freeze({
art: 2,
content: 3,
imageMode: 'aspectFit',
}),
interaction: Object.freeze({
art: 2,
content: 3,
imageMode: 'aspectFit',
choiceCount: 2,
choiceDirection: 'vertical',
}),
}),
minimumControlPx: 48,
compactHeightMaxPx: 620,
fontModes: Object.freeze({
standard: 'large',
xlarge: 'xlarge',
}),
audioSlot: Object.freeze({
placement: 'unified-topbar',
minimumHeightPx: 48,
textModeLabel: '直接看文字',
readyLabel: '听一听',
playingLabel: '暂停',
replayLabel: '重听',
usesPageAudioCueIds: true,
}),
artOverlayPolicy: Object.freeze({
event: 'transparent-actor-hotspot-only',
emotion: 'transparent-actor-hotspot-only',
}),
})
const COMIC_PAGE_TEMPLATE_BY_TYPE = Object.freeze({
cover: 'chapter-ensemble',
ensemble: 'chapter-ensemble',
event: 'actor-event',
emotion: 'emotion-interaction',
memory: 'memory-cliffhanger',
})
const COMIC_PRESENTATION_BY_CHAPTER = Object.freeze({
'S01-C01': 'front-back-comic-v1',
'S01-C02': 'front-back-comic-v1',
'S01-C03': 'front-back-comic-v1',
'S01-C04': 'front-back-comic-v1',
'S01-C05': 'front-back-comic-v1',
'S01-C06': 'front-back-comic-v1',
'S01-C07': 'front-back-comic-v1',
'S01-C08': 'front-back-comic-v1',
'S01-C09': 'front-back-comic-v1',
'S01-C10': 'front-back-comic-v1',
'S01-C11': 'front-back-comic-v1',
'S01-C12': 'front-back-comic-v1',
'S01-C13': 'front-back-comic-v1',
'S01-C14': 'front-back-comic-v1',
'S01-C15': 'front-back-comic-v1',
})
function getComicPresentationMode(chapterId) {
return COMIC_PRESENTATION_BY_CHAPTER[String(chapterId || '')]
|| 'legacy-60-40-v1'
}
const COMIC_PAGE_LAYOUT_EXCEPTIONS = Object.freeze({
'S01-C09-P03': Object.freeze({
layoutVariant: 'standard-landscape-60-40',
behavior: 'key-safe-actor-hotspot',
actorHotspot: Object.freeze({ x: 16, y: 3, w: 56, h: 82 }),
reason: '人物仍可整片点击,但透明热区须止于画面下方钥匙带之前。',
}),
})
// These pages keep the full reviewed frame and add one read-only crop from
// the same asset as a second comic panel. This is an art-reading aid, not a
// new interaction or a page-layout exception.
const COMIC_FIXED_DETAIL_INSET_PAGE_IDS = Object.freeze([
'S01-C04-P04',
'S01-C09-P01',
'S01-C09-P05',
'S01-C09-P06',
'S01-C10-P07',
'S01-C13-P04',
'S01-C15-P05',
'S01-C15-P08',
])
function allowsFixedDetailInset(pageId) {
return COMIC_FIXED_DETAIL_INSET_PAGE_IDS.indexOf(
String(pageId || ''),
) >= 0
}
function getComicPageLayoutException(pageId) {
return COMIC_PAGE_LAYOUT_EXCEPTIONS[String(pageId || '')] || null
}
function resolveComicPageLayout(pageId, type) {
const templateKey = COMIC_PAGE_TEMPLATE_BY_TYPE[type] || 'chapter-ensemble'
const exception = getComicPageLayoutException(pageId)
return {
templateKey,
layoutVariant: exception
? exception.layoutVariant
: 'standard-landscape-60-40',
layoutExceptionId: exception ? String(pageId) : '',
layoutExceptionBehavior: exception ? exception.behavior : '',
}
}
module.exports = {
COMIC_READER_LAYOUT_CONTRACT,
COMIC_PRESENTATION_BY_CHAPTER,
COMIC_PAGE_TEMPLATE_BY_TYPE,
COMIC_PAGE_LAYOUT_EXCEPTIONS,
COMIC_FIXED_DETAIL_INSET_PAGE_IDS,
allowsFixedDetailInset,
getComicPresentationMode,
getComicPageLayoutException,
resolveComicPageLayout,
}