@nx-lang/value-view

中文文档
0.10.0发布于 2026/11/02MITTypeScriptES Module零依赖构建来源已验证

An custom element that shows an NX value as NX text: highlighted with the editors' grammar, folding where it is long, and explaining itself on hover. No framework.

原始使用文档
文档目录10 节
  1. A plain page
  2. React
  3. Properties and attributes
  4. Folding
  5. Hover
  6. Going to a declaration
  7. Buttons on nodes
  8. Styling
  9. Development
  10. Host-driven selection

@nx-lang/value-view

An <nx-value> custom element that shows an NX value the way NX writes it: the text nxlang run prints, highlighted with the grammar the NX editors use, folding where it spans several lines, and explaining each part on hover. It has no framework dependency and keeps its markup and styles in a shadow root, so it drops into a plain page, a React app, a docs site or a VS Code webview alike.

The value it shows is the annotated text NxProgramArtifact.formatValue(value) returns in @nx-lang/sdk-wasm: { text, nodes }, where each node says what one part of the text is (a record, a property, a sequence, a case, a scalar) with its type and, when the program declares it, where.

A plain page

<nx-value id="output"></nx-value>

<script type="module">
  import "@nx-lang/value-view";
  import { evaluateFunction, prepareNxIrProgram } from "@nx-lang/ir-runtime";
  import { createNxHost, compileNxModule } from "@nx-lang/sdk-wasm";
  // With a bundler such as Vite; otherwise, wherever the page serves the package's `nx.wasm`.
  import nxModuleUrl from "@nx-lang/sdk-wasm/nx.wasm?url";

  const host = createNxHost(await compileNxModule(fetch(nxModuleUrl)));
  const artifact = host.buildProgramArtifact(`type User = { id:string name:string }
<User id="1" name="Ada" />`);
  try {
    document.getElementById("output").value = artifact.formatValue(evaluateFunction(prepareNxIrProgram(artifact.generateNxIr()[0].bytes), "root"));
  } finally {
    artifact.dispose();
  }
</script>

Importing the package defines the element. Where there is no DOM, as in a server render, importing it does nothing, and defineNxValueElement(registry) defines it in a registry of your choosing.

React

React 19 passes properties to custom elements directly, so no wrapper is needed:

import "@nx-lang/value-view";
import type { NxValueText } from "@nx-lang/value-view";

export function Output({ value, stale }: { value: NxValueText; stale: boolean }) {
  return <nx-value value={value} stale={stale} />;
}

Declare the element for JSX once in your app:

import type { NxValueElement } from "@nx-lang/value-view";

declare module "react" {
  namespace JSX {
    interface IntrinsicElements {
      "nx-value": React.DetailedHTMLProps<React.HTMLAttributes<NxValueElement>, NxValueElement> &
        Partial<Pick<NxValueElement, "value" | "stale" | "truncated" | "describe" | "actions" | "highlighter">>;
    }
  }
}

Properties and attributes

NameKindMeaning
valuepropertyThe { text, nodes } to show
truncatedproperty and attributeA notice after the text says the rest was cut
staleproperty and attributeThe text is muted and badged "Out of date"; hover and folding still work
describepropertyThe host's hover for a node; see below
actionspropertyThe host's buttons for a node; see below
highlighterpropertyA Shiki highlighter to share, with the NX grammar and both themes loaded
themesproperty{ light, dark } Shiki theme names; default github-light, github-dark

Without a highlighter, the element loads Shiki's core, the oniguruma engine and the two themes on first use, once for the whole page, and shows the value uncolored until they arrive. A page that already colors with Shiki can pass its highlighter instead; nxShikiLanguage() returns the grammar in the form Shiki takes, for building one.

A value longer than MAX_COLORED_CHARACTERS (20,000) is shown uncolored: coloring runs on the main thread over the whole text, and at that size it would hold up the page on every change. It still folds, hovers and copies.

Do not pass the highlighter behind a Monaco editor. The element colors with a light and a dark theme, which switches the highlighter's current theme, and @shikijs/monaco tokenizes with whatever theme that is, so the editor would recolor from the wrong palette. The NX playground lets the element load its own for this reason.

setFolded(index, folded) and isFolded(index) fold and read the node at index of value.nodes.

Folding

Every record, property and sequence whose text spans more than one line gets a toggle to its left. A folded node shows its first line and an ellipsis; either the toggle or the ellipsis unfolds it. Everything starts unfolded, and copying always copies the whole text.

Hover

Pointing at a part of the value, or moving focus to it with the arrow keys, shows a description of the innermost node there. By default the description comes from the node alone, spelled as the NX language service spells hovers:

(property) done: boolean

A host that knows more sets describe. It receives the node, its index and the value, and returns markdown, or undefined for the default, synchronously or as a promise. The playground answers with the language service's hover at the node's declaration, so hovering User in the output shows what hovering its declaration in the source shows:

element.describe = async (node) =>
  node.declaration === undefined ? undefined : await hoverMarkdownAt(node.declaration);

The element renders the markdown hovers use: fenced nx blocks, highlighted, and paragraphs with inline code.

Going to a declaration

Clicking a node with a declaration, or pressing Enter on it, fires nx-value-navigate. Its detail is { node, index, declaration }, where declaration is the span in the program's source, with 1-based lines and columns:

element.addEventListener("nx-value-navigate", (event) => {
  const { declaration } = event.detail;
  editor.revealLine(declaration.startLine);
});

Buttons on nodes

A host can put buttons on the value with actions, a function from a node, its index and the value to a list of { id, label, title? }, or undefined for none. Each button sits at the end of its node's first line, stays visible when the node is folded, and fires nx-value-action with { node, index, id } when pressed, by click or from the keyboard. The buttons are not part of the text, so copying leaves them out, and while the value is stale they are disabled.

The playground's preview puts one on each action handler in a component's output, found by the record type the SDK's formatValue spells a handler with, and dispatches through the node's pointer:

element.actions = (node) =>
  node.role === "record" && node.type === "ActionHandler"
    ? [{ id: "dispatch", label: "Tapped", title: "Dispatch Button.Tapped" }]
    : undefined;
element.addEventListener("nx-value-action", (event) => {
  session.dispatchHandler(event.detail.node.pointer, { $type: "Button.Tapped" });
});

Styling

The element follows the page's color-scheme: token colors use light-dark(), so a page that sets color-scheme: dark gets the dark theme. These custom properties restyle it:

PropertyDefault
--nx-value-font-familythe system monospace stack
--nx-value-font-size13px
--nx-value-line-height1.5
--nx-value-foregroundCanvasText
--nx-value-backgroundtransparent
--nx-value-padding8px 12px 8px 24px
--nx-value-bordera light or dark gray
--nx-value-hover-backgrounda light or dark blue tint
--nx-value-hover-popup-backgroundwhite, or a dark gray
--nx-value-action-backgrounda light or dark blue
--nx-value-action-foregrounda dark or light blue
--nx-value-action-bordera translucent blue
--nx-value-action-hover-backgrounda deeper blue

Development

pnpm install
pnpm --filter @nx-lang/value-view test

The tests run the element under jsdom with node --test.

Host-driven selection

Call element.selectNodes(indices) to highlight all matching nodes of the current value. It unfolds their ancestors and scrolls the first match into view, without firing navigation events. Pass false as the second argument to update highlighting without scrolling. Selection survives recoloring and is cleared when value is replaced. Hosts should scope indices to their current output and source snapshot; source/provenance matching belongs to the host's preview adapter.

版本与发布记录

@nx-lang/value-view 共发布 6 个版本,首次发布于 2026/09/29;过去 12 个月发布了 6 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
0.10.0latest2026/11/021 天前
0.9.02026/10/268 天前
0.8.02026/10/1816 天前
0.7.02026/10/1024 天前
0.6.02026/10/0628 天前
0.5.02026/09/291 个月前

关于 @nx-lang/value-view 的常见问题

@nx-lang/value-view 是什么?

一个自定义元素,它将NX值显示为NX文本:用编辑器的语法突出显示,在较长的地方折叠,并在悬停时解释自己。没有框架。

如何安装 @nx-lang/value-view?

在项目目录运行 npm install @nx-lang/value-view,或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖:shiki(^4.0.0)、@nx-lang/language(>=0.1.0)。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖,Yarn 需要手动安装。

@nx-lang/value-view 支持 TypeScript 吗?

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

@nx-lang/value-view 支持 ESM 还是 CommonJS?

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

@nx-lang/value-view 需要什么版本的 Node.js?

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

@nx-lang/value-view 可以商用吗?

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

@nx-lang/value-view 还在维护吗?

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