pixi-vn-history

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Pixi'VN History module

Pixi'VN History 模块

Official docs: https://pixi-vn.com/start/history — this skill also documents
goBackMode
and the real-world go-back/backlog patterns not detailed on the public page. It sticks to the public
stepHistory
API a game actually calls, not how it's implemented internally.
官方文档:https://pixi-vn.com/start/history —— 此 skill 还记录了
goBackMode
,以及 公开页面中未详述的实际返回/backlog 模式。它只关注游戏实际调用的公开
stepHistory
API,而不涉及其内部实现方式。

When to use this skill

何时使用此 skill

Load this skill whenever a task involves: letting the player undo the last line(s) of dialogue ("go back" / "rewind" button), reading/rendering the narration backlog (a "history log" or "scrollback" panel), deciding how much can be undone after a save/load, or tuning how coarse/fine-grained "going back" feels (line-by-line vs paragraph-by-paragraph).
每当任务涉及以下内容时,应加载此 skill:允许玩家撤销最近一行或多行对话(“返回”/“倒带”按钮)、读取或渲染 narration backlog(“历史日志”或“回滚记录”面板)、决定存档/读档后最多可以撤销多少内容,或调节“返回”操作的粗细粒度(逐行返回还是逐段返回)。

Mental model

心智模型

Every time narration advances one step, Pixi'VN records that moment so the player can later go back to it.
stepHistory
is the module/singleton that manages these recorded steps: going back to an earlier one, and reading back what was shown at each one (for a backlog/scrollback UI). The manager instance is the named export
stepHistory
(re-exported at the package root, so
import { stepHistory } from "@drincs/pixi-vn"
works; also reachable as
.history
on the package's default export object).
每次 narration 推进一个 step 时,Pixi'VN 都会记录这一时刻,以便玩家之后返回到该位置。
stepHistory
是管理这些已记录 step 的模块/单例:它可以返回到较早的 step,并读取每个 step 所显示的内容(用于 backlog/scrollback UI)。管理器实例通过名为
stepHistory
的 named export 导出(也会在包根目录重新导出,因此
import { stepHistory } from "@drincs/pixi-vn"
可用;也可通过包默认导出对象的
.history
访问)。

Checkpoints and
goBackMode

检查点与
goBackMode

Not every step is necessarily something the player can go back to on its own — that depends on
stepHistory.goBackMode
(type
HistoryGoBackModeType = "step" | "paragraph"
, default
"step"
):
  • "step"
    (default): every single step can be gone back to individually. Matches classic VN-style "click to undo one line" behavior.
  • "paragraph"
    : only a step that starts a new paragraph (a
    call
    /
    jump
    happened, or a called label returned), proposes choices, or requires input can be gone back to on its own — everything else replays together with the next such step. Consequence: calling
    back()
    from anywhere inside paragraph N jumps all the way to the start of paragraph N-1, not to "one line up" — the whole of paragraph N-1 gets replayed. Step 0 is always a valid target regardless of mode.
Set it directly on the manager:
ts
import { stepHistory } from "@drincs/pixi-vn";

stepHistory.goBackMode = "paragraph"; // or "step" (default)
并非每个 step 都能由玩家单独返回,具体取决于
stepHistory.goBackMode
(类型为
HistoryGoBackModeType = "step" | "paragraph"
,默认值为
"step"
):
  • "step"
    (默认):每个单独的 step 都可以单独返回。这符合经典 VN 风格的“点击撤销一行”行为。
  • "paragraph"
    :只有开启新段落的 step(发生了
    call
    /
    jump
    ,或被调用的 label 已返回)、提供选项的 step,或需要输入的 step 才能单独返回——其他所有 step 都会与下一个此类 step 一起重放。因此,在段落 N 内的任意位置调用
    back()
    ,都会直接跳转到段落 N-1 的开头,而不是“向上一行”——段落 N-1 的全部内容都会被重新播放。无论采用哪种模式,Step 0 始终是有效目标。
可以直接在管理器上设置:
ts
import { stepHistory } from "@drincs/pixi-vn";

stepHistory.goBackMode = "paragraph"; // or "step" (default)

Going back

返回操作

Trigger "go back" from game/UI code with
stepHistory.back(props)
:
ts
import { stepHistory } from "@drincs/pixi-vn";

// In a click handler for a "back" / "undo" button:
await stepHistory.back(props); // props: the StepLabelPropsType passed into your step/label functions
// go back 3 steps (or 3 checkpoints, in "paragraph" mode) at once:
await stepHistory.back(props, { steps: 3 });
This is also how Pixi'VN itself handles the player pressing a system/browser "back" gesture, and
Game.addOnError
examples show calling
await stepHistory.back(props)
inside an error handler to roll back to the previous step on failure.
Notes on
back()
:
  • steps
    must be a finite number
    > 0
    ; anything else logs a warning and does nothing.
  • If narration is mid-step, or another
    back()
    /
    continue()
    is already in flight, the request is queued automatically rather than executed immediately — safe to call from an event handler without extra guarding.
  • Use
    stepHistory.canGoBack
    (boolean) to decide whether to show/enable a "go back" button. In
    "step"
    mode this is
    true
    as soon as there's at least one earlier step to return to; in
    "paragraph"
    mode it becomes
    true
    less often, since most steps aren't individually go-back-able.
  • stepHistory.blockGoBack()
    disables
    back()
    from this point on, permanently (only takes effect when no step is currently running) — useful right after an irreversible action (e.g. a choice with real consequences) if the player shouldn't be able to rewind past it.
在游戏/UI 代码中,通过
stepHistory.back(props)
触发“返回”:
ts
import { stepHistory } from "@drincs/pixi-vn";

// In a click handler for a "back" / "undo" button:
await stepHistory.back(props); // props: the StepLabelPropsType passed into your step/label functions
// go back 3 steps (or 3 checkpoints, in "paragraph" mode) at once:
await stepHistory.back(props, { steps: 3 });
Pixi'VN 自身处理玩家按下系统/浏览器“返回”操作时也采用这种方式。
Game.addOnError
示例还展示了如何在错误处理器中调用
await stepHistory.back(props)
,以便失败时回滚到上一个 step。
关于
back()
的注意事项:
  • steps
    必须是大于 0 的有限数值;其他值会触发警告且不执行任何操作。
  • 如果 narration 正在某个 step 中执行,或已有另一个
    back()
    /
    continue()
    正在执行,该请求会自动排队,而不是立即执行——因此可以安全地从事件处理器调用,无需额外防护。
  • 使用
    stepHistory.canGoBack
    (布尔值)决定是否显示或启用“返回”按钮。在
    "step"
    模式下,只要存在至少一个可返回的较早 step,它就会变为
    true
    ;在
    "paragraph"
    模式下,大多数 step 无法单独返回,因此它变为
    true
    的频率更低。
  • stepHistory.blockGoBack()
    会从当前时点开始永久禁用
    back()
    (仅在当前没有 step 正在运行时生效)——如果玩家不应越过具有不可逆后果的操作(例如会产生真实影响的选项),可以在该操作完成后立即使用它。

Reading step/narration history (for a backlog UI)

读取 step/narration 历史(用于 backlog UI)

In Pixi'VN's own terminology, "narrative history" is the list of all dialogues, choices, etc. that have been shown to the player — that's what these getters expose:
  • stepHistory.narrativeHistory: NarrationHistory[]
    — every recorded step across the whole playthrough, in order, each with
    dialogue
    ,
    choices
    ,
    playerMadeChoice
    ,
    inputValue
    ,
    stepIndex
    ,
    openedLabelsNumber
    . This is the property the official docs' "Get" section shows:
    ts
    import { stepHistory } from "@drincs/pixi-vn";
    const dialogues: NarrationHistory[] = stepHistory.narrativeHistory;
    (The public docs page's code sample types this as
    NarrativeHistory[]
    — that type isn't actually exported; the real export from
    @drincs/pixi-vn
    is
    NarrationHistory
    , confirmed in
    src/narration/interfaces/NarrationHistory.ts
    and used as the return type in
    src/history/HistoryManager.ts
    . Trust the source/this skill's
    NarrationHistory
    naming over that docs typo.)
  • stepHistory.currentLabelHistory: NarrationHistory[]
    — just the "current page": steps belonging to the current label and its child labels, reset every time
    narration.jump
    is used. Useful for a per-scene backlog.
  • stepHistory.currentPageParagraphs: NarrationHistory[][]
    —
    currentLabelHistory
    grouped into paragraphs (a new paragraph starts whenever
    openedLabelsNumber
    changes), regardless of
    goBackMode
    .
  • stepHistory.removeNarrativeHistory(itemsNumber?)
    — deletes all narrative history if called with no argument, or trims just the oldest
    itemsNumber
    entries if you pass a number:
    ts
    stepHistory.removeNarrativeHistory(); // delete everything
    stepHistory.removeNarrativeHistory(2); // delete the first 2 elements
按照 Pixi'VN 自身的术语,“narrative history(叙事历史)”是已向玩家显示的所有对话、选项等内容的列表——以下 getter 正是用来暴露这些信息的:
  • stepHistory.narrativeHistory: NarrationHistory[]
    ——整个游戏流程中按顺序记录的所有 step,每项包含
    dialogue
    、
    choices
    、
    playerMadeChoice
    、
    inputValue
    、
    stepIndex
    、
    openedLabelsNumber
    。这是官方文档“Get”部分所展示的属性:
    ts
    import { stepHistory } from "@drincs/pixi-vn";
    const dialogues: NarrationHistory[] = stepHistory.narrativeHistory;
    (公开文档页面的代码示例将其类型写为
    NarrativeHistory[]
    ,但该类型实际上并未导出;
    @drincs/pixi-vn
    真正导出的类型是
    NarrationHistory
    ,这一点可从
    src/narration/interfaces/NarrationHistory.ts
    确认,并且
    src/history/HistoryManager.ts
    也将其用作返回类型。请以源码/此 skill 中的
    NarrationHistory
    命名为准,而不是文档中的拼写错误。)
  • stepHistory.currentLabelHistory: NarrationHistory[]
    ——仅包含当前“页面”:属于当前 label 及其子 label 的 step;每次使用
    narration.jump
    时都会重置。适合用于按场景划分的 backlog。
  • stepHistory.currentPageParagraphs: NarrationHistory[][]
    ——将
    currentLabelHistory
    按段落分组(每当
    openedLabelsNumber
    发生变化时就开始一个新段落),且不受
    goBackMode
    影响。
  • stepHistory.removeNarrativeHistory(itemsNumber?)
    ——不传参数时删除全部叙事历史;传入数字时,仅删除最早的
    itemsNumber
    条记录:
    ts
    stepHistory.removeNarrativeHistory(); // delete everything
    stepHistory.removeNarrativeHistory(2); // delete the first 2 elements

Save/load and step limits

存档/读档与 step 限制

  • stepHistory.stepLimitSaved
    (default
    20
    ) caps how many steps' worth of go-back history are kept when exporting for a save file (consumed by
    Game.exportGameState()
    — see
    pixi-vn-saves
    ). Older steps beyond that limit are dropped from what's exported — meaning after loading a save, the player can only go back up to
    stepLimitSaved
    steps, even though
    narrativeHistory
    itself may show more (older entries lose the ability to be "gone back to" but keep their display text — the docs put it as "only essential information from older steps is kept"). Set it to
    Infinity
    to disable the limit and keep everything go-back-able.
  • stepHistory.clear()
    wipes all history (called by
    Game.clear()
    and at the start of
    Game.restoreGameState()
    ).
  • stepHistory.stepLimitSaved
    (默认值为
    20
    )限制导出存档文件时保留多少个 step 的返回历史(由
    Game.exportGameState()
    使用——参见
    pixi-vn-saves
    )。超出该限制的较早 step 不会包含在导出数据中——这意味着读档后,玩家最多只能返回
    stepLimitSaved
    个 step,即使
    narrativeHistory
    本身仍可能显示更多记录(较早的记录会失去被“返回到”的能力,但仍保留其显示文本——文档将其表述为“仅保留较早 step 的必要信息”)。将其设为
    Infinity
    可禁用限制,并让所有 step 都保持可返回状态。
  • stepHistory.clear()
    会清除全部历史(由
    Game.clear()
    调用,也会在
    Game.restoreGameState()
    开始时调用)。

Real-world usage (official React template)

实际用法(官方 React 模板)

The official "TS narration + React" template (what
npm create pixi-vn@latest
scaffolds, source at
pixi-vn-react-template
) shows one concrete way to wire up "go back" and a backlog UI. This is that template's convention, not a library requirement — the APIs below (
canGoBack
,
back()
,
narrativeHistory
) are all you actually need; the loading-state/React-Query/i18n wrapping around them is the template's own app architecture.
Guarding the "back" button/gesture —
src/lib/query/narration-query.ts
exposes
stepHistory.canGoBack
through a React Query hook so the UI can enable/disable a back button reactively:
ts
export function useQueryCanGoBack() {
  return useQuery({
    queryKey: [NARRATION_DATA_USE_QUERY_KEY, "can_go_back_use_query_key"],
    queryFn: async () => stepHistory.canGoBack,
  });
}
Triggering
back()
—
src/lib/hooks/narration-hooks.ts
wraps the call in a loading-state guard and error handling, then invalidates cached interface data (dialogue/choices/etc. queries) once it settles:
ts
const goBack = useCallback(async () => {
  if (hasOpenMenu) return;
  GameStatus.setLoading(true);
  return stepHistory
    .back(gameProps) // gameProps: the app's StepLabelProps-shaped object from useGameProps()
    .then(() => {
      GameStatus.setLoading(false);
      gameProps.invalidateInterfaceData();
    })
    .catch((e) => {
      GameStatus.setLoading(false);
      console.error(e);
    });
}, [gameProps, hasOpenMenu]);
The same
stepHistory.back
is also bound directly as a handler elsewhere (
src/lib/hooks/quick-tools-hooks.ts
), e.g.
stepHistory.back.bind(stepHistory)
fired on a scroll-down gesture in a "quick tools" wheel — confirming
back(props)
is safe to call straight from arbitrary UI event handlers, not just a dedicated button.
Backlog/history-log UI —
src/lib/query/narration-query.ts
's
useQueryNarrativeHistory({ searchString })
maps
stepHistory.narrativeHistory
into a flat display model per step (translating character name/dialogue text, passing through
choices
and
inputValue
as-is), then filters by a search string against character name and text:
ts
const dialogues = stepHistory.narrativeHistory.map((step) => ({
  character: /* step.dialogue?.character name, translated */,
  text: /* step.dialogue?.text, translated */,
  icon: /* character icon, if any */,
  choices: step.choices,
  inputValue: step.inputValue,
}));
This is the shape a backlog/scrollback panel typically wants: one row per narrative-history entry with speaker, line, any choices offered, and any input the player typed at that step.
官方“TS narration + React”模板(即
npm create pixi-vn@latest
创建的项目,源码位于
pixi-vn-react-template
)展示了一种连接“返回”功能和 backlog UI 的具体方式。这是该模板自身的约定,而不是库的强制要求——实际所需的 API 只有以下这些(
canGoBack
、
back()
、
narrativeHistory
);外层的加载状态、React Query 和 i18n 封装属于模板自身的应用架构。
控制“返回”按钮/手势——
src/lib/query/narration-query.ts
通过 React Query hook 暴露
stepHistory.canGoBack
,使 UI 可以响应式地启用或禁用返回按钮:
ts
export function useQueryCanGoBack() {
  return useQuery({
    queryKey: [NARRATION_DATA_USE_QUERY_KEY, "can_go_back_use_query_key"],
    queryFn: async () => stepHistory.canGoBack,
  });
}
触发
back()
——
src/lib/hooks/narration-hooks.ts
在调用外层加入加载状态防护和错误处理,并在操作完成后使缓存的界面数据(对话/选项等查询)失效:
ts
const goBack = useCallback(async () => {
  if (hasOpenMenu) return;
  GameStatus.setLoading(true);
  return stepHistory
    .back(gameProps) // gameProps: the app's StepLabelProps-shaped object from useGameProps()
    .then(() => {
      GameStatus.setLoading(false);
      gameProps.invalidateInterfaceData();
    })
    .catch((e) => {
      GameStatus.setLoading(false);
      console.error(e);
    });
}, [gameProps, hasOpenMenu]);
其他地方也会将同一个
stepHistory.back
直接绑定为事件处理器,例如
src/lib/hooks/quick-tools-hooks.ts
中会在“快捷工具”滚轮发生向下滚动时触发
stepHistory.back.bind(stepHistory)
——这说明可以安全地从任意 UI 事件处理器直接调用
back(props)
,而不只是用于专门的按钮。
Backlog/history-log UI——
src/lib/query/narration-query.ts
中的
useQueryNarrativeHistory({ searchString })
会将
stepHistory.narrativeHistory
映射为每个 step 对应的扁平显示模型(翻译角色名称和对话文本,并原样传递
choices
与
inputValue
),然后根据搜索字符串筛选角色名称和文本:
ts
const dialogues = stepHistory.narrativeHistory.map((step) => ({
  character: /* step.dialogue?.character name, translated */,
  text: /* step.dialogue?.text, translated */,
  icon: /* character icon, if any */,
  choices: step.choices,
  inputValue: step.inputValue,
}));
这正是 backlog/scrollback 面板通常所需的数据结构:每条叙事历史记录对应一行,其中包含说话者、对白、提供过的选项,以及玩家在该 step 输入的内容。

Gotchas

注意事项与陷阱

  • Canvas is optional. If
    Game.init()
    was called without a canvas element (headless/engine-only usage), canvas state is never captured or restored during
    back()
    — going back only affects storage, sound, path and narration position in that setup.
  • "paragraph"
    mode replays, it doesn't rewind precisely.
    back()
    in
    "paragraph"
    mode always lands on the start of the previous paragraph/checkpoint, undoing everything since then in one jump — there is no way to undo "just the last line" within a paragraph.
  • There is no "redo" after
    back()
    .
    Going back to an earlier step discards every step recorded after the point you land on, rather than merely skipping past them — so there's nothing to go forward to again.
  • stepHistory.get(stepIndex)
    can return
    undefined
    even for a valid, recorded step index if that step carried no dialogue/choices/input worth showing (an internal bookkeeping-only entry) — check for
    undefined
    rather than assuming every index in range has a step to render.
  • If restoring a step fails (e.g. corrupted or incompatible save data),
    back()
    logs an error and leaves the game state unchanged rather than throwing.
  • Canvas 是可选的。 如果调用
    Game.init()
    时没有传入 canvas 元素(用于 headless/engine-only 场景),那么在
    back()
    期间不会捕获或恢复 canvas 状态——在这种情况下,返回操作只会影响 storage、声音、路径和 narration 位置。
  • “paragraph”模式执行的是重放,而不是精确倒带。 在
    "paragraph"
    模式下,
    back()
    总是会定位到上一个段落/检查点的_起点_,并通过一次跳转撤销从那里开始的全部内容——无法在段落内只撤销“最后一行”。
  • 调用
    back()
    后无法“重做”。
    返回到较早的 step 时,该位置之后记录的每个 step 都会被丢弃,而不只是暂时跳过——因此之后无法再次向前进入这些 step。
  • 即使
    stepIndex
    是有效的已记录索引,
    stepHistory.get(stepIndex)
    也可能返回
    undefined
    。
    如果该 step 没有值得显示的对话、选项或输入内容,仅是一条内部记录,则会出现这种情况——应检查
    undefined
    ,而不要假设范围内的每个索引都有可渲染的 step。
  • 如果恢复某个 step 失败(例如存档数据损坏或不兼容),
    back()
    会记录错误并保持游戏状态不变,而不是抛出异常。

Related skills

相关 skill

pixi-vn-getting-started, pixi-vn-saves, pixi-vn-narration, pixi-vn-canvas, pixi-vn-storage
pixi-vn-getting-started, pixi-vn-saves, pixi-vn-narration, pixi-vn-canvas, pixi-vn-storage