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 🙏

© 2024 – Pkg Stats / Ryan Hefner

sketchzone

v0.0.11

Published

![Build passing](https://github.com/robsimmons/sketchzone/actions/workflows/compile.yml/badge.svg) [![NPM Module](https://img.shields.io/npm/v/sketchzone.svg)](https://www.npmjs.com/package/sketchzone)

Downloads

276

Readme

sketchzone

Build passing NPM Module

You want to create a little inspector for your programming language or data structure or something: your user writes some text document and you will use your "inspector" to show them something --- some JavaScript widget that will render something based on the text the user provided provided.

I talked more about the motivation behind sketchzone in this blog post. The sketchzone package is intended to encapsulate a bunch of "price of entry" quality-of-life issues that a browser-based implementation of such a tool is going to encounter. You provide the inspector, and sketchzone provides:

  • Codemirror integration for the editor
  • Persistance of multiple sketches via IndexedDB
  • Multiple tabs
  • Browse/reopen closed documents
  • Sharable links using URL hashes
  • A mobile-friendly mode that switches between the editor and inspector
  • Light/dark mode

This is fundamentally made for an audience of one (me), but reach out if you'd like to use it yourself and run into trouble. sketchzone is currently licensed under the GPL-3 and CC-BY-NC-SA licenses (whichever you prefer), but if neither of those licenses work for you let me know.

Implementing an inspector

Your job if you're using this library is to implement the types in impelementer-types.ts. Specifically, you need to provide a function createAndMountInspector that takes a DOM element and a string document, creates and mounts the inspector to the given DOM element, and optionally returns an Inspector object.

I highly recommend using your own codemirror extensions rather than relying on the defaults:

import { EditorView, keymap, lineNumbers } from '@codemirror/view';
import { defaultKeymap, history, historyKeymap } from '@codemirror/commands';

const codemirrorExtensions = [
  lineNumbers(),
  history(),
  EditorView.lineWrapping,
  keymap.of([...defaultKeymap, ...historyKeymap]),
];

Examples

These examples are all built on Glitch as a static site that uses Vite to roll up sketchzone's dependencies on React and Codemirror.

  • Simplest possible example - deceptively simple enough, uses defaults for everything. Loading a document just displays its length.
  • Simple example - a better example of a basic configuration, which uses a button to show off how tabs maintain their own inspectors.
  • Simple example (react) - sketchzone works really well with writing a simple inspector in React. This is exactly the same as the last simple example, but built with React instead of injecting using innerHTML to slam a bunch of HTML into the document.

By returning an object containing 1-4 functions from the createAndMountInspector() function, the behavior of sketchzone can be configured to support a couple of different uses cases:

  • Using unmount() to always unload - the unmount() function is called whenever you are about to stop viewing an inspector, and in this example, we return a truthy value from the unmount() function so that sketchzone will terminate and destroy the inspector.
  • Using destroy() to reclaim resources - if an inspector uses resources that need to be reclaimed when the tab is closed for good, that can be done in the destroy() function.
  • Using reload() to stick around - the default behavior is to unmount, destroy, and re-initialize an inspector whenever the reload button is pressed. It's possible to keep the inspector around by defining reload().
  • Using unmount() and remount() to pause - having unmount() return true can keep tabs that aren't open from consuming resources, but if you want to do a little bit more work to tell the inspector how to suspend itself when it's unmounted, and then resume when it's remounted, it's possible to conserve resources without deleting all the user's state.

Some more advanced examples:

Structure

Internally the thing the user edits is called a document, and the thing that you must define in order to use sketchzone in your project is the "inspector." These are the names that sketchzone uses to talk about itself:

C /-------------------------------------\
o | Tab1 x  Tab2 x  Tab3 x  Ta[+] Logo  |
n | /--------\ /----------------------\ |
f | | Editor | | Inspector controller | |
i | |        | \----------------------/ |
g | |        | /----------------------\ |
  | |        | | Inspector            | |
m | |        | |                      | |
e | |        | |                      | |
n | \--------/ \----------------------/ |
u \-------------------------------------/

The app assumes it has full control over the window, and that the body of the HTML document looks like this:

<body id="body-root">
  <main id="main-root">
    <div id="sketchzone-config"></div>
    <div id="sketchzone-container">
      <div id="sketchzone-header">
        <div id="sketchzone-tabs"></div>
        <div id="sketchzone-logo"></div>
      </div>
      <div id="sketchzone-active-sketch">
        <div id="sketchzone-codemirror-root"></div>
        <div id="sketchzone-divider"></div>
        <div id="sketchzone-inspector-root">
          <div id="sketchzone-inspector-controller" class="zone1"></div>
          <div id="sketchzone-inspector-contents"></div>
        </div>
      </div>
    </div>
  </main>
  <div id="modal-root"></div>
</body>

Configuring Style

Many CSS variables attached to the <body> element are intended to be modified for specific users of sketchzone.

Style

body {
  --sketchzone-mono-font-family: 'Fira Mono', monospace;
  --sketchzone-ui-font-family: 'Fira Sans Condensed', sans-serif;
  --sketchzone-line-numbers-font-family: var(--sketchzone-mono);
  --sketchzone-radius: 8px;
  --sketchzone-button-height: 2rem;
}

Dimensions

body {
  --sketchzone-outer-padding: 12px;
  --sketchzone-small-padding: 8px;
  --sketchzone-large-padding: 16px;
  --sketchzone-tab-bottom-padding: 10px;
  --sketchzone-fixed-padding: 16px;

  --sketchzone-outer-padding-narrow: 4px;
  --sketchzone-small-padding-narrow: 6px;
  --sketchzone-large-padding-narrow: 10px;
}

When the page width is narrower than 650px, a media query switches in the -narrow variants, as well as going from a 2-pane view showing both the text and the inspector a 1-pane view that switches between the text and the inspector.

Vertical height is determined by the following:

------------------------------------
| --sketchzone-outer-padding
------------------------------------ begin main rectangle
| --sketchzone-small-padding
------------------------------------
| --sketchzone-button-height
| Tab switcher buttons & Logo
------------------------------------
| --sketchzone-tab-bottom-padding
------------------------------------ begin sketch-specific rectangles
| --sketchzone-sketch-height (calculated)
------------------------------------ begin sketch-specific rectangles
| --sketchzone-small-padding
------------------------------------ end main rectangle
| --sketchzone-outer-padding
------------------------------------

Color scheme

Light and dark mode both use 10 colors. The scheme here uses OKLCH to maintain a consistency of relative perceptual brightness when switching between light and dark mode, while keeping text in a range that allows light mode to have vibrant and contrasting color schemes.

body {
  /**** Dark mode ****/
  /* Zone 1 is the area where config and codemirror lives */
  --sketchzone-dark-1-background: oklch(27% 0 0);
  --sketchzone-dark-1-text: oklch(67% 0 0);
  --sketchzone-dark-1-active-button-background: oklch(32% 0 0);
  --sketchzone-dark-1-active-button-text: oklch(72% 0 0);

  /* Zone 2 is the desaturated area where the tab switcher lives */
  --sketchzone-dark-2-background: oklch(37% 0 0);
  --sketchzone-dark-2-text: oklch(77% 0 0);
  --sketchzone-dark-2-active-button-background: oklch(42% 0 0);
  --sketchzone-dark-2-active-button-text: oklch(82% 0 0);

  /* Color of the modal background and drop shadows */
  --sketchzone-dark-shadow: oklch(72% 0 0);
  --sketchzone-dark-overlay: oklch(72% 0 0 / 30%);

  /**** Light mode ****/
  /* Zone 1 is the area where config and codemirror lives */
  --sketchzone-light-1-background: oklch(97% 0 0);
  --sketchzone-light-1-text: oklch(57% 0 0);
  --sketchzone-light-1-active-button-background: oklch(92% 0 0);
  --sketchzone-light-1-active-button-text: oklch(52% 0 0);

  /* Zone 2 is the desaturated area where the tab switcher lives */
  --sketchzone-light-2-background: oklch(87% 0 0);
  --sketchzone-light-2-text: oklch(47% 0 0);
  --sketchzone-light-2-active-button-background: oklch(82% 0 0);
  --sketchzone-light-2-active-button-text: oklch(42% 0 0);

  /* Color of the modal background and drop shadows */
  --sketchzone-light-shadow: oklch(52% 0 0);
  --sketchzone-light-overlay: oklch(52% 0 0 / 30%);
}