NarraLeaf

NVL 容器

NvlContainer 等 NVL 模式组件及 useNvl 系列状态钩子

NvlContainer 组件渲染 NVL(小说模式)的对话框叠加层,以可滚动列表形式展示多条对话。可直接使用 DefaultNvlContainer,或组合 NvlContainerNametagTexts 构建自定义组件

示例

  1. 导入组件
import { useEffect } from "react";
import { DefaultNvlContainer, useGame } from "narraleaf-react";
  1. 使用默认组件(或通过 game.configure 自定义)
function App() {
    const game = useGame();

    useEffect(() => {
        game.configure({
            nvlDialog: DefaultNvlContainer,
        });
    }, []);

    return /* ... */
}
  1. 自定义组件
import {
    NvlContainer,
    Nametag,
    Texts,
    useGame,
    type INvlContainerProps,
} from "narraleaf-react";

function CustomNvlContainer({ dialogs = [] }: INvlContainerProps) {
    return (
        <NvlContainer className="bg-black/80 text-white p-16">
            {dialogs.map((d) => (
                <div key={d.entry.id} className="space-y-2">
                    {d.entry.character && <Nametag entry={d.entry} />}
                    <Texts
                        entry={d.entry}
                        gameState={d.gameState}
                        words={d.words}
                        useTypeEffect={d.useTypeEffect}
                        isActive={d.isActive}
                    />
                </div>
            ))}
        </NvlContainer>
    );
}

function App() {
    const game = useGame();
    useEffect(() => {
        game.configure({ nvlDialog: CustomNvlContainer });
    }, []);
    return /* ... */
}

组件

NvlContainer

NVL 模式的外层容器,处理可见性、过渡动画和宽高比缩放

  • children?: React.ReactNode - 子元素(对话列表)
  • className?: string - 类名
  • style?: React.CSSProperties - 样式

DefaultNvlContainer

默认 slot 组件,接收播放器传入的 dialogsrenderDialogItem,用 NametagTexts 渲染每条对话

  • dialogs?: NvlDialogProxy[] - 对话条目列表
  • renderDialogItem?: NvlDialogItemRenderer - 可选,仅需定制每条对话布局时使用

NvlDialogList

NvlDialogList 负责渲染条目本身。DefaultNvlContainer 用的就是它;基于 NvlContainer 自建的容器需要它(或自己的等价物)来画出列表。每个条目都被包进对话上下文,因此在其中渲染的 NametagTexts 读的是该条目,而不是 ADV 那一行

  • children?: React.ReactNode - 自定义的条目元素。它会按条目逐个克隆,并注入 entryindex
  • renderDialogItem?: NvlDialogItemRenderer - 优先级高于 children。接收 { entry, index, isActive, nametag, texts }
  • className?: string / style?: React.CSSProperties - 应用在列表容器上

childrenrenderDialogItem 都不传时,每个条目由 DefaultNvlDialogItem 绘制

DefaultNvlDialogItem

默认的条目样式:说话人名字加一个冒号,后面跟着这一行台词。可以在自定义列表里当兜底用,也可以当作参考实现

  • entry: NvlDialogEntry - 要绘制的条目
  • index: number - 它在列表中的序号
  • texts?: React.ReactNode - 替换渲染出来的正文,名牌不受影响
  • className?: string / style?: React.CSSProperties

属性

INvlContainerProps

传入 NVL slot 组件的属性:

  • dialogs?: NvlDialogProxy[] - 包含已求值文本和状态的对话条目数组
  • renderDialogItem?: NvlDialogItemRenderer - 可选,接收 { entry, index, isActive, nametag, texts }

NvlDialogItemRenderProps

传入 renderDialogItem 的属性:

  • entry: NvlDialogEntry - 对话条目
  • index: number - 列表索引
  • isActive: boolean - 是否为当前活动对话
  • nametag: React.ReactNode - 预渲染的角色名
  • texts: React.ReactNode - 预渲染的文本内容

使用 renderDialogItem 定制布局

只需调整每条对话的展示(如加边框、淡化非活动项)时,可包装 DefaultNvlContainer 并传入 renderDialogItem

function CustomNvlWithRenderer({ dialogs }: INvlContainerProps) {
    return (
        <DefaultNvlContainer
            dialogs={dialogs}
            renderDialogItem={({ entry, index, isActive, nametag, texts }) => (
                <div className={isActive ? "opacity-100" : "opacity-60"}>
                    {nametag}
                    <div className="border-l-4 border-blue-500 pl-2">{texts}</div>
                </div>
            )}
        />
    );
}

// 注册:game.configure({ nvlDialog: CustomNvlWithRenderer });

Hooks

有四个钩子用于读取 NVL 状态。它们都是不带参数的上下文读取器,返回的就是播放器挂载的那个 NvlProvider 所持有的值

该 provider 包住的是舞台、对话与 NVL 叠层——因此自定义 NVL 容器、自定义对话框,以及舞台渲染的其他任何东西都可以调用它们。由页面路由渲染的页面位于该 provider 之外,拿到的是惰性的默认值:未激活、不可见、没有条目

import { useIsNvlMode, useNvlDialogs } from "narraleaf-react";

function LineCount() {
    const inNvl = useIsNvlMode();
    const dialogs = useNvlDialogs();

    if (!inNvl) return null;
    return <span>{dialogs.length} 行</span>;
}

useNvl

返回整个 NVL 上下文值

  • state: NvlState - 完整记录:activevisiblesessionIddialogsoptionsactiveDialogIdphase"idle" | "typing" | "awaitAdvance")、pendingAdvanceisTyping
  • dialogs: NvlDialogEntry[] - 当前列表中的条目
  • isActive: boolean - 游戏是否处于 NVL 模式
  • isVisible: boolean - NVL 层是否显示在屏幕上。会话在该层被隐藏期间仍然是激活的,所以这两件事问的不是同一个问题
  • transitionOptions: Partial<TransformDefinitions.CommonTransformProps> | null - 当前生效的显示或隐藏过渡的 transform 选项,没有则为 null

useNvlDialogs

useNvl().dialogs——当前列表中的条目

useIsNvlMode

useNvl().isActive——游戏是否处于 NVL 模式

useIsNvlVisible

useNvl().isVisible——NVL 层是否显示在屏幕上

本页目录