@moduletrace/twui (0.1.8)
Installation
@moduletrace:registry=https://git.tben.me/api/packages/Moduletrace/npm/npm install @moduletrace/[email protected]"@moduletrace/twui": "0.1.8"About this package
@moduletrace/twui
A modular skeletal React component framework for Tailwind CSS v4.
Install
The package is published to the Moduletrace Gitea registry. Add this to your project's .npmrc:
@moduletrace:registry=https://git.tben.me/api/packages/moduletrace/npm/
Then install the package with its required peers:
bun add @moduletrace/twui react react-dom lucide-react tailwindcss
Optional peers
Only install these if you use the matching components:
| Feature | Packages |
|---|---|
editors/AceEditor |
ace-builds |
elements/RemoteCodeBlock, mdx/markdown/* |
next-mdx-remote, remark-gfm, rehype-prism-plus |
mdx/mdx-components, next-js/hooks/useMDXComponents |
@types/mdx (dev) |
elements/ai/* |
react-markdown, remark-gfm (+ openai types for the legacy AIPrompt* blocks) |
editors/TinyMCE loads TinyMCE from a URL at runtime. Set it with the base_url prop, or with the NEXT_PUBLIC_TINYMCE_BASE_URL / BUNEXT_PUBLIC_TINYMCE_BASE_URL public env vars.
CSS
Import base.css once in your main stylesheet. It includes @import "tailwindcss", the default theme tokens, and an @source rule so Tailwind generates the classes the components use. Override tokens in your own @theme inline block:
@import "@moduletrace/twui/base.css";
@theme inline {
--color-primary: #e2b433;
--color-primary-hover: #9e7503;
--color-primary-dark: #e2b433;
--color-primary-dark-hover: #f7cc59;
/* ...see base.css for the full list of tokens */
}
.twui-button-general {
@apply rounded-full;
}
Usage
Import from the root entry:
import { Button, Stack, Row, useStatus } from "@moduletrace/twui";
Or use deep imports, which mirror the source folder layout:
import Button from "@moduletrace/twui/layout/Button";
import Input from "@moduletrace/twui/form/Input";
import TinyMCEEditor from "@moduletrace/twui/editors/TinyMCE";
Editors, MDX, Next.js helpers, the AI blocks and RemoteCodeBlock are deep-import only, so the root entry never pulls in optional peers.
AI chat
elements/ai/AIChat is a complete chat UI. Give it a height and a send function. send can return a string, an async iterable of chunks, a ReadableStream, or a fetch Response:
import AIChat from "@moduletrace/twui/elements/ai/AIChat";
<AIChat
className="h-[80vh]"
history={{ storageKey: "my-app-chats" }}
models={["gpt-5", "claude-opus-5-5"]}
send={({ messages, model, signal }) =>
fetch("/api/chat", {
method: "POST",
body: JSON.stringify({ messages, model }),
signal,
})
}
/>;
| Feature | Props |
|---|---|
| Streaming replies, stop, regenerate, retry | built in |
| Saved conversations (sidebar, search, pin, rename, delete) | history (true, options, or a useAIChatHistory() result), generateTitle in the options |
| Read aloud (text-to-speech) | speech (browser voice by default, or { speak } returning audio), autoSpeak |
| Voice input (speech-to-text) | dictation (browser recognition by default, or { transcribe }) |
| Model picker | models, model, onModelChange |
| Edit messages, thumbs up/down, markdown export | editable, feedback / onFeedback, exportable |
| Empty state, avatars, custom rendering | suggestions, emptyState, userImageUrl, renderMessageContent, renderMessageActions |
To drive the chat from outside, call useAIChat (and useAIChatHistory) yourself and pass the results as chat and history. The pieces (AIChatMessage, AIChatComposer, AIChatSidebar, useAIChatSpeech, useAIChatDictation) are exported from elements/ai/ for custom layouts.
Component modules are marked "use client", so they work in the Next.js App Router.
Development
bun install
bun run typecheck
bun test src
bun run compile # tsc → dist/, then scripts/post-compile.ts
Playground
dev/ is a local bunext app that imports the library straight from src/ (through the committed dev/twui symlink and a tsconfig path alias), so component edits hot-reload without running tsc. It has its own node_modules and is excluded from the published package by the files whitelist.
cd dev
bun install
cd ..
bun run dev # http://localhost:7010
Install the library dependencies at the repo root first: the symlinked sources resolve react, tailwind-merge, lucide-react and the optional peers from the root node_modules.
Other useful scripts:
bun run dev:types # tsc --watch, for consumers working against dist/
bun run dev:typecheck # typecheck the playground pages and the sources they import
Publishing
export GITBEN_NPM_TOKEN=...
./publish.sh "Commit message"
publish.sh typechecks, tests, compiles, commits, pushes, and runs bun publish. Bump version in package.json first.
Dependencies
Dependencies
| ID | Version |
|---|---|
| lodash | ^4.18.1 |
| tailwind-merge | ^3.7.0 |
Development Dependencies
| ID | Version |
|---|---|
| @types/ace | ^0.0.52 |
| @types/bun | ^1.4.3 |
| @types/lodash | ^4.17.26 |
| @types/mdx | ^2.0.14 |
| @types/react | ^19.3.0 |
| @types/react-dom | ^19.3.0 |
| ace-builds | ^1.45.0 |
| lucide-react | ^1.59.0 |
| next-mdx-remote | ^6.0.0 |
| openai | ^7.38.0 |
| react | ^19.3.1 |
| react-dom | ^19.3.1 |
| react-markdown | ^10.1.0 |
| rehype-prism-plus | ^2.0.2 |
| remark-gfm | ^4.0.1 |
| tailwindcss | ^4.3.3 |
| typescript | ^5.9.3 |
Peer Dependencies
| ID | Version |
|---|---|
| ace-builds | ^1.40.0 |
| lucide-react | >=0.577.0 |
| next-mdx-remote | ^6.0.0 |
| openai | >=6.0.0 |
| react | >=18 |
| react-dom | >=18 |
| react-markdown | >=9.0.0 |
| rehype-prism-plus | ^2.0.0 |
| remark-gfm | ^4.0.0 |
| tailwindcss | ^4 |