npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@noeta-cloud/ui-editor

v0.3.0

Published

Noeta's embeddable React components: the membership panels, the table editor, the document editor, the file editors, the thread chat and the NoetaEditor dispatcher. Composes @noeta-cloud/ui-core.

Readme

@noeta-cloud/ui-editor

Noeta's embeddable React components. Noeta's own screens consume these same components — the embedded surface and the first-party UI are one codebase, never a fork.

Layering

  • <NoetaProvider api auth inviteLink> — injection context for the membership surfaces. The package never imports app singletons; the host supplies an API client satisfying MembershipApi, an auth adapter satisfying NoetaAuthAdapter (the app passes its Better Auth client — it matches structurally), and the invite-link builder.
  • <MembersPanel> / <TeamsPanel> — the individual membership surfaces (people + invites; teams).
  • <MembershipPanel workspaceId> — both, stacked, for embedders who want the whole thing.
  • <TableView resourceId> — the table sub-editor: a live, collaborative grid over a Noeta table resource (also at the ./table subpath, without the membership panels). It reads and writes the same Yjs row model every other Noeta surface uses (table-ops, exported), so an edit in the grid is the edit an agent or a published page sees. source widens what it shows: { kind: "file", resourceId } reads a CSV file resource (parsed exactly as import_csv would, live to the file's room) and { kind: "rows", columns, rows } shows rows the host already holds. Only a table source is editable; readers can still sort by a column and resize columns locally, neither of which is written back.
  • <RealtimeProvider realtime> / useRealtime() — how a component finds its realtime connector. Inside the Noeta app no provider is mounted: the default is a same-origin, cookie-authenticated connector. <NoetaEmbedProvider> mounts a cross-origin one.

Embedding the table editor

import { NoetaEmbedProvider, TableView } from "@noeta-cloud/ui-editor/table";

<NoetaEmbedProvider baseUrl="https://acme.noeta.cloud" token={getEmbedToken} workspaceId={wsId} userId={sub}>
  <TableView resourceId={tableId} readOnly={!settings.allowEdits} fillHeight />
</NoetaEmbedProvider>

The token must be minted with surfaces: ["table"] (POST /api/embed/token, server-to-server). The grid is pure realtime — it calls no /api route — so the token rides the WebSocket upgrade URL as ?embed_token= (a browser can set neither a header nor a cross-site cookie on a handshake); the getter form is re-read before every reconnect, so re-minting needs no remount.

Write-capability is a setting AND a cap. readOnly renders cells as text and hides the row/column tools — an affordance. The server write-gates every socket on the reader's own role, min(their role, the token's role_cap), independently. A host that wants a genuinely read-only embed sets readOnly and mints with role_cap: "viewer"; a host that wants edits sets neither.

yjs is a peer dependency (one instance — the provider must recognise the Y.Doc it syncs).

Embedding the document editor

import { NoetaEmbedProvider, DocEditor } from "@noeta-cloud/ui-editor/doc";
import "@blocknote/core/fonts/inter.css";
import "@blocknote/mantine/style.css";

<NoetaEmbedProvider baseUrl="https://acme.noeta.cloud" token={getEmbedToken} workspaceId={wsId} userId={sub}>
  <DocEditor resourceId={docId} workspaceId={wsId} readOnly={!settings.allowEdits} user={{ name, color }} />
</NoetaEmbedProvider>

Mint with surfaces: ["doc"]. The document itself is the same realtime room Noeta opens (the surface grants it); the editor's chrome — inline media upload, the "@" mention picker, docLink titles, the workspace doc default and the comment layer — is the short /api allowlist the surface names, every call still authorized against the user's own role. workspaceId enables comments and mentions; without it the editor is a plain writing surface. readOnly sets BlockNote non-editable and hides the toolbar — again an affordance; role_cap: "viewer" is the cap. Without a conn the editor opens its own connection (useLiveDoc) and waits for the first sync before mounting, so it never offers an editable surface over a document that has not arrived.

BlockNote and ProseMirror are dependencies of this package but stay external to its build so your bundle holds one copy; load their two stylesheets yourself as above. A linkedApp block (a hosted Noeta app) renders as a placeholder unless the host injects a renderer through <NoetaProvider linkedApp> — the app host is deliberately not part of this package. Inline images and print are plain browser GETs the embed token cannot ride yet (see the plan's phase 2 notes).

Embedding a file, or "the editor"

import { NoetaEmbedProvider, TextFileEditor, BlobFileView } from "@noeta-cloud/ui-editor/file";

<NoetaEmbedProvider baseUrl="https://acme.noeta.cloud" token={getEmbedToken} workspaceId={wsId} userId={sub}>
  <TextFileEditor resourceId={fileId} name="index.html" mediaType="text/html" readOnly={!settings.allowEdits} />
  <BlobFileView resourceId={uploadId} name="logo.png" />
</NoetaEmbedProvider>

Mint with surfaces: ["file"]. A text file is the same realtime room Noeta opens (the surface grants it), edited in place by a CodeMirror editor bound to its Y.Text — the very text agents write over MCP. A binary file is its blob's metadata plus the bytes, which the component fetches THROUGH the client (bearer) and renders as an object URL, since an <img src> cannot carry a token; the surface allowlists that one bytes route. An HTML file shows its source only — the sandboxed preview is a host-injected renderer (<NoetaProvider filePreview>), like linkedApp.

NoetaEditor (root entry) is the dispatcher: give it a resource id and it opens the document editor, the table grid, the code editor or the blob view, looking the type up through GET /api/resources/:id (every surface allowlists it) unless you pass resource yourself.

@noeta-cloud/ui-editor/code is the code editor and the viewers as plain components over YOUR data — CodeEditor (content + onChange, or a collab binding), CodeSearchBar, VimStatusBadge, ImageViewer, HexViewer — with no Yjs, realtime or provider in its graph. RemADE mounts this over its in-memory tabs. CodeMirror is a dependency kept external (one copy in the host; a second @codemirror/state fails with "Unrecognized extension value" — dedupe it like React if your checkout links this package).

Embedding a thread chat

import { NoetaEmbedProvider, ThreadChat } from "@noeta-cloud/ui-editor/thread";

<NoetaEmbedProvider baseUrl="https://acme.noeta.cloud" token={getEmbedToken} workspaceId={wsId} userId={sub}>
  <ThreadChat
    threadId={threadId}
    nameOf={(id) => people[id]?.name ?? id}
    appName={() => "Acme Insights"}
    renderData={(m) => <MyAttachment data={m.data} />}
    className={styles.chat}
  />
</NoetaEmbedProvider>

Mint with surfaces: ["thread"]. ThreadChat is the conversation only — the live timeline (history paging, new messages, edits and deletes as they land) and a composer — with no header, since audience, topics and archive belong to your chrome. A message's data (any JSON object your app attached) renders through renderData, so an attached chart spec can show as your own live preview. A message with a persona renders as an agent (square avatar, an "Agent" tag, never grouped with its user's own messages) — only an app credential may set one. Theme it with --noeta-thread-* custom properties on your className. For your own chrome entirely, use useThreadTimeline(threadId): the same paging, de-duplication and reconnect, no markup.

Contract (src/contract.ts)

The package owns minimal structural wire types (TeamView, PersonView, …) and its own copy of the invitable-role vocabulary and the embed-token URL param. The Noeta server's modules stay the product SSOT, and a drift guard there fails the build if the copies diverge. Richer host types satisfy these interfaces structurally, without the package depending on any server schema.

Consumption

npm install @noeta-cloud/ui-editor @noeta-cloud/ui-core yjs

Entries ship built JS + .d.ts, so no particular bundler is assumed: @noeta-cloud/ui-editor (everything, plus NoetaEditor), @noeta-cloud/ui-editor/table (the grid and its realtime/model layer alone), @noeta-cloud/ui-editor/doc (the document editor, its blocks, comments and the doc model), @noeta-cloud/ui-editor/code (the CodeMirror editor and the image/hex viewers over your own data) and @noeta-cloud/ui-editor/file (./code plus the live text-file editor and blob view). Load the stylesheets once, at your app's entry point — the components carry no inline styles and render unstyled without them:

import "@noeta-cloud/ui-core/fonts.css";
import "@noeta-cloud/ui-core/tokens.css";
import "@noeta-cloud/ui-core/base.css";
import "@noeta-cloud/ui-core/styles.css";
import "@noeta-cloud/ui-editor/styles.css";
// only if you mount <DocEditor>:
import "@blocknote/core/fonts/inter.css";
import "@blocknote/mantine/style.css";

React, react-dom, lucide-react, yjs, and @noeta-cloud/ui-core are peer dependencies and stay external to the build. React must resolve to a single instance — two copies null the hooks dispatcher and the app renders blank with Cannot read properties of null (reading 'useState'). With a linked/monorepo checkout, dedupe it explicitly (in Vite: resolve.dedupe: ["react", "react-dom", "react/jsx-runtime"]).

The ./teams.module.scss, ./members.module.scss and ./table.module.scss subpaths are raw Sass, exported for hosts composing our styles into their own components; those require a Sass-capable bundler.