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

papyrus

v0.5.9

Published

Embedded code editor leveraging PrismJS

Downloads

651

Readme

𓁁 Papyrus

Embedded syntax highlighting and code editing with textarea enhancements leveraging PrismJS. Papyrus is a drop-in solution for code sample showcasing which offers theme customizations and ships pre-stacked.

Documentation + Custom Theming: papyrus.js.og

Reasoning

Papyrus was created to help alleviate some of cumbersome configuration incurred for showcasing code snippets in documentation and websites (specifically the Æsthetic Documentation). I love PrismJS, it's dope and does dope shit, but going beyond the defaults and bare minimum can be a tad extraneous when you're seeking high level customizations. I wanted something more flexible and integrated, something which provided basic level text editing capabilities. The result is Papyrus.

Benefits

Papyrus extends upon the default grammars provided by Prism which allows for more control of syntax highlighting. It applies a more advanced capture approach so token colors are more granular with Papyrus theming. The module comes pre-packaged and supports only a small subset of languages common in font-end development, you simply drop it with no additional configuration required. The text editor capabilities are perfect for quick showcases while adhering to common expectations when writing code.

Papyrus is tiny, it's only 12kb ~ gzip and supports the following languages

  • XML
  • HTML
  • Liquid
  • CSS
  • SCSS
  • JSON
  • YAML
  • Bash (Shell)
  • JavaScript
  • TypeScript
  • JSX
  • TSX

Limitations

Papyrus is appropriating PrismJS and extending its grammars. PrismJS is not built for incremental code edits or frequent changes so performance bottleneck's are incurred at around 3k~loc. There is some rumblings that PrismJS will improve its parse algorithm for use cases such as that being appropriated by Papyrus and so future in versions we may see better performance overall in when working (editing) large documents.

If you require support for large files which exceed 3k~loc maybe choose ACE, Monaco, CodeMirror or Copenhagen. Papyrus is not designed to be a full-featured editor, it's intended use case is for basic code editing and code sample showcasing.

Note Papyrus does not provide completions or intelliSense capabilities.

Installation

Papyrus ships with PrismJS pre-installed, so you only need this module.

$ pnpm add papyrus

Options

Papyrus defaults to using the following options.

| Option | Default | Description | | -------------------- | ------- | ----------------------------------------------------------- | | autoSave | false | Saves text edits to local storage between refreshes | | autoClosingPairs | [] | A list of characters to indent onto newlines | | editor | true | Enable/Disable the text editor feature | | history | true | Record history | | id | null | Assign a custom id for querying the model instances | | indentSize | 2 | The size of indentation | | indentChar | | The indentation character | | indentMultiline | true | Whether or not multiline tab indent is supported | | input | '' | The input code, fallbacks to <code> inner HTML | | language | null | The language id, fallbacks to <code> class name reference | | lineHighlight | true | Whether or not to highlight lines | | lineIndent | true | Whether or not to preserve indentation levels on newlines | | lineNumbers | true | Whether or not to show line numbers | | locLimit | 1500 | The maximum lines of code to allow | | newlineIndentPairs | [] | A list of characters to indent onto newlines | | showSpace | false | Show invisible whitespace characters | | showTab | false | Show invisible tab characters | | showCRLF | false | Show invisible LF + CR character combinator sequences | | showLF | false | Show invisible LF (newline) characters | | showCR | false | Show invisible CR (carriage return) characters | | spellcheck | false | Allow spellchecking in the text editor | | tabIndent | false | Allow tab indentation on selections | | trimStart | true | Strip leading extraneous newlines and whitespace | | trimEnd | true | Strip ending extraneous newlines and whitespace |

Usage

Because the module acts a wrapper around PrismJS the applied syntax highlighting is intended to be as simple as possible. There are 3 different distribution bundles available depending on how you wish to invoke and use Papyrus. You may also prefer leverage attributes on <pre> elements to customize operations using data-papyrus-* annotations or alternatively use the default function on the exported namespace.

Take the following markup in a static HTML file:

<html>
  <head>
    <link href="papyrus.css" rel="stylesheet">
    <script src="papyrus.js"></script>
  </head>
  <body>

    <pre class="papyrus">
      <code class="language-liquid">
        {{ object.prop }}
      </code>
    </pre>

    <pre class="papyrus">
      <code class="language-js">
        const foo = () => console.log('bar');
      </code>
    </pre>

  </body>
</html>

You would initialize Papyrus and have all the above code regions apply highlighting using the below options provided. Code regions which contain attributes instruct Papyrus to adhere to the options passed. To apply syntax highlighting on the above code dynamically (i.e: in the browser) you will call the following in your bundle:

import papyrus from 'papyrus';

papyrus();

Methods

The default export exposes the following methods. Most cases you'll use papyrus.mount() which will give you refined control over the code block instance

import papyrus from 'papyrus';

// BROWSER ONLY - Highlight/activate editor mode
papyrus(options?: {})

// BROWSER ONLY - Returns all instances of Papyrus
papyrus.model: Map<string, Model>;

// BROWSER ONLY - Get a model by its id or return all models
papyrus.get(id?: string: Model | Model[];

// BROWSER ONLY - Mount and activate the editor
papyrus.mount(element: HTMLPreElement, options?: {}): Model;

// BROWSER ONLY - Static method which work identical to papyrus.create(), returns an element.
papyrus.render(code: string, options?: {}):  HTMLPreElement;

// NODE USAGE - Usage within Node, returns a HTML string.
papyrus.create(code: string, options?: {}): string;

All instances of papyrus.model are stored in a Map and can be accessed via window.papyrus in the browser.

Mount

The mount import returns a model instance which allow you to work with the code regions. It's more feature filled than simply invoking the default papyrus() import. This is typically what you'll be using to control and activate the text editor. If you're working with a SPA (virtual DOM) like React or Mithril, than this is the method you should leverage.

import papyrus from 'papyrus';

const p = papyrus.mount(document.querySelector('pre'), {
  id: 'some-id',
  autoSave: true,
  autoClosingPairs: [
    ['"', '"'],
    ["'", "'"],
    ['(', ')'],
    ['{', '}'],
    ['[', ']']
  ],
  newlineIndentPairs: [
    ['{', '}'],
    ['[', ']']
  ],
  editor: true,
  indentChar: ' ',
  indentSize: 2,
  input: '',
  language: null,
  lineHighlight: true,
  lineIndent: true,
  lineNumbers: true,
  locLimit: 1500,
  indentMultiline: true,
  tabConvert: true,
  spellcheck: false,
  showCRLF: false,
  showSpace: false,
  showCR: false,
  showLF: false,
  showTab: false,
  tabConvert: true,
  trimEnd: true,
  trimStart: true
});

Create

The papyrus.create() method is for usage within Node. The method returns a string and if you're leveraging a SSG like 11ty or transforming markdown with something like markdown-it, then you be using create.

import papyrus from 'papyrus';

const p = papyrus.create('...', {
  id: 'some-id',
  autoSave: true,
  editor: true,
  indentChar: ' ',
  indentSize: 2,
  input: '',
  language: null,
  lineHighlight: true,
  lineIndent: true,
  lineNumbers: true,
  locLimit: 1500,
  spellcheck: false,
  showCRLF: false,
  showSpace: false,
  showCR: false,
  showLF: false,
  showTab: false,
  trimEnd: true,
  trimStart: true,

  // Additional Options
  addClass: {
    pre: [],
    code: []
  },
  addAttrs: {
    pre: [],
    code: []
  }
});

Render

The papyrus.render() method is for cases where you want to return a HTMLPreElement and insert it somewhere into the DOM. It works identical to papyrus.create() but returns an element instead of a string.

import papyrus from 'papyrus';

const p = papyrus.render('...', {
  id: 'some-id',
  autoSave: true,
  autoClosingPairs: [
    ['"', '"'],
    ["'", "'"],
    ['(', ')'],
    ['{', '}'],
    ['[', ']']
  ],
  newlineIndentPairs: [
    ['{', '}'],
    ['[', ']']
  ],
  editor: true,
  indentChar: ' ',
  indentSize: 2,
  input: '',
  language: null,
  lineHighlight: true,
  lineIndent: true,
  lineNumbers: true,
  locLimit: 1500,
  indentMultiline: true,
  tabConvert: true,
  spellcheck: false,
  showCRLF: false,
  showSpace: false,
  showCR: false,
  showLF: false,
  showTab: false,
  tabConvert: true,
  trimEnd: true,
  trimStart: true,

  // Additional Options
  addClass: {
    pre: [],
    code: []
  },
  addAttrs: {
    pre: [],
    code: []
  }
});

Model

The model method is a getter which returns the instances of Papyrus. Each instance of Papyrus will return a Model. The model will give you access and control over the code regions, enable/disable the editor and generally allow you to refine behavior. You can generate a model from the default papyrus() import and the papyrus.mount() method. The papyrus.create() and papyrus.render() methods do not generate models, they are static in nature.

import papyrus from 'papyrus';

// CREATE MODEL

const p = papyrus.mount(document.querySelector('pre'));

// READ ONLY

readonly p.mode: 'static' | 'error' | 'editing';    // The current mode
readonly p.lines: number;                           // The number of lines
readonly p.language: Languages;                     // The current language id
readonly p.pre: HTMLPreElement;                     // The HTML `<pre>` element
readonly p.code: HTMLElement;                       // The HTML `<code>` element
readonly p.textarea: HTMLTextAreaElement;           // The HTML `<textarea>` element
readonly p.raw: string;                             // The raw string of the `textarea`

// METHODS

// Update editor options, omitting the param returns current options.
p.options(options?: EditorOptions): EditorOptions

// Listener event which invokes each time code changes in editing mode.
p.onUpdate<T = any>(callback:(input: string, language: Languages), scope?: T): false | string | void

// Update the input, optionally provide a language id to change the language mode.
p.update(input: string, language?: Languages): void

// Disable editor mode, makes code input readonly
p.disableEditor(): void

// Enable editor, makes code editable
p.enableEditor(): void

// Show a custom error in the code editor
p.showError: (input: string, context?: { title?: string; stack?: string, heading?: string }) => void;

// Hide the error that was shown
p.hideError(): void


// Returns the models
window.papyrus: Map<string, Model>;

Theming

The current theming is made available via CSS and SCSS.

SCSS Variables

You can import the SCSS file using @import "papyrus";

/* -------------------------------------------- */
/* BASE STYLES                                  */
/* -------------------------------------------- */

// FONT FAMILY
$papyrus-font-family:                    consolas, monaco, "Andale Mono", "Ubuntu Mono", monospace !default;
$papyrus-font-size-root:                 15px !default;
$papyrus-font-size:                      1em !default;
$papyrus-line-height:                    1.7 !default;

// EDITOR SIZING
$papyrus-height:                         auto !default;
$papyrus-width:                          100% !default;

// SELECTION
$papyrus-selection-bg:           255, 255, 255 !default;
$papyrus-selection-alpha:                  0.3 !default;

// CODE REGION
$papyrus-code-padding-y:                 0.3em !default;
$papyrus-code-padding-x:                 0.5em !default;
$papyrus-code-border-radius:             0.5em !default;

$papyrus-code-color:                   #fafafa !default;
$papyrus-code-bg:                      #181b20 !default;
$papyrus-code-inline-bg:               #eeebeb !default;
$papyrus-code-caret-color:             #ffffff !default;

// SCROLLBAR
$papyrus-scrollbar-width:                  2px !default;
$papyrus-scrollbar-track:              #181b20 !default;
$papyrus-scrollbar-bg:                 #384355 !default;
$papyrus-scrollbar-thumb:              #384355 !default;

// INVISIBLE CHARACTERS
$papyrus-invisible-space-color:         #42454D !default;
$papyrus-invisible-tab-color:           #808080 !default;
$papyrus-invisible-lf-color:            #808080 !default;
$papyrus-invisible-cr-color:            #808080 !default;
$papyrus-invisible-crlf-color:          #808080 !default;

// LINE NUMBERS
$papyrus-line-number-width:               3.3em !default;
$papyrus-line-number-color:             #363d49 !default;

// LINE NUMBER FENCE
$papyrus-line-fence-width:               0.01em !default;
$papyrus-line-scroll-left:                    0 !default;
$papyrus-line-fence-color:              #363d49 !default;

// LINE HIGHLIGHT
$papyrus-line-highlight-alpha:             0.05 !default;
$papyrus-line-highlight-bg:        171, 190, 206 !default;
$papyrus-line-highlight-number:         #fafafa !default;


/* -------------------------------------------- */
/* XML LANGUAGE                                 */
/* -------------------------------------------- */

$papyrus-xml-prolog:                   #BECAFF !default;
$papyrus-xml-name:                     #FF93BC !default;
$papyrus-xml-prefix:                   #BECAFF !default;
$papyrus-xml-delimiter:                #BECAFF !default;
$papyrus-xml-tag-name:                 #FF93BC !default;
$papyrus-xml-equals:                   #FF93BC !default;
$papyrus-xml-attr-name:                #91EBC2 !default;
$papyrus-xml-attr-value:               #FFF9A6 !default;
$papyrus-xml-comment:                  #888888 !default;

/* -------------------------------------------- */
/* HTML LANGUAGE                                */
/* -------------------------------------------- */

$papyrus-html-text-content:            #FAFAFA !default;
$papyrus-html-doctype:                 #FAFAFA !default;
$papyrus-html-delimiter:               #BECAFF !default;
$papyrus-html-tag-name:                #FF93BC !default;
$papyrus-html-equals:                  #FF93BC !default;
$papyrus-html-attr-name:               #91EBC2 !default;
$papyrus-html-attr-value:              #FFF9A6 !default;
$papyrus-html-comment:                 #888888 !default;

/* -------------------------------------------- */
/* CSS LANGUAGE                                 */
/* -------------------------------------------- */

$papyrus-css-selector:                #a5d447 !default;
$papyrus-css-tag-selector:            #FF93BC !default;
$papyrus-css-colon:                   #91EBC2 !default;
$papyrus-css-function:                #a5d447 !default;
$papyrus-css-variable:                #b594d9 !default;
$papyrus-css-operator:                #E91E63 !default;
$papyrus-css-punctuation:             #fafafa !default;
$papyrus-css-important:               #E91E63 !default;
$papyrus-css-atrule:                  #E91E63 !default;
$papyrus-css-property:                #81D4FA !default;
$papyrus-css-property-value:          #FFF9A6 !default;
$papyrus-css-number:                  #F48FB1 !default;
$papyrus-css-hexcode:                 #F48FB1 !default;
$papyrus-css-combinator:              #E91E63 !default;
$papyrus-css-unit:                    #E91E63 !default;
$papyrus-css-attr-name:               #91EBC2 !default;
$papyrus-css-attr-value:              #FFF9A6 !default;
$papyrus-css-attr-punctuation:        #FF93BC !default;
$papyrus-css-pseudo-element:          #e18d27 !default;

/* -------------------------------------------- */
/* CSS LANGUAGE                                 */
/* -------------------------------------------- */

$papyrus-scss-selector:               #a5d447 !default;
$papyrus-scss-tag-selector:           #FF93BC !default;
$papyrus-scss-colon:                  #91EBC2 !default;
$papyrus-scss-function:               #a5d447 !default;
$papyrus-scss-variable:               #b594d9 !default;
$papyrus-scss-operator:               #E91E63 !default;
$papyrus-scss-punctuation:            #fafafa !default;
$papyrus-scss-important:              #E91E63 !default;
$papyrus-scss-atrule:                 #E91E63 !default;
$papyrus-scss-property:               #81D4FA !default;
$papyrus-scss-property-value:         #FFF9A6 !default;
$papyrus-scss-number:                 #F48FB1 !default;
$papyrus-scss-hexcode:                #F48FB1 !default;
$papyrus-scss-combinator:             #E91E63 !default;
$papyrus-scss-unit:                   #E91E63 !default;
$papyrus-scss-attr-name:              #91EBC2 !default;
$papyrus-scss-attr-value:             #FFF9A6 !default;
$papyrus-scss-attr-punctuation:       #FF93BC !default;
$papyrus-scss-pseudo-element:         #e18d27 !default;

/* -------------------------------------------- */
/* LIQUID LANGUAGE                              */
/* -------------------------------------------- */

$papyrus-liquid-fallback:             #fafafa !default;
$papyrus-liquid-delimiters:           #fafafa !default;
$papyrus-liquid-tag:                  #E91E63 !default;
$papyrus-liquid-output:               #81d4fa !default;
$papyrus-liquid-boolean:              #ff80f4 !default;
$papyrus-liquid-number:               #935eff !default;
$papyrus-liquid-punctuation:          #E91E63 !default;
$papyrus-liquid-operator:             #E91E63 !default;
$papyrus-liquid-parameter:            #ff953c !default;
$papyrus-liquid-object-name:          #81d4fa !default;
$papyrus-liquid-object-prop:          #fafafa !default;
$papyrus-liquid-filter-name:          #3defb9 !default;
$papyrus-liquid-string:               #FFF9A6 !default;
$papyrus-liquid-comment:              #888888 !default;
$papyrus-liquid-string-delimiters:    #888888 !default;
$papyrus-liquid-string-object-name:   #81d4fa !default;

/* -------------------------------------------- */
/* JSON LANGUAGE                                */
/* -------------------------------------------- */

$papyrus-json-punctuation:            #fafafa !default;
$papyrus-json-property:               #81D4FA !default;
$papyrus-json-string:                 #FFF9A6 !default;
$papyrus-json-boolean:                #FF80F4 !default;
$papyrus-json-number:                 #9753fd !default;
$papyrus-json-operator:               #E91E63 !default;

/* -------------------------------------------- */
/* JAVASCRIPT LANGUAGE                          */
/* -------------------------------------------- */

$papyrus-js-fallback:                 #fafafa !default;
$papyrus-js-keyword:                  #E91E63 !default;
$papyrus-js-function:                 #9EE34F !default;
$papyrus-js-class-name:               #9EE34F !default;
$papyrus-js-function-name:            #81D4FA !default;
$papyrus-js-punctuation:              #FAFAFA !default;
$papyrus-js-special-chars:            #E91E63 !default;
$papyrus-js-parameter:                #FFAB40 !default;
$papyrus-js-variable:                 #81D4FA !default;
$papyrus-js-operator:                 #E91E63 !default;
$papyrus-js-operation:                #E91E63 !default;
$papyrus-js-module:                   #E91E63 !default;
$papyrus-js-semi:                     #fafafa !default;
$papyrus-js-flow:                     #E91E63 !default;
$papyrus-js-number:                   #F48FB1 !default;
$papyrus-js-boolean:                  #F48FB1 !default;
$papyrus-js-string:                   #F5EC70 !default;
$papyrus-js-regex:                    #F5EC70 !default;
$papyrus-js-regex-flags:              #E91E63 !default;
$papyrus-js-literal-property:         #22c0cb !default;
$papyrus-js-comment:                  #888888 !default;


/* -------------------------------------------- */
/* TYPESCRIPT                                   */
/* -------------------------------------------- */

$papyrus-ts-fallback:                #fafafa !default;
$papyrus-ts-types:                   #177fd4 !default;
$papyrus-ts-boolean:                 #ff80f4 !default;
$papyrus-ts-number:                  #935eff !default;
$papyrus-ts-operator:                #E91E63 !default;
$papyrus-ts-parameter:               #ff953c !default;
$papyrus-ts-method:                  #7ef0ff !default;
$papyrus-ts-function-name:           #9EE34F !default;
$papyrus-ts-filter-name:             #3defb9 !default;
$papyrus-ts-string:                  #FFF9A6 !default;
$papyrus-ts-comment:                 #888888 !default;

CSS Variables

:root {
  /* PRE ELEMENT -------------------------------- */

  --papyrus-font-size-root: 15px;
  --papyrus-font-size: 1em;
  --papyrus-font-family: consolas, monaco, andale mono, ubuntu mono, monospace;
  --papyrus-line-height: 1.7;
  --papyrus-height: auto;
  --papyrus-width: 100%;

  /* CODE ELEMENT ------------------------------- */

  --papyrus-code-color: #fafafa;
  --papyrus-code-padding-y: 0.3em;
  --papyrus-code-padding-x: 0.5em;
  --papyrus-code-bg: #181b20;
  --papyrus-code-inline-bg: #eeebeb;
  --papyrus-code-border-radius: 0.5em;
  --papyrus-code-caret-color: #ffffff;

  /* SELECTED TEXT ------------------------------ */

  --papyrus-selection-bg: 255, 255, 255;
  --papyrus-selection-alpha: 0.3;

  /* SCROLLBARS --------------------------------- */

  --papyrus-scrollbar-width: 2px;
  --papyrus-scrollbar-track: #181b20;
  --papyrus-scrollbar-bg: #384355;
  --papyrus-scrollbar-thumb: #384355;

  /* INVISIBLE CHARACTERS ----------------------- */

  --papyrus-invisible-space-color: #42454d;
  --papyrus-invisible-tab-color: #808080;
  --papyrus-invisible-lf-color: #808080;
  --papyrus-invisible-cr-color: #808080;
  --papyrus-invisible-crlf-color: #808080;

  /* LINE NUMBERS ------------------------------- */

  --papyrus-line-number-width: 3.3em;
  --papyrus-line-number-color: #363d49;
  --papyrus-line-fence-color: #363d49;
  --papyrus-line-fence-width: 0.01em;
  --papyrus-line-scroll-left: 0;

  /* LINE HIGHLIGHT ----------------------------- */

  --papyrus-line-highlight-alpha: 0.05;
  --papyrus-line-highlight-bg: 171, 190, 206;
  --papyrus-line-highlight-number: #fafafa;

  /* -------------------------------------------- */
  /* XML                                          */
  /* -------------------------------------------- */

  --papyrus-xml-prolog: #becaff;
  --papyrus-xml-name: #ff93bc;
  --papyrus-xml-prefix: #becaff;
  --papyrus-xml-comment: #888888;
  --papyrus-xml-delimiter: #becaff;
  --papyrus-xml-tag-name: #ff93bc;
  --papyrus-xml-equals: #ff93bc;
  --papyrus-xml-attr-name: #91ebc2;
  --papyrus-xml-attr-value: #fff9a6;

  /* -------------------------------------------- */
  /* HTML                                         */
  /* -------------------------------------------- */

  --papyrus-html-text-content: #fafafa;
  --papyrus-html-doctype: #fafafa;
  --papyrus-html-delimiter: #becaff;
  --papyrus-html-tag-name: #ff93bc;
  --papyrus-html-equals: #ff93bc;
  --papyrus-html-attr-name: #91ebc2;
  --papyrus-html-attr-value: #fff9a6;
  --papyrus-html-comment: #888888;

  /* -------------------------------------------- */
  /* LIQUID                                       */
  /* -------------------------------------------- */

  --papyrus-liquid-fallback: #fafafa;
  --papyrus-liquid-delimiters: #fafafa;
  --papyrus-liquid-tag: #e91e63;
  --papyrus-liquid-output: #81d4fa;
  --papyrus-liquid-boolean: #ff80f4;
  --papyrus-liquid-number: #935eff;
  --papyrus-liquid-punctuation: #e91e63;
  --papyrus-liquid-operator: #e91e63;
  --papyrus-liquid-parameter: #ff953c;
  --papyrus-liquid-object-name: #81d4fa;
  --papyrus-liquid-object-prop: #fafafa;
  --papyrus-liquid-filter-name: #3defb9;
  --papyrus-liquid-string: #fff9a6;
  --papyrus-liquid-comment: #888888;
  --papyrus-liquid-string-delimiters: #888888;
  --papyrus-liquid-string-object-name: #81d4fa;

  /* -------------------------------------------- */
  /* CSS                                          */
  /* -------------------------------------------- */

  --papyrus-css-selector: #a5d447;
  --papyrus-css-tag-selector: #ff93bc;
  --papyrus-css-colon: #91ebc2;
  --papyrus-css-function: #a5d447;
  --papyrus-css-variable: #b594d9;
  --papyrus-css-operator: #e91e63;
  --papyrus-css-punctuation: #fafafa;
  --papyrus-css-important: #e91e63;
  --papyrus-css-atrule: #e91e63;
  --papyrus-css-property: #81d4fa;
  --papyrus-css-property-value: #fff9a6;
  --papyrus-css-number: #f48fb1;
  --papyrus-css-hexcode: #f48fb1;
  --papyrus-css-combinator: #e91e63;
  --papyrus-css-unit: #e91e63;
  --papyrus-css-attr-name: #91ebc2;
  --papyrus-css-attr-value: #fff9a6;
  --papyrus-css-attr-punctuation: #ff93bc;
  --papyrus-css-pseudo-element: #e18d27;

  /* -------------------------------------------- */
  /* JSON                                         */
  /* -------------------------------------------- */

  --papyrus-json-punctuation: #fafafa;
  --papyrus-json-property: #81d4fa;
  --papyrus-json-string: #fff9a6;
  --papyrus-json-boolean: #ff80f4;
  --papyrus-json-number: #9753fd;
  --papyrus-json-operator: #e91e63;

  /* -------------------------------------------- */
  /* JAVASCRIPT                                   */
  /* -------------------------------------------- */

  --papyrus-js-fallback: #fafafa;
  --papyrus-js-keyword: #e91e63;
  --papyrus-js-function: #9ee34f;
  --papyrus-js-class-name: #9ee34f;
  --papyrus-js-function-name: #81d4fa;
  --papyrus-js-punctuation: #fafafa;
  --papyrus-js-special-chars: #e91e63;
  --papyrus-js-parameter: #ffab40;
  --papyrus-js-variable: #81d4fa;
  --papyrus-js-operator: #e91e63;
  --papyrus-js-operation: #e91e63;
  --papyrus-js-module: #e91e63;
  --papyrus-js-semi: #fafafa;
  --papyrus-js-flow: #e91e63;
  --papyrus-js-number: #f48fb1;
  --papyrus-js-boolean: #f48fb1;
  --papyrus-js-string: #f5ec70;
  --papyrus-js-regex: #f5ec70;
  --papyrus-js-regex-flags: #e91e63;
  --papyrus-js-literal-property: #22c0cb;
  --papyrus-js-comment: #888888;

  /* -------------------------------------------- */
  /* TYPESCRIPT                                   */
  /* -------------------------------------------- */

  --papyrus-ts-fallback: #fafafa;
  --papyrus-ts-types: #177fd4;
  --papyrus-ts-boolean: #ff80f4;
  --papyrus-ts-number: #935eff;
  --papyrus-ts-operator: #e91e63;
  --papyrus-ts-parameter: #ff953c;
  --papyrus-ts-method: #7ef0ff;
  --papyrus-ts-function-name: #9ee34f;
  --papyrus-ts-filter-name: #3defb9;
  --papyrus-ts-string: #fff9a6;
  --papyrus-ts-comment: #888888;
}

How it works?

Papyrus works just the same as PrismJS when highlighting code, however code insertion is applied using a morph opposed to hard assignment via innerHTML which helps performance. The Papyrus code editor is made possible by applying textarea enhancements, and unlike other modules that leverage contenteditable annotation (like the wonderful CodeJar), Papyrus leverages the <textarea> element.

The <textarea> exists as a layer over node structures generated with PrismJS and replicates the inner markup of <code> elements to give the impression of a real text editor. The benefits of using <textarea> over contenteditable is a matter of loc~limit and performance. Textarea is a snappy, there is less to augment and it can hold 1000's of lines with no issues.

Special Thanks

Thanks to Swyxio for passing on the NPM package name.