jg-react-ui

中文文档
0.4.0发布于 2026/11/04MITTypeScriptES Module构建来源已验证

Consumer-themed React components with Moldea-derived APIs, explicit styling contracts, and reusable application behavior.

原始使用文档
文档目录12 节
  1. Install
  2. Usage
  3. Preferences, theme, and notifications
  4. Controls, forms, and plain text
  5. Dialogs, drawers, and layout
  6. Routing and query recovery
  7. File uploads and audio input
  8. Markdown and code
  9. Code diffs
  10. Markdown editors
  11. Documentation
  12. License

jg-react-ui

Consumer-themed React components with retained Moldea-derived APIs.

Install

pnpm add jg-react-ui

Usage

import { createElement } from 'react';
import { Button } from 'jg-react-ui/components/button';
const saveAction = createElement(Button, { size: 'touch' }, 'Save changes');

Import Tailwind once, then jg-react-ui/styles.css. Supply --background, --foreground, --primary, --primary-foreground, --secondary, --secondary-foreground, --destructive, --chart-1 (success), --chart-2 (warning), --border, --radius, --jg-ui-font-sans, --jg-ui-font-mono, --jg-ui-shadow-inset, --jg-ui-shadow-surface, --code, --code-foreground, --jg-ui-shadow-raised, and --jg-ui-inverse-color-scheme in your own theme. Colors must be complete CSS color values. .dark selects dark utility variants; this package supplies no brand palette. For the JG palette and fonts, use jg-personal-react-ui instead.

Button, Badge, IconTile, and Alert retain Moldea’s APIs from commit 2920a7b0a6e814e95872fcdd6dd8746ae422c038, including variants, sizes, refs, badges, and asChild where supported. The exported badgeVariants, iconTileVariants, and alertVariants retain their source signatures. Badge defaults to a span; IconTile is decorative unless aria-hidden={false} is supplied; static Alert uses role="alert", with phrasing-content title and description slots. Button’s touch sizes preserve at least 44px under a coarse pointer, including on tablets. Button’s asChild requires exactly one element and retains Base UI’s button semantics, including when that element is an anchor. Use the child’s explicit role="link" when it represents navigation.

All JavaScript entries are safe to import during server rendering. Styles remain explicit CSS imports. UI preference schemas retain the complete theme, diff layout, wrapping, and install-dismissal record; the dismissal timestamp is an ISO datetime string or null. Providers create their stores per mounted tree and perform no browser storage I/O during rendering.

Preferences, themes, and notifications derive from the same pinned Moldea commit. Package adaptations replace the global store and private application registry with provider-local SSR-safe stores and scoped error feedback, validate captured namespaces against jg-storage’s rules, preserve cancellation, and allow explicit null dismissal resets. Theme effects use React’s browser lifecycle, and the notification host preserves the configured font and hydrates into a body portal.

Badge and Alert asChild also require exactly one element, otherwise they throw Error("asChild requires exactly one valid React element child."). For interaction="redirect", slot an anchor; for interaction="action", slot a native button. The interaction option supplies styling, while the native child supplies keyboard semantics. Keep supplementary AlertAction controls inside a static Alert, away from interactive parent buttons or anchors. A linked Badge remains compact by default; add coarse-pointer minimum dimensions when it is a standalone touch target.

ErrorFeedbackProvider scopes application-owned error copy and access-loss classification to a React tree. Pass a resolver from createErrorFeedback in jg-exception; useErrorFeedbackResolver reads the nearest policy. Without a provider, unknown diagnostics and codes remain private behind generic copy. Cancellation stays recognizable, and a throwing application policy falls back safely.

Preferences, theme, and notifications

Compose UiPreferencesProvider, ThemeProvider, and one Toastr host in that order. ThemeToggleButton retains the source Button props and request callback. The document theme and Sonner notification channel are document-wide; keep one theme owner and one notification host per document even when independent preference roots are mounted.

Sonner injects its base notification CSS when its browser module loads. Package theme styles remain explicit imports. All JavaScript entries remain importable without browser globals on the server.

The preference provider supports explicit local, read-only, and remote modes. Local mode reads and writes best-effort browser storage and follows matching cross-tab events. Read-only mode exposes a snapshot and denies updates. Remote mode requires complete initialPreferences and an async persistPreferences adapter: updates are optimistic, saves run sequentially and coalesce newer snapshots, and a rejected save rolls back to the latest authoritative props. The application owns network requests and cache invalidation. Pending requests cannot update storage or show notifications after unmount.

Optional storageId defaults to jg-ui-preferences. It must be non-empty and contain only ASCII letters, digits, underscores, and hyphens. getUiPreferencesStorageKey returns its raw jg-storage: key. IDs are captured for the provider lifetime; use a keyed remount to change namespaces. Invalid IDs fail before storage access or initialization-script creation.

Render ThemeInitializationScript in the document head before application JavaScript, using the same storageId as the provider and your CSP nonce when required. Server snapshots use initial props or defaults, then storage synchronizes after hydration. See the recipes for composition and the shared owned error contracts.

Controls, forms, and plain text

Forms, select/choice controls, tabs, dropdown menus, tooltips, copy feedback, progress, dividers, empty states, links, inline code, and plain typography/table primitives retain the same pinned Moldea APIs. The application owns form validation and associations. Set your app root's font-sans bg-background text-foreground classes, supply both shadow roles and code color roles, and compose labelled touch rows around compact checkbox/radio/switch glyphs. Fields use at least 16px text under a coarse pointer; touch tabs and menu rows retain 44px targets. tw-animate-css supplies the retained enter/exit animation utilities through the explicit package stylesheet.

TabsList requires overflowBehavior: 'scroll' | 'truncate'; composite labels in a truncated list need title or aria-label. Select and menu dismiss aliases honor intentional cancellation. Select keeps the source's accepted forceMount/sticky aliases without additional Base UI behavior. Slotted controls require one valid element and retain their component-specific native Error guards. Menus and tooltips retain their portal and positioning APIs.

CopyButton writes exact text during the initiating gesture, blocks overlapping writes, and starts feedback after fulfillment. Its feedback duration remains milliseconds. copyLabel supplies a scoped ready name, visible label/copiedLabel and native naming overrides are retained, failures use the nearest error policy, and cancellation stays silent. It clears timers and ignores late feedback after unmount. TextLink adds secure rel tokens and a new-tab announcement; TableContainer accepts native region/name/focus props for keyboard-scrollable tables.

Dialogs, drawers, and layout

Dialog, AlertDialog, Drawer, ConfirmationDialog, and page/layout primitives retain the pinned compound APIs. DialogContent requires a size and direct DialogHeader, DialogBody, and optional DialogFooter children; asChild accepts one panel element containing those regions. Use an element that permits the dialog role, such as a div; place a form inside the body and associate fixed submit actions with its form ID. Drawer requires a side, its content requires a size, and its direct regions are ordered header, body, optional footer. AlertDialogContent requires a direct header and footer. Invalid composition and invalid slotted children retain native Error guards.

Medium and large dialogs become full-screen pages on phones, with generated Back and desktop close controls. Body regions own scrolling, while headers and actions stay fixed. Compact modal close controls retain their fine-pointer appearance and have actual 44px targets under a coarse pointer. The default initial focus skips unusable controls; initialFocus and generated control icons/labels remain configurable. Overlay dismissal stays disabled by default for dialogs. isDismissalDisabled blocks on-page dismissal; committed navigation still cancels obsolete dialogs.

DialogNavigationProvider, DialogNavigationObserver, and useDialogNavigationDismissal directly reuse jg-react-router's implementations and context. Place the observer inside the router, with persistent dialog hosts under the same provider. Navigation dismissal does not create history entries. The host application owns route history and its navigation UI; drawers close on route actions through an explicit DrawerClose composition or application state.

Mount one ConfirmationDialog per document and use useConfirmationDialogStore.getState().openConfirmationDialog(options). This imperative store retains the source's document-wide session API; do not mutate it while server rendering. Completion resets the session before calling the application callback, which receives true on confirmation and undefined on cancellation. The application owns async work and callback errors. Controlled dialog children remain immutable through closing transitions, then release at completion. Mobile return snapshots ignore stale callbacks and are skipped for reduced motion or unsupported browsers.

PageShell, PageHeader, PageHeaderRow, PageHeaderTitleGroup, PageHeaderMetadata, PageTitle, PageDescription, Stack, Inline, and Surface preserve the native semantic props and shared geometry. Supply --jg-ui-shadow-surface alongside the other generic theme roles. The stylesheet supplies source page-gutter spacing and the drawer gutter hooks; it does not reset the document's palette or typography.

Routing and query recovery

RouterRoot retains the source data-router outlet, pending progress, and shared dialog observer. It requires React Router's data router and the shared router peer. Route progress respects the top safe-area inset. It does not add history entries or change page focus/scroll policy; compose jg-react-router's PageOutlet when the host wants that behavior. GlobalErrorBoundary reads the thrown route error, focuses its labelled recovery main, preserves native refs, and cancels obsolete dialog sessions; GlobalErrorBoundaryView accepts an explicit error without router context. Recovery defaults to jg-sw's updateApp, with an optional onRefreshClick. Unknown errors and recovery failures use scoped safe feedback. Pending refresh and retry actions ignore overlapping clicks.

QueryBoundary, QueryLoader, LoadMoreButton, QueryErrorAlert, and QueryErrorToastr retain the structural query APIs. The boundary delegates initial resolution to jg-tanstack-query's shared hook: stale unapproved data waits, current data survives background failures, a new resolutionKey starts over, and access loss hides data immediately. After access loss, caller approval cannot restore data until every query settles successfully with current data. The nearest ErrorFeedbackProvider owns access-loss classification and visible copy; no application registry ships with these packages.

The default error slot is an inline blocking/retained alert. Custom renderers can use a persistent QueryErrorToastr for retained content. Supply a unique stable toastId per active failure owner and one Toastr host per document. Toast publication follows commit effects so a later-mounted host receives initial failures, and stale queued feedback is cancelled. Retry reads the latest committed queries and policy, ignores active fetches/overlapping retries, and keeps failures actionable until recovery; unmount cleanup preserves a same-ID replacement. Retry settles every failed query, including rejected or synchronously throwing adapters. Cancelled errors produce no outage alert or toast. Load-more click callbacks run first and may cancel the request with preventDefault.

Default compact pagination and recovery actions preserve their fine-pointer geometry and have at least 44px targets under a coarse pointer. JG's global recovery surface uses the foundation's inverse palette in both themes. Generic consumers set --jg-ui-inverse-color-scheme to match their recovery surface (dark or light); selection and focus use its primary roles. Query clients, network adapters, pagination limits, application code maps, and routing history remain host-owned.

File uploads and audio input

FileUpload, FileUploadDialog, ImageUploadField, and AudioInput retain the pinned source props and callbacks. File uploads reuse jg-react's admission, concurrency, progress, cancellation, and retry queue. The caller supplies validation and transport callbacks; no endpoint, authorization policy, storage service, or transcription provider is included. Upload adapters must honor their abort signal. The image field displays the caller's current image and falls back safely when its preview fails; the caller owns object URL creation and revocation.

The upload dialog finalizes successful results through onSubmit, retries finalization without reuploading, and reports abandoned successful items through onDiscard. The caller owns remote cleanup and callback failures. Audio input owns microphone tracks, recorder handlers, waveform contexts, intervals, and deferred commands. Obsolete permission/transcription results cannot update a replacement session or call completion after unmount. Recording reset is immediate; closing retains the source's short visual reset delay.

transcribe receives the captured recording and returns text. Users can send directly or edit the transcription first; a failed send retries the authored text without transcribing again. Provider operations remain caller-owned and should have their own deadline/cancellation policy. Recording/transcription error callbacks receive diagnostic exceptions, while visible feedback uses the nearest scoped resolver. The seven owned audio codes and stable messages are listed in errors/README.md; coded provider exceptions preserve their diagnostic identity. Canceled permission/transcription work stays silent and can be restarted; diagnostic callbacks still run. Optional result callbacks remain optional. Standalone review remains the default. Supply onTranscriptionApply to place Transcribe results directly in the main input while onTranscriptionResult handles Send separately; isResultActionVisible={false} uses Transcribe as the sole result action. Progress stays on the active button, with a screen-reader announcement. Pending work blocks dismissal and restart.

Markdown and code

Markdown retains GFM tables/task lists, heading and prose rendering, uppercase template-variable highlights, image width hints (title="width=560"), renderer/plugin overrides, and standalone YouTube links. Raw HTML is skipped by default. HTTP(S) and relative document links open in secure new tabs by default, with an accessible announcement; fragments and non-web links remain in the current tab. Set shouldOpenLinksInNewTab={false} or supply an anchor renderer for application routing. Applications own the trust policy of custom plugins, URL transforms, and renderers.

Message columns need min-width: 0; grid tracks use minmax(0, 1fr). Markdown and InlineCode wrap long paths and unbroken tokens. InlineCode retains preferred separator breaks without changing copied text. Code blocks and tables contain their own horizontal scrolling; Markdown tables have labelled, keyboard-focusable scroll groups. Unlabelled fences remain preformatted code blocks with exact whitespace.

CodeBlock and CodeFileHeader preserve metadata/status/action slots, aliases, syntax highlighting, literal plain mode, custom copy text, and wrapping controls. Declared prose languages or prose file extensions wrap automatically. prefersWrappedCode takes precedence over shouldWrapContent, then automatic prose wrapping and the scoped saved preference. Explicit overrides and read-only providers hide mutation controls. diff and diff-<language> fences preserve source endings, patch markers and multiline syntax. CodePreferencesControls also exposes split/unified preference menus for source surfaces. Compact actions keep the source fine-pointer geometry and use 44px coarse-pointer targets.

The personal adapter maps syntax and file-status roles to jg-personal-ui's existing theme tokens. Generic syntax uses unbranded One-theme fallbacks; supply --syntax-foreground and --syntax-token-{keyword,string,string-expression,comment,function,constant,parameter,punctuation,link} to override them. Token maps are reused across renders, and unchanged Markdown parser inputs are memoized. Every grammar/style import names its exact JavaScript file, so Node imports need no consumer resolver changes.

parseYouTubeVideoUrl validates supported HTTPS single-video links and returns a branded identifier. YouTubeVideo uses the fixed privacy-enhanced origin, strips authored playback parameters, and enables autoplay only through its explicit prop. URL validation/building reuses jg-utilities, with the retained UI's narrower route policy. A forged invalid identifier throws the dependency's documented INVALID_YOUTUBE_VIDEO_ID contract.

Code diffs

CodeDiff runs beneath UiPreferencesProvider and ThemeProvider. It retains source metadata, upstream viewer controls, style overrides, fold renderers, and responsive unified/split preferences. Its runtime is the package's bundled, patched [email protected]; consumers configure no patch or install script. The upstream dependency supplies declarations, while runtime imports use the bundled adapter. React and React DOM remain external.

Bounded excerpts (at most 8192 combined characters and 128 combined lines) render synchronously, including during server rendering and revision changes. Larger comparisons retain the lazy Web Worker path; explicit disableWorker keeps its source meaning. Worker support and Blob-worker permissions depend on the host environment. styles, loadingElement, infiniteLoading, alwaysShowLines, line offsets, gutter renderers, and other retained viewer props remain available. Split view starts at lg by default; narrower layouts use unified rows. Code wraps inside fixed-width cells. The optional tabIndex belongs to the content beneath the stationary file header.

The checked build verifies pristine and patched ESM file identities before bundling, rejects stale hunks, and preserves the worker's separate dynamic chunk. Moldea's patch is unchanged. Local accessibility adaptations make the summary a labelled group and give the all-lines button an explicit name and expanded state. Fold controls have native keyboard behavior and coarse-pointer targets of at least 44px. Complete bundled licenses ship in THIRD-PARTY-NOTICES.md; pnpm build:viewer --write-notices regenerates that file, and ordinary builds check it for staleness. Build tooling and patch sources stay outside the tarball. Turbo’s package-local unit and browser tasks depend on the package build, so test collection always receives the compiled patched viewer. Use Turbo when running these tasks from a fresh checkout.

Large-diff results are cached only for the current viewer input, matching the synchronous path's single-revision ownership. A miss recomputes in the real worker; returning to an older revision does not retain its old source/result in memory. Replies for obsolete inputs cannot clear the current revision's loading state. Controlled alwaysShowLines and extraLinesSurroundingDiff changes recompute large comparisons; runtime line references use L-1 and R-1 forms. The checked build applies these local corrections after verifying the unchanged source patch; real-worker regressions reproduce both failures before correction. Work remains local to the viewer, with no cross-root cache or additional persistence.

Markdown editors

MarkdownEditor retains the controlled value/onChange, supportedTools, accessibility props, and auto/compact/wide layouts. Auto measures the owning container and selects wide at 640px. CompactMarkdownEditor and WideMarkdownEditor expose the same props without layout. A changed value replaces the document as one edit that keeps the editor's focus, with the caret at the end of the text, and isn't reported back through onChange. The exported all-tools, authoring, and prose policies retain the source command sets; existing documents remain readable when authoring tools are constrained.

ChatMarkdownEditor retains paragraph, H1–H3, bullet/numbered-list, bold/italic, and safe-link formatting. HTML is removed, unsupported blocks become readable prose, and image alternatives remain text. Paste checks bound raw input before parsing and the serialized result before replacing the draft. Rejected paste leaves the draft intact; formatting feedback remains until the next edit.

Supply a positive safe-integer maxLength, which counts the message without the line break its Markdown ends with. maxEditorHeight defaults to 256px, must be positive and finite, and is constrained to 50dvh by the stylesheet. controlsRef exposes getMarkdown(), focus(), and appendLiteralText(text). Literal insertion escapes Markdown and normally returns false before readiness or when the draft is too long. Pass { allowOverLimit: true } to retain complete dictation, including during initialization, for editing in the main input. Read-only editors reject insertion. The consumer must block button and keyboard submission while over limit. Key the editor by draft identity to clear content and undo history after a successful submission. Initial snapshots retain the controlled draft, and focus waits for the initialized editor. Use these controls to compose caller-owned upload and dictation flows.

formattingContainer uses inline placement when omitted, reserves external placement with null, and portals the formatting menu into a supplied HTMLElement. The existing styles.css entry includes Milkdown's common/frame styles before editor overrides. The personal package re-exports the generic implementations directly and supplies the shared JG tokens.

Invalid configuration retains these native Error messages:

  • ChatMarkdownEditor maxLength must be a positive safe integer.
  • ChatMarkdownEditor maxEditorHeight must be a positive finite number.

Documentation

The API reference ships as API.md, and app-side examples ship as RECIPES.md. These personal-use packages are unlisted on the public package website.

License

MIT.

版本与发布记录

jg-react-ui 共发布 10 个版本,首次发布于 2026/10/30;过去 12 个月发布了 10 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
0.4.0latest2026/11/041 天前
0.3.52026/11/041 天前
0.3.42026/11/032 天前
0.3.32026/11/032 天前
0.3.22026/11/032 天前
0.3.12026/11/022 天前

关于 jg-react-ui 的常见问题

jg-react-ui 是什么?

Consumer-themed React components with Moldea-derived APIs, explicit styling contracts, and reusable application behavior.

如何安装 jg-react-ui?

在项目目录运行 npm install jg-react-ui,或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖:react(^19.2.4)、react-dom(^19.2.4)、tailwindcss(^4.3.3)、react-router(^7.14.2)。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖,Yarn 需要手动安装。

jg-react-ui 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

jg-react-ui 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

jg-react-ui 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >=22.17.0。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

jg-react-ui 可以商用吗?

jg-react-ui 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

jg-react-ui 还在维护吗?

最新版本 0.4.0 发布于 2026/11/04(1 天前);至今共发布 10 个版本,其中过去 12 个月发布了 10 个;首次发布于 2026/10/30。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。