@ai-matrx/design-system

中文文档
0.86.407发布于 2026/10/30MITTypeScriptESM + CJS近 30 天下载 17.4万

AI Matrx semantic React primitives shared across Vite, Next.js, desktop, and customer-built applications.

原始使用文档
文档目录5 节
  1. Styling — the package ships its CSS, the host owns the valu…
  2. Release gate
  3. Data tables
  4. Table row actions
  5. Drill-down

@ai-matrx/design-system

The small, semantic UI foundation shared by AI Matrx React applications — primitives whose behavior and variants are identical across Vite and Next.js hosts.

Controls: Button, Badge, Label, Separator, the Input family, the Textarea family, Checkbox, Switch, RadioGroup, Slider, Toggle + ToggleGroup, Avatar, Progress. Frameless fields: Field from @ai-matrx/design-system/controls and ProInput / ProTextarea from @ai-matrx/design-system/writing-field accept bare. The surrounding surface owns the border, fill, and focus treatment; the field paints none, including under the desktop look. A bare ProTextarea keeps its text inset and the space reserved for its microphone and menu controls. Existing default fields keep their normal styling.

Content transfer: moved to @ai-matrx/alchemy/react/workspace (0.69.0, ALC-16); it supplies Matrx Alchemy's inlined glyph, accessible menu, projected capture workspace, optional AI capability seam, and locally scoped SurfaceHandle provider. Its source is captured on user action; preparation keeps an immutable original, preserves manual and alternate-source sessions, fences stale async results, and routes every copy/download through Transport. Import @ai-matrx/design-system/content-transfer.css with the package structural sheets.

<MatrxCopyMenu
  label="Log"
  export={{
    items: [
      {
        id: "raw",
        label: "Download raw log",
        build: () => ({
          content: rawText,
          filename: "worker.log",
          extension: "txt",
          mime: "text/plain",
        }),
      },
    ],
  }}
/>

An explicit filename is an exact safe basename captured with its bytes. Omit it to use the label-derived default; unsafe values fail the export visibly. Surfaces: Card, Table, Tabs, Accordion, Collapsible, ScrollArea, Popover, Tooltip, HoverCard, Sheet, Dialog, AlertDialog, Drawer, BottomSheet + TabbedBottomSheet, Alert, Resizable, Skeleton. Menus: DropdownMenu, ContextMenu, Command / CommandDialog, Select, CreatablePicker. Composed: EditableLabel, SegmentedControl, ArchiveFilter, ScoreRing, OverflowToolbar, ConfirmDialog + ConfirmDialogHost. Hooks + utilities: useScrollFade, useIsMobile, usePortalContainer, useDialogContainer, useDrawerDirection, cn.

The confirm dialog lives here; the imperative confirm() lives in @ai-matrx/kit. Since 0.11.0 / kit 0.9.0 the surface is one implementation, in the package that owns AlertDialog, the motion layer and the overlay scrim token. Mount the host once near your provider root and call confirm() from anywhere — they share one registry through a globalThis slot:

// app root, once
import { ConfirmDialogHost } from "@ai-matrx/design-system";
<ConfirmDialogHost />

// anywhere — a thunk, a util, a click handler
import { confirm } from "@ai-matrx/kit/confirm-opener";
if (!(await confirm({
  title: "Re-run research",
  description: "This discards the 14 pages you already have (~$50 and 10 minutes) and starts over.",
  variant: "destructive",
  confirmLabel: "Discard and re-run",
}))) return;

Use the declarative <ConfirmDialog open … busy /> inline instead when the dialog must stay open with a spinner while async work finishes. Name the consequence: a confirmation that says only "Are you sure?" does not satisfy the platform's destructive-and-expensive-actions law.

Footer actions stay touch-sized through portals. Dialog, AlertDialog, Drawer, Sheet, and BottomSheet footers apply a 44px action floor on coarse pointers or below 1024px. Import the package stylesheet; no host subtree class is required. Fine-pointer desktop keeps its declared density. State controls and prose links retain their sizing; exceptional compact actions can use data-touch-exempt with a call-site reason.

Every list over an archivable entity uses ArchiveFilter. THE ARCHIVED-ITEMS LAW (Arman, 2026-09-09 — common-docs/policies/archived-items.md): an archive filter on every list, the default hides archived rows, and seeing them is one click. ArchiveFilter is the one control — active (default) / archived / all, in the same words the Next.js app uses.

import { ArchiveFilter, DEFAULT_ARCHIVE_FILTER, type ArchiveFilterValue } from "@ai-matrx/design-system";

const [archived, setArchived] = React.useState<ArchiveFilterValue>(DEFAULT_ARCHIVE_FILTER);
const rows = useRows({ archived });   // the SERVER applies the predicate

<ArchiveFilter value={archived} onValueChange={setArchived} counts={{ active: rows.activeCount }} />

Pass the value to the reader, never filter the already-fetched page: counts, badges and pagination must describe what the list renders. toArchiveFilter() narrows an untrusted value (URL param, stored preference) back to the tri-state, defaulting to active so nothing silently widens.

The Tooltip lives here, and only here. @ai-matrx/tap-target used to ship its own copy; since design-system 0.7.0 / tap-target 0.2.0 it consumes this one and re-exports nothing.

Where hosts had forked a primitive over DENSITY, density is a prop, not a fork: Card size="sm|md|lg", Table size="sm|md", Accordion size="sm|md", Switch/Avatar/Checkbox/Slider/RadioGroup/Toggle size, ResizableHandle size="xs".."4xl". It is declared once on the root and every item reads it from context, so a card cannot mix paddings and a radio group cannot mix control sizes.

Motion ships with the component — ALL of it. Every animated surface here (Dialog, AlertDialog, Sheet, CommandDialog, Popover, DropdownMenu, ContextMenu, HoverCard, Select, Tooltip, Accordion, Collapsible, Skeleton) animates out of the box from keyframes in styles.css. You never define keyframes, add an animate-* utility, or install tailwindcss-animate / tw-animate-css for anything this package renders — that plugin is loaded from a host's CSS entry, which is why three of the four apps that already used this package had zero animation on every overlay and no error to show for it (0.10.0). prefers-reduced-motion is honored in the sheet.

Retiming is a token change, not an override — set --matrx-motion-duration-fast / -base / -slow / -sheet, --matrx-motion-ease-out / -in, or --matrx-motion-popper-travel. For one surface that must appear instantly, pass animated={false}; CollapsibleContent's opt-out additionally stops it clipping its body, for the rare panel with a child that must overflow.

Mobile is the Drawer's job. Drawer owns the iOS rules every host was re-deriving — dvh never vh, pb-safe/pt-safe safe-area padding, a 44px grab-handle target, DrawerBody as the flex-1 min-h-0 scroll shard — and direction makes a top/side drawer a prop rather than a fork. Every touch control (Slider, RadioGroup, ResizableHandle) carries an invisible 44px target on coarse pointers, with desktop density untouched.

pnpm add @ai-matrx/design-system
import { Badge, Button } from "@ai-matrx/design-system";

<Button variant="subtle" size="sm">Add step</Button>
<Badge variant="success">Ready</Badge>

Host-shaped systems are injected, never imported: wrap subtrees in PortalContainerProvider to point nested Popover portals at a dialog or popout body, and hand OverflowToolbar your menu and tooltip systems through its renderOverflowMenu / renderTooltip props. @ai-matrx/kit is a real dependency (installed automatically).

web-theme.css registers Tailwind Typography for a host CSS build. Install @tailwindcss/[email protected] in any host that imports that stylesheet. It is an optional peer so component-only consumers do not install a build plugin at runtime.

Styling — the package ships its CSS, the host owns the values

The package is opinionated about the token contract and ships every rule its components need to work; it is not opinionated about your colors.

// app root, once
import "@ai-matrx/design-system/tokens.css"; // default token VALUES  — override these
import "@ai-matrx/design-system/styles.css"; // structural CSS        — required
/* your Tailwind/CSS entry — FIRST line, before @import "tailwindcss" */
@layer matrx-design-system-tokens, matrx-design-system;
  • styles.css (required) — every rule the components need to render right: the .matrx-scroll-fade mask geometry behind useScrollFade, the .matrx-mobile-sheet density restatement TabbedBottomSheet sets, the .matrx-glass-thin-border chrome, .pb-safe, .shadow-input. It hardcodes no color: every color resolves from a token.
  • tokens.css (default values) — a neutral, sensible default for every token the package reads, in both themes, so a fresh app renders correctly unthemed. This is not a brand.
  • theme.css (Tailwind v4 hosts, optional) — @import "@ai-matrx/design-system/theme.css"; from your Tailwind entry maps the semantic color utilities (bg-background, text-muted-foreground, …) onto those tokens and registers the package build as a scan source. A host that already maps this vocabulary (matrx-frontend does, as a branded superset) skips this file and keeps its own.

Overriding with your brand. Redeclare any token — --primary, --background, the --matrx-glass-* material, the overlay scrims — anywhere in your own CSS. The package defaults live in the matrx-design-system-tokens layer, and the one-line @layer declaration above puts that layer at the bottom of the cascade, so any host declaration wins: layered or unlayered, before or after, whatever the specificity. Declare the layer order once and the question never arises.

Tailwind CSS v4 ignores node_modules by default, so a host that does not import theme.css still registers the package build beside its Tailwind import:

@import "tailwindcss";
@source "../node_modules/@ai-matrx/design-system/dist";

Workspace consumers point @source at apps/shared/design-system/src.

Color roles — pick by meaning, always with the pair's own text token.

  • primary (blue action) · bg-primary text-primary-foreground.
  • secondary = quiet GRAY surface (shadcn meaning) · bg-secondary text-secondary-foreground. Never a text, border, ring or gradient color.
  • brand-secondary = the PURPLE brand accent · bg-brand-secondary text-brand-secondary-foreground; as text/border/gradient: text-/border-/from-brand-secondary.
  • muted / accent = gray rows, hovers, wells · text-muted-foreground for quiet text.
  • destructive success warning info read as TEXT on page and card (text-destructive) and as fills with their -foreground.
  • A tone as text on a tint of itself (badge, chip) uses its -ink token: bg-success/15 text-success-ink.
  • Guards: token-contrast.test.ts (every pair ≥ 4.5:1, both themes) and secondary-role.census.test.ts (no misuse in any package).

Release gate

pnpm --filter @ai-matrx/design-system typecheck
pnpm --filter @ai-matrx/design-system test
pnpm --filter @ai-matrx/design-system check:package

The gate validates declarations and exports against the packed tarball, installs it into an empty project, and imports the public entry point.

Data tables

The browser-only table playground has two explicit presets: Capability examples is its illustrative starting configuration, while Package defaults reflects the table source's defaults. The latter uses normal density unless a host provides one, standalone zebra rows, sticky headers, view tabs, column resizing/reordering, copy, selection, and 25-row pagination; grouping, saved views, summary, spreadsheet, editing, formatting, styling, facets, detail, and validation require configuration. Its Status review and Owner review views are provided local starting examples using the same saved-view host contract as user-created views. They are editable and persist only in that browser's local storage; production hosts own durable storage, authorization, and any database-backed truth.

Desktop columns are adjustable by default. Optional summary supplies KPI metrics above the toolbar and totals aligned to column IDs below the rows. Callbacks receive the filtered rows before pagination, plus isFiltered and coverage (all, loaded, or page). The table labels that scope and never calls aggregates for an empty loading or failed read. Retained stale rows are labeled; unavailable aggregates remain visible. Use summary.source only for caller-provided complete source totals; missing source values are never replaced with an aggregate of the loaded page.

summary={{
  metrics: [{ id: "amount", label: "Amount", value: ({ rows }) => rows.reduce((n, row) => n + row.amount, 0) }],
  totals: { amount: ({ rows }) => rows.reduce((n, row) => n + row.amount, 0) },
}}
import { MatrxDataTable } from "@ai-matrx/design-system/data-table";
import { usePaginatedData } from "@ai-matrx/data/react";

// The caller owns loadPage, source identity, and query state.
const pagination = usePaginatedData({ queryKey, initialCursor: 0, loadPage, getRowId });
<MatrxDataTable data={pagination.rows} columns={columns} getRowId={getRowId}
  query={{ mode: "controlled-append", state, onStateChange, pagination,
    scroll: { mode: "scroll", thresholdPx: 96, intentTimeoutMs: 1200 } }} />;

URL/source owners can control columns without rebuilding the table: pass columnState={{ order, hidden, onChange }} and reorderableColumns. The package then commits both the Columns dialog and header drag through onChange. The dialog provides drag ordering and labeled arrow controls; columns marked hideable: false remain visible.

Use application-resolved preferences for scroll; manual always retains Load more. Numbered exact-page sources use controlled with totalItems; fully loaded client arrays use local mode or controlled-local. Partial server results must not claim client filtering searched the entire source. Query identity must cover authentication, organization, filters, sort, and page size. A page loader receives an AbortSignal and returns rows, a next cursor (null when complete), and an optional exact total. Repeated cursors fail visibly instead of looping.

Framework-specific identity ports are optional via @ai-matrx/design-system/data-table/host. Utilities and types have dedicated subpaths listed in the export map. The package retains copy/subset/export, row windows, hierarchy, selection, inline editing, and accessible manual pagination.

Editable append tables keep drafts separately for each query identity during the component lifetime. A return to that identity restores its drafts; drafts from other results offer Copy and confirmed Discard, never a save against the current rows. Applications own persistence across unmounts and must remount the table when changing authenticated users so prior-user drafts are not exposed.

Table row actions

Standard MatrxDataTable.rowActions returns icon descriptors, never React content:

rowActions={(row) => [{
  id: "inspect", icon: Eye, label: `Inspect ${row.name}`,
  tooltip: "Open record details", onClick: () => openRecord(row.id),
  variant: "ghost", tone: "primary",
}]}

label is the accessible name and default tooltip, not visible button text. disabled and loading retain the icon button; the table owns density and sizing. onClick receives the button event for callers that need modifier keys. A menu, editor or other React content must be declared in its own column: { id: "workflow", header: "Workflow", customActions: (row, controls) => <WorkflowMenu row={row} controls={controls} /> }. Custom columns carry live record controls, never participate in sort/filter/copy/spreadsheet data, and are not draggable (their headers may contain controls). Hidden custom columns stay hidden in mobile cards too. A table with a visible custom column draws ONE actions column: the row copy rides inline at the start of the first visible custom column, and the built-in row-actions column is drawn only for standard icon rowActions or the window button. Under fitToWidth a custom column is as wide as its widest row strip or its header, never a scaled share; when it ends the row it is pinned right like the built-in column.

Row actions declared once, with a "⋯" overflow menu

rowActionDefs declares the actions once for the whole table; rowActionsInline caps how many show as icons (default: no cap, so a table that does not opt in is unchanged). The first N, in declared order, are icons; the rest become one "⋯" menu at the end of the strip, each item with its icon and label. An action that does not apply to a row is drawn DISABLED in its slot (with unavailableTooltip), never removed, so the icons line up down the column.

rowActionsInline={3}
rowActionDefs={[
  { id: "track", icon: Radar, label: "Track", appliesTo: (p) => !p.tracked, unavailableTooltip: "Already tracked", onClick: (p) => track(p) },
  { id: "open", icon: ExternalLink, label: "Open", onClick: (p) => window.open(p.url) },
  { id: "edit", icon: Pencil, label: "Edit", onClick: (p) => edit(p) },
  { id: "delete", icon: Trash, label: "Delete", tone: "destructive", onClick: (p) => remove(p) },
]}

rowActionsInline also caps a plain rowActions list. Playground: ?scenario=row-actions-overflow.

Saved views and column defaults

A saved view (or URL-owned columnState) only decides for the columns it names in order or hidden. A column added later, or declared hidden: true, that an older state never mentions keeps the column's own default.

Drill-down

Use it when a person needs to ask "how many, how much, by what" of a table: group by a Dimension, pivot a second one across the top, pick the Measures, click a group to drill in, compare a window with the one before. Every state is a URL a person can share.

Rows already loaded — one prop. "Loaded" means the COMPLETE set: every row of the source is in data. Over a partial source — server pagination (controlled / controlled-append), a coverage or facets.totalRows larger than the rows held — the table refuses in words ("63 of 4,812,004 rows loaded: group on the server.") instead of printing counts of whatever happens to be loaded; use the server path below.

import { MatrxDataTable } from "@ai-matrx/design-system/data-table";

<MatrxDataTable data={jobs} columns={columns} getRowId={(j) => j.id} drill={{ local: true }} />

Dimensions (few-valued text judged over every row, yes/no, dates with their grains — never an id or a near-unique column) and Measures (count; sum, average, min, max of each number column in its own format) are inferred from columns. The table's search and column filters narrow what the groups count. Narrow or extend them with drill={{ local: true, dimensions: ["status", "owner"], measures: ["count", "sum_amount"], extraMeasures: [...] }}; other knobs: url (prefix, default drill, or false), timeZone, limit (groups before Other, default 100), ceiling (default 50,000 rows — past it the table says so and names the server path; never a silent cut), source ({ total }, { paged: true } or { cap } when the table cannot tell it holds part of its source), hide (Dimension, Measure or column keys never offered — not as a Dimension, a Measure or an inferred attribute).

What the table reads on its own: coverage.total / matched / cap, facets.totalRows, a controlled-local or controlled-append source's sourceProcessing.sourceTotal, and an append source's pagination.totalItems. A list that reached coverage.cap refuses ("List capped at 200: group on the server."); an append source with no next page and its total held is COMPLETE and groups. While the trail narrows a complete list, the host's coverage is not restated as "Filtering the N loaded". An inferred Measure prints in its column's format (money, duration, percent; a blank reads "—"), so extraMeasures is only for a new label or op. A Measure may say its blanks: nulls: "unknown" (a group with a missing input reads Unknown) or "count" (the known part, then "N unknown"); the answer row carries unknown[measure]. In development a level, a breakout or a named Dimension that is not a Dimension is warned once, with why the column was dropped (near-unique, too many values, no accessor) — drillDroppedItems returns the same lines for a test.

A chart above the groups? One more option (Arman, 2026-10-25: the chart is part of the table):

<MatrxDataTable data={jobs} columns={columns} getRowId={(j) => j.id} drill={{ local: true, chart: true }} />

chart is true or { measure, stack, time, seriesLimit, height } (MatrxDataTableDrillChart). The bars are counted from the same rows the groups count, so the table's search and filters narrow both. A server-answered drill passes chart plus chartAnswers / chartError (the two rounds drillChartQuestions plans). The table never imports recharts: the HOST binds the chart once (MatrxDataTableHost.DrillChart = MatrxDrillChart from ./data-table/drill-chart, loaded lazily); a host with none shows "Chart unavailable here" where the chart would be. Guard: src/data-table/MatrxDataTable.drill-chart.test.tsx.

Rows on the server — the declared definition: a *.drill.ts definition in apps/shared/records/scripts/drill-definitions/, read through @ai-matrx/records (drillAsk) — mount DrillExplorer (matrx-frontend) or useDrill / the records-grid drill (@ai-matrx/records-ui), which hand the table { dimensions, measures, question, onQuestionChange, answers }. Same screen; swapping local for server changes no UI.

How to declare levels

A drill-down is a table about the thing you clicked, not a list. Each Dimension may declare what a LEVEL of it looks like — the same shape locally and on the server (MatrxDrillLevel):

KeyMeansExample
breakoutsDimension references offered as one-click "By …" chips once one of its values is drilled (and first in the row menu's Break out by), in orderperson → ["provider", "model", "agent", "feature", "conversation", "at:day"]
showThe Measures shown while grouping by it (a chip, the Group menu, a drill into it)conversation → ["cost", "calls", "tokens_in", "tokens_out", "started", "last_activity"]
attributesWhat describes ONE value: columns while grouping by it, and the drilled value's header. Server: Dimension keys each group holds one value of. Local: any column keyperson → ["organization"]; model → ["provider"]
recordsThe columns "See these records" shows at this levelconversation → ["created_at", "agent_id", "cost"]

A breakout may name A SIBLING DEFINITION'S Dimension, <definition>:<dimension> (ai_usage_executions:conversation on ai_usage's person — that rollup has no conversations). The host offers it through drill.cross (MatrxDrillCross: label(source, ref) → the Dimension's words or null = not offered; open(source, ref, question) switches the source, grouped by ref, carrying the trail and window). parseBreakoutRef reads one; drillLevelProblems(dims, measures, { siblings: { ai_usage_executions: [...keys] } }) judges it. Drilling a value lands on its level's FIRST free breakout (after a declared path), with that level's show; a question grouped by a Dimension and naming no Measures shows its level's, an address included (?drill.by=conversation → drillQuestionFromParams(params, fallback, prefix, dimensions), useDrillUrlState({ dimensions }), drillSettleShow). attributes: [] says a level has none.

What the drilled screen does on its own: every header number has a visible label; a relation value (entity) offers its record doors (open, preview, its kind's menu, via the host's resolveEntityDoors) beside its crumb and in a group row's ⋯ menu, and "See records" lists the value's records; two groups with one label read "label · first distinguishing fact" (an attribute, else a moment Measure — drillDistinctLabels); an attribute column the trail filters or every group holds the same value of is dropped, as are the records list's filtered, header-said and constant columns; a failed attribute read (MatrxDrillAttribute.error, MatrxDrillFocusAttribute.error) is said in the canonical ErrorBox, never as "—".

Selectors for walks: data-matrx-drill-group / data-matrx-drill-value / data-matrx-drill-level (a group row), data-matrx-drill-into (its label), data-matrx-drill-breakout (+ -breakout-source for a sibling's), data-matrx-drill-see-records, data-matrx-drill-focus-doors, data-matrx-drill-focus-total, data-matrx-drill-focus-attribute, data-matrx-drill-crumb, data-matrx-drill-failed, data-matrx-drill-unknown.

Group actions, doors, names, the fixed filter and where the controls sit

OptionOnMeans
groupActions(groups)a Dimensionitems drawn in a group row's ⋯ menu and its right-click menu ("Copy repair brief" on a lint class); selector data-matrx-drill-group-action
doorFor(value)a Dimensionwhere a named group opens: { entity, id } through the host's resolveEntityDoors, or { href } (a page). entity alone still opens a value that IS a record id
labelFroma Dimension, or local labelFrom: { owner_id: "owner_name" }the column (local) or Dimension (server, useDrill) whose value names each value; labelFor wins when given
controls: "toolbar" | "trail"the drill configthe trail + window/Measures/Group-by in the toolbar, or in their own row (MatrxDrillBar, data-matrx-drill-bar). Default "trail" under hideToolbar, else "toolbar". MatrxDrillControls is the three menus for a page's own row
base: [{ label, value }]the drill configthe host's fixed filter (one site), said first in the trail as a crumb no click removes (data-matrx-drill-base); the host applies it (useDrill({ base }))

A blank text value and a missing one are ONE "No value" group, locally and in the server door.

Nothing declared = inferred: breakouts are every other Dimension, fewest values first, time last at a day; show is the question's own; attributes (local mode) are the columns that hold one value within every group of it.

Local — one prop, keyed by Dimension:

<MatrxDataTable data={jobs} columns={columns} getRowId={(j) => j.id}
  drill={{ local: true, levels: {
    owner: { breakouts: ["client", "project", "status", "due:month"], attributes: ["team"], show: ["count", "sum_amount"] },
    client: { attributes: ["industry"] },
  } }} />

Server — on the Dimension in its *.drill.ts (judged by platform.drill_definition_problems; drill_describe hands it to the screen):

{ key: "person", label: "Person", from: "person_id", kind: "relation", relation: { token: "user", person: true },
  level: { breakouts: ["provider", "model", "agent", "feature", "conversation", "at:day"], attributes: ["organization"] } },

A host that answers itself hands drill.attributes (columns while grouping) and drill.focusAttributes (the drilled value's header) on MatrxDataTableDrillConfig, or the same attributes to MatrxDrillAnswerTable and focusAttributes / measures / answers to MatrxDrillTrail. Helpers: drillWithLevels, drillBreakouts, drillRegroup, drillLevelShow, drillLevelProblems (the local validator), drillGroupAttributesLocally, drillInferAttributes, parseBreakoutRef, drillSettleShow, drillDistinctLabels, drillDroppedItems.

Try it: open the playground at http://127.0.0.1:3026/?scenario=drill-level-owner (Ada's header and her chips), drill-level-breakout (Ada by client), drill-level-attributes (clients with their industry). Also http://127.0.0.1:3026/?scenario=drill-start (pnpm demo; also drill-blocked-by-owner, drill-pivot-month, drill-chart, drill-search, drill-partial, drill-ceiling) — any setup is a link (Copy link in its panel; demo/README.md § Links). State: common-docs/systems/data/drill-down/STATE.md.

版本与发布记录

@ai-matrx/design-system 共发布 766 个版本,首次发布于 2026/08/23;过去 12 个月发布了 766 个版本。

版本发布日期距今
0.86.407latest2026/10/306 天前
0.86.4062026/10/297 天前
0.86.4052026/10/297 天前
0.86.4042026/10/298 天前
0.86.4032026/10/288 天前
0.86.4022026/10/288 天前

其他发布通道:backfill → 0.62.4。

关于 @ai-matrx/design-system 的常见问题

@ai-matrx/design-system 是什么?

AI Matrx semantic React primitives shared across Vite, Next.js, desktop, and customer-built applications.

如何安装 @ai-matrx/design-system?

在项目目录运行 npm install @ai-matrx/design-system,或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖:react(>=18.0.0)、react-dom(>=18.0.0)。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖,Yarn 需要手动安装。它还提供命令行工具 matrx-check-close-in-flow,可以用 npx matrx-check-close-in-flow 直接运行。

@ai-matrx/design-system 支持 TypeScript 吗?

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

@ai-matrx/design-system 支持 ESM 还是 CommonJS?

同时提供 ES Module 与 CommonJS 入口,import 和 require() 都可以使用,打包工具会自动选择合适的版本。

@ai-matrx/design-system 需要什么版本的 Node.js?

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

@ai-matrx/design-system 可以商用吗?

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

@ai-matrx/design-system 的下载量有多少?

根据 npm 官方下载统计,@ai-matrx/design-system 在 2026/10/05 – 2026/11/03 这 30 天内被下载 174,463 次,日均约 5,815 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@ai-matrx/design-system 还在维护吗?

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