@reiwuzen/tabria
v1.3.0
Published
pnpm publish --access public
Readme
Tabria
Core idea
Tabria separates state management from UI rendering.
UI (React / Vue / etc)
|
Tabria (state engine)
|
Application logicThe library only handles state transitions, such as:
- opening tabs
- closing tabs
- switching tabs
- navigating between pages inside a tab
- restoring closed tabs from closed history
Your UI framework simply subscribes to the state and renders it.
Main concepts
Workspace
The global state container.
Workspace
activeTab
limits { openTabs, closedTabs, totalTabs }
tabs.openOrder[]
tabs.closedOrder[]
tabs.storage{}Tab
Represents a working context.
Tab
id
title
createdAt
updatedAt
runtimeState
pages.order[]
pages.storage{}
currentPageIdA tab can contain multiple pages.
Page
A navigation entry inside a tab.
Page
id
key
url
type
view
state
metaExample:
type: "reader"
view: "vertical"
state: { page: 12 }Page History
Each tab maintains its own ordered page history.
Example navigation:
Library
-> Manga
-> ReaderHistory representation:
[Library, Manga, Reader]Operations include:
pushPagepopPagereplacePageupdatePageState
Core capabilities
Tabria provides primitives for:
Tab management
createWorkspacecreateTabopenTabaddTabactivateTabcloseTabmoveTabreopenClosedTab
Page navigation
createPagepushPagepopPagereplacePageupdatePageState
Closed tab history
tabs.closedOrderreopenClosedTab()
Design goals
Tabria is designed to be:
- headless: no UI components
- framework-agnostic: works with React, Vue, Svelte, etc.
- predictable: pure state transition functions
- portable: usable in web, desktop (Tauri/Electron), or Node apps
Installation
pnpm add @reiwuzen/tabrianpm install @reiwuzen/tabriaUsage
import {
createWorkspace,
createTab,
createPage,
addTab,
openTab,
pushPage,
updatePageState,
getActiveTab,
getActivePage,
type Workspace,
} from "@reiwuzen/tabria";
let workspace: Workspace = createWorkspace();
const tab = createTab({ title: "Library" });
workspace = addTab(workspace, tab);
workspace = openTab(workspace, { title: "Manga" });
const library = createPage({
type: "library",
view: "grid",
});
workspace = pushPage(workspace, tab.id, library);
workspace = updatePageState(workspace, tab.id, {
section: "favorites",
});
const activeTab = getActiveTab(workspace);
const activePage = getActivePage(workspace);
// Customize or bypass limits (null disables a limit)
workspace = createWorkspace({
limits: {
openTabs: 20,
closedTabs: 20,
// openTabs: null,
},
});API Reference
Grouped exports (optional)
coreactionsselectorsapi(merged convenience API)
Types
WorkspaceTabTabIDTabStatePagePageIDPageStateJsonObj
core
createTabcreatePagecreateWorkspace
actions
openTabaddTabactivateTabcloseTabmoveTabreopenClosedTabpushPagepopPagereplacePageupdatePageState
selectors
getTabsgetTabgetActiveTabgetActivePagegetPageStack
Selectors are read-only helpers for view code.
Use actions for any state updates.
Behavior Notes
- All operations are pure: they return new state objects instead of mutating inputs.
- Selectors are pure read helpers and should be used for view/derived access only.
- If an operation targets a non-existent tab, the original state is returned.
closeTabmoves the tab ID fromtabs.openOrdertotabs.closedOrder, setsclosedAt, and marks the tabruntimeStateasdiscarded.reopenClosedTab()restores the most recently closed tab by default.reopenClosedTab(tabId)restores a specific closed tab when found.- Workspace defaults are
openTabs: 20,closedTabs: 20, andtotalTabs: 40. - Set any workspace limit to
nullwhen creating a workspace to bypass that limit. totalTabsis derived internally asopenTabs + closedTabsand is not user-configurable.
Development
pnpm install
pnpm buildBuild output:
- ESM:
dist/index.js - CJS:
dist/index.cjs - Types:
dist/index.d.ts
License
ISC
