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

@anacode-editor/code-editor

v0.12.0

Published

A self-contained, reusable multi-file code editor built on Monaco.

Readme

@anacode-editor/code-editor

A self-contained, multi-file code editor for the web — built on Monaco, packaged as a drop-in Svelte 5 component.

▶ Live demo  ·  Editor  ·  Import / Export  ·  Multi-editor

npm License: MIT Svelte 5 TypeScript Built on Monaco

File tree, tabs, and Monaco in a single <EditorSession> component (shown in both bundled themes).


What it is

@anacode-editor/code-editor provides the pieces around Monaco that a multi-file editing experience needs: a file tree, tabs, an in-memory file system, per-file save/dirty state, and detection of when a file has changed underneath the editor. It's packaged as a single Svelte 5 component you can drop in, or as individual services you can compose.

  • Drop-in or composable — render one <EditorSession>, or wire the underlying services yourself through subpath exports.
  • Works with real projects — the demo loads full open-source repositories (FastAPI, Caddy, Cobra, Resilience4j) from a zip.
  • Content-hash conflict detection — writes are compare-and-swap against a content hash, so a file changed elsewhere surfaces a conflict prompt rather than being overwritten.
  • Layered and typed — a documented architecture, interface-driven services, and Result<T, E> return types throughout.

Demos

Three routes in the deployed demo, each showing a different part of the package:

  • Editor — a multi-file editor in one component: file tree (create / rename / move / delete, drag-and-drop, search, context menu, VS Code icons), tabs with a breadcrumb, and per-file dirty/save state.
  • Import / Export — import a project as a .zip (or pick a bundled sample like FastAPI ~19 MB), edit in place, and download the result.
  • Multi-editor — two independent EditorSession instances bound to the same in-memory file system; a structural change in one is reflected in the other.

Features

Editing

  • Monaco-powered editor with multi-file tabs, breadcrumb, and per-file view-state (cursor, scroll, folds) preserved across switches
  • Configurable font size, tab size, word wrap, line numbers, minimap, and theme (bundled Night Owl & Tomorrow)
  • Light/dark aware

File system

  • In-memory, command → plan → event file system: every mutation is validated into a plan, then executed to atomic events (event-sourced, Immer-backed)
  • Create / rename / move / delete with a graph index that rejects illegal moves (cycle detection)
  • File-tree search, drag-and-drop, and a typed command registry

Saving & conflicts

  • Per-document draft state: dirty tracking, save / save-all / revert / force-overwrite
  • Content-hash conflict detection (compare-and-swap) — edits against a stale file raise a conflict you resolve via reload or overwrite
  • Handles files that disappear underneath the editor (invalid-document state) and reloads buffers when content changes on disk

Persistence

  • Zip import / export of the whole workspace, with pluggable strategies

Engineering

  • Layered, documented architecture (Core → Primitives → Orchestration → Composition)
  • Interface-driven services; Result<T, E> error handling instead of exceptions
  • Subpath exports and shipped .d.ts types

Quick start

Available on npm as @anacode-editor/code-editor:

npm install @anacode-editor/code-editor

# peer dependencies
npm install svelte bits-ui @lucide/svelte mode-watcher tailwindcss tw-animate-css

The snippet below is the condensed version of the fully runnable demo in src/routes/+page.svelte.

<script lang="ts">
	import { onMount, onDestroy } from 'svelte';
	import {
		EditorSession,
		EditorSessionFactory,
		NodeType,
		ROOT_NODE_ID,
		ROOT_PERMISSIONS,
		EMPTY_CONTENT_HASH,
		type FileSystemMapReadonly,
		type FileSystemPath,
		type NodeID,
		type IEditorSession
	} from '@anacode-editor/code-editor';
	import { FileSystemZipImporter } from '@anacode-editor/code-editor/persistence';
	import { StaticDefaultEditorConfigurationService } from '@anacode-editor/code-editor/config';
	import '@anacode-editor/code-editor/styles.css';

	// A minimal two-file workspace.
	const initialState: FileSystemMapReadonly = {
		[ROOT_NODE_ID]: {
			id: ROOT_NODE_ID,
			type: NodeType.FOLDER,
			name: 'project',
			path: '/project' as FileSystemPath,
			parentID: null,
			permissions: ROOT_PERMISSIONS,
			children: [1 as NodeID],
			userSpace: null
		},
		[1 as NodeID]: {
			id: 1 as NodeID,
			type: NodeType.FILE,
			name: 'hello.ts',
			path: '/project/hello.ts' as FileSystemPath,
			parentID: ROOT_NODE_ID,
			content: 'export const hello = () => "world";\n',
			contentHash: EMPTY_CONTENT_HASH,
			permissions: { read: true, write: true, rename: true, delete: true },
			userSpace: null
		}
	};

	let session: IEditorSession | null = $state(null);
	const factory = new EditorSessionFactory(new FileSystemZipImporter());
	const config = new StaticDefaultEditorConfigurationService();

	onMount(async () => {
		const result = await factory.createFromFileSystemMap(initialState, config);
		if (result.ok) session = result.value;
	});

	onDestroy(() => session?.dispose());
</script>

{#if session}
	<EditorSession {session} />
{/if}

The factory also offers createFromFileSystem(...) (bring your own IFileSystemService) and createFromZip(...). See src/routes/ for all three patterns running end-to-end.


Localization

The editor's own UI (file tree, tabs, dialogs, prompts, notifications) ships in English, French, and Spanish. Pass a locale — and optionally per-string overrides — as the last argument to any factory method:

const result = await factory.createFromFileSystemMap(initialState, config, {
	locale: 'fr',
	// optional: reword any string, in any locale
	overrides: { 'common.cancel': 'Abandonner' }
});

Only English is complete by design: it defines the key set and is the fallback for any key a locale (or your overrides) doesn't cover. The full key catalog and types are exposed via @anacode-editor/code-editor/localization. Locale is fixed per session — to switch languages, recreate the session.

Monaco's built-in UI (context menu, find widget, …) localizes separately: Monaco's locale is global per page and must be set before its module first evaluates, so it belongs to the host app. Load the matching NLS pack before importing anything editor-related:

if (locale === 'fr') await import('monaco-editor/esm/nls.messages.fr.js');
else if (locale === 'es') await import('monaco-editor/esm/nls.messages.es.js');
const { EditorSession, EditorSessionFactory } = await import('@anacode-editor/code-editor');

Skipping this is fine — Monaco's chrome simply stays English while the editor's own UI follows your locale.


Two ways to use it

| | Drop-in | Compose | | --------------- | ----------------------------- | -------------------------------------------------------------------------------------------------------- | | You write | <EditorSession {session} /> | wire the individual services yourself | | You get | the full editor experience | full control over file system, save, conflict, tree, tabs | | Import from | @anacode-editor/code-editor | @anacode-editor/code-editor/file-system, /session, /persistence, /state, /config, /shared, … |

The root entry exposes the drop-in surface; concern-scoped entry points expose the underlying services for composing the stack yourself. Internals not promoted to a barrel remain reachable via deep subpath imports.


Architecture

Dependencies flow downward only. The core is framework-agnostic — no Svelte and no Monaco below the UI layers.

Layer 3  Composition      EditorSession · EditorWorkspace · session factory
   ▲
Layer 2  Orchestration    presentation orchestrator · attachment port · projections
   ▲
Layer 1  Primitives       file-system · documents · save · conflict · tree · tabs · prompts
   ▲
Layer 0  Core             command→plan→event engine · graph index · hashing · Result types

The full design — package boundary, dependency rules, the command/plan/event and Result patterns, naming conventions — is documented in docs/Architecture.md.


Tech stack

Svelte 5 (runes) · TypeScript (strict) · Monaco · Tailwind CSS v4 · bits-ui (shadcn-svelte primitives) · graphology · Immer · JSZip · Vite · Vitest


Vendored ui-primitives

src/lib/ui-primitives/ is vendored from the shadcn-svelte registry (style nova, preset b3abuW3Imm) and stays byte-identical to registry output so npx shadcn-svelte add <name> --overwrite re-vendors stay cheap. Lint suppressions and formatter workarounds do not count as deviations. The only real deviations — re-apply them after any re-vendor — are localization (getEditorMessages() instead of hardcoded English):

  • dialog/dialog-content.svelte — sr-only close label
  • dialog/dialog-footer.svelte — close button label
  • breadcrumb/breadcrumb.sveltearia-label
  • breadcrumb/breadcrumb-ellipsis.svelte — sr-only "more" label

Consumers must import shadcn-svelte/tailwind.css alongside styles.css (peer dependency); it provides the data-open/data-closed/… variants the primitives rely on.


License

MIT