npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@inkweave/core

v2.4.2

Published

Core engine for InkWeave - ink story runtime with extension system

Readme

@inkweave/core

English | 中文

InkWeave 的核心运行时引擎,基于 inkjs。提供 ink 故事解析、事件驱动运行时、插件系统和响应式状态管理。

设计理念

Core 包遵循最小依赖、最大扩展原则:

  • 不依赖任何前端框架 — 可在 React、Svelte、Vue 或纯 Node.js 中使用
  • 通过 SPI 暴露扩展点 — 标签处理、内容解析、选择行为、实例补丁均可由外部插件注入
  • 事件驱动 — 所有生命周期节点(初始化、继续、选择、清理、销毁)通过事件总线通信
  • 静态注册表模式 — 插件通过全局注册表注册处理器,Story 实例化时自动加载

目录结构

src/
├── index.ts              统一导出(公共 API)
├── create.ts              工厂函数 createInkStory()
├── types.ts               跨模块纯类型定义
├── constants.ts           常量(Events, CHOICE_SEPARATOR 等)
│
├── events/                事件系统
│   ├── EventEmitter.ts    发布订阅实现
│   └── types.ts           EventEmitterInterface 等
│
├── plugin/                插件子系统
│   ├── types.ts           Plugin, ChoiceRenderer
│   ├── PluginRegistry.ts  全局注册表 + 依赖解析
│   └── PluginLoader.ts    实例级加载器
│
├── story/                 Story 引擎 + 扩展点
│   ├── InkStory.ts        核心运行时类
│   ├── TagHandler.ts      标签处理器(ink → JS 副作用)
│   ├── ContentParser.ts   内容行解析器(文本转换)
│   ├── ChoiceHandler.ts   选择项标签处理器
│   ├── InteractionManager.ts  外部交互映射管理器
│   ├── Patches.ts         实例补丁系统(JS → Engine)
│   └── Externals.ts       外部函数绑定
│
├── command/               命令系统
│   ├── types.ts           Command, ModalContentProps
│   └── CommandRegistry.ts 命令注册 + 执行
│
├── state/                 响应式状态(Zustand)
│   ├── contents.ts        内容 store
│   ├── choices.ts         选择 store
│   └── variables.ts       变量 store
│
└── file/                  文件处理
    ├── FileHandler.ts     FileHandler 接口 + BaseFileHandler
    └── InkjsFileHandler.ts inkjs 桥接器

扩展系统

Core 提供 6 个扩展点,插件通过静态注册表注入行为:

| 扩展点 | 方向 | 注册 API | 用途 | |--------|------|---------|------| | TagHandler | ink → JS | TagHandler.add(name, fn) | 处理 ink 标签副作用(# image, # sound) | | ContentParser | ink → JS | ContentParser.tag(name, fn) | 文本行转换(添加 CSS 类) | | ChoiceHandler | ink → JS | ChoiceHandler.add(type, fn) | 选择项行为定制 | | InteractionManager | JS → Engine | ink.interactionManager.register(name, fn) | 外部交互映射(手势、键盘等驱动选择) | | Patches | JS → Engine | Patches.add(fn, opts) | 运行时扩展 Story 实例属性 | | Externals | ink → JS | Externals.add(id, fn) | 注册 ink 外部函数 |

Clear/Restart 标签TagHandler.isFlushTag() 统一判断,各扩展点不再硬编码标签名。

插件系统

// 注册插件
PluginRegistry.register({
  id: "my-plugin",
  enabledByDefault: true,
  onLoad: () => { TagHandler.add("mytag", handler); },
});

// 控制启用
PluginRegistry.setEnabled({ "my-plugin": false });

PluginRegistry 负责全局注册和配置,PluginLoader 负责实例化时加载启用的插件。依赖解析(正向级联启用、反向级联禁用)由 PluginRegistry.resolveDependencies() 处理。

命令系统

CommandRegistry.add("restart", {
  name: "menu_restart",
  handler: (ink) => ink.restart(),
});

框架适配器通过 getModalContent 注入框架特定的模态框内容。每个框架的入口文件负责将命令注册到 CommandRegistry

框架适配

Core 提供 ChoiceRenderer SPI 接口供框架适配器实现:

export interface ChoiceRenderer {
  register(type: string, component: unknown): void;
}

框架适配器(如 React、Svelte)在各自的入口文件中创建 ChoiceRenderer 实现,注册框架特定的选择项组件。

交互管理器(InteractionManager)

InteractionManager 允许将外部交互(手势、键盘、语音等)映射到故事选择,无需用户点击选项。适用于"王权"类左右滑动游戏、键盘快捷操作等场景。

import { createInkStory, InteractionManager } from "@inkweave/core";

const ink = createInkStory("...");

// 注册映射:将 "swipe-left" 映射到第一个选项
ink.interactionManager.register("swipe-left", (choices) => choices[0]?.index ?? null);
ink.interactionManager.register("swipe-right", (choices) => choices[1]?.index ?? null);

// 外部检测到滑动后触发
someSwipeDetector.onSwipeLeft(() => ink.interactionManager.trigger("swipe-left"));

trigger() 会自动调用 ink.choose(index),无需手动处理。

内置预设

InteractionManager.presets.left   // 第一个选项
InteractionManager.presets.right  // 第二个选项
InteractionManager.presets.first  // 第一个选项
InteractionManager.presets.second // 第二个选项

API

| 方法 | 说明 | |------|------| | register(name, resolver) | 注册交互映射 | | trigger(name) | 触发交互,返回是否成功 | | unregister(name) | 注销交互 | | has(name) | 检查是否已注册 | | getRegistered() | 获取已注册的名称列表 | | clear() | 清空所有注册 |

基本用法

import { createInkStory } from "@inkweave/core";

const ink = createInkStory("Hello World\n+ [Choice] -> end");
ink.continue();                           // 推进故事
const choices = ink.choices;              // 获取当前选项
ink.choose(0);                            // 选择第 0 项

依赖

  • inkjs — ink 编译器/运行时
  • zustand — 响应式状态管理(仅用于 stores)