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

@tutti-os/ui-rich-text

v0.0.10

Published

Host-agnostic rich text foundations for Tutti frontend packages.

Readme

@tutti-os/ui-rich-text

Host-agnostic rich text foundations for Tutti frontend packages.

This package is the new home for the repository's rich text work. It is intended to own:

  • document normalization and plain-text extraction
  • generic markdown-link helpers and mention-link serialization
  • editor and readonly surfaces
  • plugin and mention runtime contracts
  • rich text extension registration

This package should not own workspace-domain semantics such as /workspace/... path policy, workspace-file markdown meaning, host file lookup, or product workflow-specific reference rules. Those stay with the owning workspace-domain package or host adapter.

Current migration status:

  • src/internal/ported-source/* is a direct snapshot of the old top-level richText/ directory so we can refactor from the current code instead of redesigning from memory.
  • src/core/richTextDocument.ts is the first promoted, host-agnostic surface extracted from that snapshot.
  • editor wrappers and current node extensions are intentionally not public yet because they still depend on app-specific imports and legacy host seams.
  • the package root export is intentionally narrow; core, editor, plugins, and types remain the explicit public subpaths

Known transitional seam:

  • current editor and readonly surfaces still embed workspace-reference semantics such as /workspace/... link handling and workspace reference presentation
  • treat that behavior as transitional implementation, not as the intended public contract of @tutti-os/ui-rich-text
  • before adding another host-specific inline reference protocol here, stop and re-evaluate the generic rich-text reference seam across real consumers

Current refactor plan:

  1. Promote host-agnostic document helpers from ported-source into core.
  2. Define a stable plugin contract for @, #, and future inline token triggers.
  3. Rebuild editor wrappers around injected host adapters instead of app-local imports.
  4. Keep domain-specific reference protocols in their owning packages and only promote the generic rich-text seam here when it is truly host-agnostic.

Mention protocol draft

The first stable plugin contract in this package is the @ mention protocol.

Boundary split:

  • the editor core owns trigger detection, selection state, keyboard handling, insertion lifecycle, and storage shape
  • the host plugin owns query behavior, suggestion copy, insert mapping, and reverse resolution

Stable stored attrs:

type RichTextMentionAttrs = {
  trigger: "@";
  plugin: string;
  entityId: string;
  label: string;
  href?: string;
  kind?: string;
  version?: string;
  meta?: Readonly<Record<string, string>>;
};

Why this shape:

  • plugin identifies which host capability owns the token
  • entityId is the durable identity and must not depend on visible copy
  • label is the last rendered fallback text so readonly and indexing can still work without a roundtrip
  • href, kind, version, and meta are optional extension points for host routing and compatibility

Plugin contract:

interface RichTextMentionPlugin<TItem = unknown, TResolved = unknown> {
  id: string;
  trigger?: "@";
  query: (
    input: RichTextMentionQueryInput
  ) => Promise<readonly TItem[]> | readonly TItem[];
  getItemKey: (item: TItem) => string;
  getItemLabel: (item: TItem) => string;
  getItemSubtitle?: (item: TItem) => string | null | undefined;
  getItemKeywords?: (item: TItem) => readonly string[] | undefined;
  toMention: (item: TItem) => RichTextMentionInsert;
  renderText?: (attrs: RichTextMentionAttrs) => string;
  resolveMention?: (
    input: RichTextMentionResolveInput
  ) =>
    | Promise<RichTextResolvedMention<TResolved>>
    | RichTextResolvedMention<TResolved>;
}

Interpretation:

  • query decides what @ can mention
  • getItemLabel and getItemSubtitle decide the suggestion copy
  • toMention maps a chosen item into the stored attrs shape
  • renderText lets the host override readonly text such as @Alice vs @Alice Chen
  • resolveMention maps stored attrs into a fixed display state plus an optional resolved entity payload

Display-state protocol:

type RichTextMentionRenderState = "active" | "missing" | "disabled" | "loading";

type RichTextResolvedMention<TResolved = unknown> = {
  state: RichTextMentionRenderState;
  label?: string;
  tooltip?: string;
  href?: string;
  entity?: TResolved;
};

Interpretation:

  • active means normal styling and normal interaction
  • missing means the original resource no longer exists and should render in a muted non-interactive style
  • disabled means the resource still exists but the current host should not allow normal interaction, for example because of permissions or product rules
  • loading is a temporary host-controlled resolution state

This package intentionally keeps the public status model visual and generic. Business-specific reasons such as deleted, archived, forbidden, or offline stay inside the host plugin and collapse into one of the fixed render states above.

Helpers now exported:

  • createRichTextMentionPlugin
  • createRichTextMentionAttrs
  • createRichTextMentionRegistry
  • createRichTextLinkMarkdown
  • getRichTextMentionDisplayText
  • isRichTextMentionAttrs
  • normalizeRichTextContent
  • resolveRichTextMentionView

Runtime surfaces now exported:

  • RichTextAtTextarea
  • RichTextMentionReadonly

Current runtime behavior:

  • the registry aggregates multiple @ plugins in declaration order
  • query results are flattened into a shared result shape with prebuilt mention attrs
  • resolve falls back to missing when the plugin no longer exists
  • readonly rendering maps the resolved state into one fixed visual shape family with overridable labels, tooltips, and click handling