// screens-b.jsx — Objects, Events, Writing (screens 04-06)
/* ════════════════════════════════════════════════════════════
04 · OBJECTS — clues stitch a case back together
════════════════════════════════════════════════════════════ */
function ObjectGlyph({ kind }) {
// Tiny ink-style sketches for each object kind. Loose, monochrome.
const map = {
"半截竹杖": (
),
"血染袖口": (
),
"旧信": (
),
"铜令牌": (
巡
),
"油纸包": (
),
};
return map[kind] || null;
}
function Screen04Objects({ copy }) {
const s = copy.screens.objects;
return (
}>
← 返回列表
{s.winTitle}⌄
{s.filterStatus}⌄
{s.filterSort}⌄
{s.cards.map((c, i) => (
持有人 {c.holder}
首次出现 {c.firstAt}
当前状态 {c.state}
关联人物 {c.related}
剧情作用 {c.role}
))}
+
添加物件
记录新的线索或旧物
{s.traceHead}
{s.traceFrom}
{s.traceFromHint}
{s.traceArrow}
{s.traceTo}
{s.traceToHint}
);
}
/* ════════════════════════════════════════════════════════════
05 · EVENTS — turn fragments into events and foreshadowing
════════════════════════════════════════════════════════════ */
function Screen05Events({ copy }) {
const s = copy.screens.events;
return (
}>
{s.winTitle}
{s.viewLabel}
{s.views.map((v, i) => (
{v}
))}
{s.filter}
{s.tlHead}
{s.tlSub}
{s.timeline.map((e, i) => (
))}
{s.foreshadowHead}
{s.foreshadowSub}
{s.foreshadows.map((f, i) => (
{f.t}
{f.ch}
))}
{s.causalHead}
{s.causalSub}
{s.causal.map((row, i) => (
{row.map((node, j) => (
{node}
{j < row.length - 1 && → }
))}
))}
{s.indexHead}
{s.indexSub}
{s.index.map((c, i) => {c} )}
);
}
/* ════════════════════════════════════════════════════════════
06 · WRITING — all the structure exists so you can write
════════════════════════════════════════════════════════════ */
function Screen06Writing({ copy }) {
const s = copy.screens.writing;
return (
} className="bw-frame--wide">
{/* Left: book + chapter list */}
{s.bookSelect}
⌄
⚙
{s.tabs.map((t, i) => (
{t}
))}
{s.chapters.map((c, i) => (
{c.active && › }
{c.title}
{c.active && › }
))}
{s.newChapter}
{/* Middle: prose */}
{s.proseTitle}
{s.proseWords}
↺ ↻ ⋯
{s.proseParas.map((p, i) => (
{p}{i === s.proseParas.length - 1 && }
))}
{s.statusBar.words}
{s.statusBar.speed}
· {s.statusBar.today}
{s.statusBar.saved}
{s.statusBar.auto}
{/* Right: quick references */}
{s.refHead}
{s.refs.map((r, i) => (
{r.kind}:{r.title}
{r.hasPortrait &&
}
{r.lines.map((l, j) => {l} )}
))}
☁
{s.endHead}
{s.endNote}
);
}
function ScreenFAQItem({ item, index, isOpen, onToggle }) {
const buttonId = `faq-trigger-${index}`;
const panelId = `faq-panel-${index}`;
return (
);
}
Object.assign(window, {
Screen04Objects, Screen05Events, Screen06Writing, ObjectGlyph,
ScreenFAQItem, Screen08FAQ,
});
/* 07 FAQ - same shell, FAQ grid as body. */
function Screen08FAQ({ copy }) {
const s = copy.screens.faq;
const [openIndex, setOpenIndex] = React.useState(null);
const toggle = (index) => setOpenIndex((current) => current === index ? null : index);
return (
{[0, 1].map((column) => (
{copy.faq.items.map((item, index) => (
index % 2 === column && (
toggle(index)}
/>
)
))}
))}
);
}