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

cygtut

v1.0.2

Published

JSON-driven tutorial overlay engine for web applications

Readme

CygTut

CygTut is a high-level tutorial overlay engine built on top of cyghtml 0.1.1.

Its purpose is simple:

  • keep tutorial meaning above the low-level renderer
  • place tutorial objects above an existing host page without rewriting that page
  • resolve target-based positions and anchors
  • keep tutorial runtime state, commands, and triggers
  • compile the result into a final cyghtml document
  • let cyghtml render the final overlay DOM

CygTut is not a low-level HTML/CSS renderer. It does not want to replace cyghtml. It exists to sit above cyghtml and own tutorial behavior.

Philosophy

CygTut follows these design rules.

  1. It is a tutorial engine first.
  2. It should reuse as much of cyghtml as possible.
  3. The host page should stay untouched.
  4. Tutorial UI should be rendered as overlay DOM above the host page.
  5. The most important positioning model is target-based placement.
  6. The final rendered result should be absolute overlay HTML/CSS.
  7. Grouping should compile to real parent DOM nodes.
  8. Final animation should be expressed as CSS.
  9. CygTut should own state, commands, triggers, presets, anchors, and compilation.
  10. cyghtml should only receive final render data.

In short:

  • cygtut decides tutorial meaning and placement
  • cygtut compiles the final overlay document
  • cyghtml renders it

Relationship To cyghtml

The intended architecture is:

cygtut authoring script
-> cygtut runtime state
-> cygtut compile step
-> final cyghtml document
-> cyghtml renders overlay DOM

That means:

  • cygtut owns tutorial meaning
  • cygtut owns runtime variables and progression
  • cygtut resolves targets and anchors
  • cygtut computes final overlay geometry
  • cygtut outputs a final cyghtml document
  • cyghtml mounts the overlay root and renders the document

Core Runtime Model

The most important concept in CygTut is the overlay root.

The host page already exists. CygTut does not rewrite it. Instead, it creates overlay objects above that page.

Those overlay objects are placed like this:

  1. select a target element on the host page
  2. read the target's current rectangle
  3. choose a target anchor and self anchor
  4. compute the final absolute overlay coordinates
  5. compile the result to cyghtml
  6. let cyghtml render the overlay DOM

So the core model is:

existing page
-> target selected
-> target geometry read
-> tutorial object geometry computed
-> final overlay HTML/CSS generated
-> cyghtml renders it

This keeps the host page safe while still allowing tutorial UI to feel attached to real page elements.

Responsibility Split

CygTut owns

  • tutorial runtime state
  • tutorial variables
  • commands
  • triggers
  • target selection
  • target anchor and self anchor resolution
  • object presets and tutorial object meaning
  • grouping rules
  • final position and size calculation
  • mobile/window branching logic
  • conversion into a final cyghtml document

cyghtml owns

  • overlay root mounting
  • final top-level vars, css, and html rendering
  • DOM creation from tag or markup
  • attrs, style, text, and items
  • parent-child composition
  • delayed parent resolution
  • runtime event hookup
  • final DOM commit

The boundary is simple:

  • cygtut decides what should exist and where
  • cyghtml renders that already-decided result

How CygTut Uses cyghtml

The most important implementation rule is: CygTut should prefer reusing cyghtml features instead of recreating them.

Feature mapping:

  • overlay root mounting -> CygHtmlEngine.mount(), mountById(), mountByClass(), mountByTag()
  • final overlay DOM output -> CygHtmlDocument.html
  • final runtime variables -> CygHtmlDocument.vars
  • final CSS rules -> CygHtmlDocument.css
  • real parent-child grouping -> items and appendTo
  • host-page mounting features remain available in cyghtml, but are not yet exposed as first-class CygTut authoring fields
  • DOM event binding -> cyghtml node events plus CygHtmlEngine.handlers
  • final CSS animation rendering -> style.animation, style.transition, style.transform, style.opacity
  • interactive overlay behavior -> CygHtmlEngine({ interactive })

That means CygTut should not reimplement:

  • low-level DOM creation
  • low-level stylesheet generation
  • low-level event listener wiring
  • overlay root mounting logic
  • parent resolution for final HTML nodes

Instead, CygTut should focus on:

  • tutorial meaning
  • target lookup
  • anchor resolution
  • command and trigger flow
  • compilation into final cyghtml data

Planned And Incomplete Areas

The current architecture is usable, but several important areas still need more detailed design or stronger implementation.

This section turns those areas into concrete planning notes so later code changes have a stable target.

1. Target lifecycle policy

This is one of the most important unfinished areas because target-based placement is the center of the whole runtime.

Goal

CygTut should treat host-page targets as unstable, real-world DOM dependencies. A target may already exist, appear later, disappear, move, resize, or be temporarily hidden. The runtime should define exactly what happens in each case.

Required behavior

  1. When a target exists:
  • read its current getBoundingClientRect()
  • resolve targetAnchor
  • resolve selfAnchor
  • compute final absolute overlay geometry
  1. When a target does not exist yet:
  • do not crash
  • do not create a wrong fallback geometry
  • mark the object as unresolved for the current render pass
  • skip output for that object in the compiled cyghtml document
  1. When a target appears later:
  • allow the next normal runtime render pass to resolve it
  • the object should become visible without needing to be recreated by the user
  1. When a target disappears after previously existing:
  • treat the object as unresolved again
  • hide it by omitting it from the compiled output
  • preserve its runtime object state unless the script explicitly removes it
  1. When a target moves or resizes:
  • recompute geometry
  • recompile
  • rerender through cyghtml

Runtime states

A target-bound object should conceptually have these states:

  • resolved
  • pending-target
  • hidden-by-state
  • removed

Even if these are not formal enum values yet, the runtime behavior should follow that model.

Observation policy

Current minimum policy:

  • rerender on resize
  • rerender on scroll

Planned stronger policy:

  • optional host MutationObserver
  • optional ResizeObserver for known targets
  • optional throttling to avoid excessive recompilation

Boundary with cyghtml

CygTut owns:

  • selector lookup
  • target existence checks
  • geometry resolution
  • unresolved-target policy

cyghtml owns:

  • rendering whatever final nodes CygTut gives it

That means unresolved targets should be handled before compilation finishes. cyghtml should never need to understand tutorial target semantics.

2. Animation system detail

The animation model now has the correct high-level shape:

  • enter
  • loop
  • preset
  • custom

This section defines how that model should work more precisely.

Goal

CygTut should expose tutorial-friendly animation authoring while still compiling everything into ordinary CSS that cyghtml can render without special logic.

Phase model

Supported phases:

  • enter: one-time appearance animation
  • loop: persistent or repeating animation while the object remains active

Intentional omission for now:

  • no first-class exit phase

Reason:

  • object disappearance can be expressed by state changes, object replacement, or future authoring sugar
  • keeping only enter and loop keeps the base model simpler

Authoring shape

Recommended structure:

{
  "animation": {
    "enter": {
      "preset": "fade-in"
    },
    "loop": {
      "custom": {
        "animation": "pulse 1.2s ease-in-out infinite"
      }
    }
  }
}

Backward-compatibility is currently allowed with raw CSS-like animation objects, but the long-term preferred model is the two-phase structure above.

Preset policy

Current preset direction:

  • fade-in
  • slide-up
  • slide-down
  • slide-left
  • slide-right
  • scale-in
  • pulse
  • rotate

Planned rules:

  • preset names are semantic authoring shortcuts
  • preset output is compiler-owned CSS
  • presets may inject support styles such as keyframes into the final compiled document
  • timing defaults should be stable but may later become overridable per object

Custom policy

custom is the escape hatch. It should accept CSS-facing values such as:

  • animation
  • transition
  • transform
  • opacity

CygTut should not try to outgrow CSS here. If authors need richer animation behavior, the right answer is usually to emit better final CSS, not to build a new animation engine.

Merge rules

When both phases exist:

  • enter contributes its one-time appearance styling
  • loop contributes its persistent styling
  • if both define the same low-level field, loop wins for the steady-state compiled style
  • if both define animation, the compiler may combine them as comma-separated CSS animation values when appropriate

Group behavior

If animation is applied to a group object:

  • the group parent receives the final CSS animation styles
  • child elements should follow naturally through normal DOM inheritance/transform behavior

This is one of the main reasons grouping should compile to real parent DOM nodes.

Boundary with cyghtml

CygTut owns:

  • animation authoring shape
  • preset resolution
  • generated support styles
  • final low-level animation CSS values

cyghtml owns:

  • applying the final CSS to DOM nodes

3. Responsive and device branching

CygTut already treats responsive behavior as tutorial runtime state rather than renderer logic. That direction is good, but the detailed model still needs to be made explicit.

Goal

Responsive tutorial behavior should come from tutorial state and compilation rules, not from hidden renderer magic.

Core rule

Responsive decisions belong in CygTut. cyghtml should stay unaware of concepts like mobile tutorial mode, window tutorial mode, or tutorial-specific breakpoint logic.

Recommended variable model

Use normal variables such as:

  • global.device = "window"
  • global.device = "mobile"
  • global.device = "tablet"

The host app may set these directly. CygTut reacts through normal triggers, commands, and compilation.

Planned branching styles

  1. Trigger-driven branching
  • simplest and most explicit
  • object creation and updates depend on global.device
  1. Compile-time object branching
  • later optional convenience layer
  • for example a single authoring object may contain desktop and mobile variants that compile differently

Viewport recomputation

Current minimum:

  • rerender on resize
  • recompute all target-based geometry

Still to design more deeply:

  • whether viewport-based clamping should be global or per object
  • whether object-specific responsive overrides should live in the base object schema
  • whether a standard breakpoint helper belongs in authoring sugar

Boundary with cyghtml

CygTut owns:

  • responsive tutorial semantics
  • responsive state variables
  • responsive geometry and style decisions

cyghtml owns:

  • rendering the final already-chosen document

4. Command coverage and flow semantics

The current command set is enough for the basic runtime, but some command behavior still needs clearer semantics.

Goal

Commands should stay small, explicit, and easy to compile into runtime state changes. Authoring convenience should be layered above them rather than making the core command system too large.

Current core command families

Structure:

  • CREATE_GROUP
  • ADD_OBJECT
  • UPDATE_OBJECT
  • REMOVE_OBJECT
  • ADD_TO_GROUP
  • REMOVE_FROM_GROUP
  • UNGROUP

Variables:

  • SET_VAR
  • INC_VAR
  • TOGGLE_VAR

Flow:

  • RUN
  • IF
  • WAIT_UNTIL
  • EMIT

Areas still needing stronger design

WAIT_UNTIL

Right now it is a placeholder. It needs a real policy.

Planned direction:

  • condition-based waiting only
  • no hidden time engine in the first pass
  • WAIT_UNTIL should suspend the current command sequence until a condition becomes true
  • resumption should occur through the same runtime state-change pipeline used by triggers
RUN

Needs clearer naming conventions.

Planned direction:

  • allow step:<name>
  • allow scene:<name>
  • allow explicit named command groups later if needed
EMIT

Needs a sharper host integration contract.

Planned direction:

  • stay a small bridge from tutorial runtime to host application callbacks
  • do not let it become a hidden second state system
Visibility control

Still undecided whether explicit commands like these are needed:

  • SHOW_OBJECT
  • HIDE_OBJECT

Current direction:

  • prefer ADD_OBJECT, UPDATE_OBJECT, and REMOVE_OBJECT
  • only add dedicated visibility commands if authoring becomes unnecessarily noisy without them

Boundary with cyghtml

Commands should mutate tutorial state. They should not directly manipulate low-level DOM. The only DOM output should be the compiled result passed to cyghtml.

5. Tutorial object schema depth

The common object model is defined, but some object families still need deeper planning.

Goal

Keep the object model opinionated enough to feel tutorial-native, while keeping the final compiled output simple enough for cyghtml.

Current first-party object set

  • tooltip
  • highlight
  • text
  • button
  • mask
  • group
  • image
  • progress

Schema questions still to settle

  1. Which fields are truly universal?
  • target
  • targetAnchor
  • selfAnchor
  • offset
  • autoPlacement
  • x
  • y
  • width
  • height
  • style
  • attrs
  • vars
  • events
  • triggers
  • animation
  1. Which fields should remain object-specific?
  • title and text for tooltips
  • padding for highlights
  • src and alt for images
  • value and max for progress
  1. Should richer content exist? Possible future directions:
  • richer tooltip body content
  • icon support
  • structured content blocks
  • inline action rows
  1. Should groups gain more layout helpers? Possible future directions:
  • helper stacking rules
  • built-in gap helpers
  • local alignment helpers

Current direction:

  • default group children remain absolute
  • richer layout helpers, if added, should still compile into plain HTML/CSS and not replace DOM semantics

6. External host integration

CygTut already supports direct variable control and uses cyghtml event handlers underneath, but host integration still needs a sharper documented contract.

Goal

Host apps should be able to control tutorial progression without needing to understand the low-level overlay renderer.

Current public direction

Public control stays state-oriented:

  • getVar
  • setVar
  • updateVars
  • subscribe
  • getSnapshot

Mount control may also be exposed by delegating to cyghtml:

  • mount
  • mountById
  • mountByClass
  • mountByTag
  • unmount

Areas still needing design

  1. EMIT callback registry
  • where host callbacks are registered
  • what payload shape they receive
  • whether they can return values or only perform side effects
  1. Debugging and introspection
  • getRendererDocument() is already exposed as a debug-oriented public API
  • the remaining design question is how stable its compiled payload contract should be across future versions
  • whether more renderer introspection should stay limited to debug workflows
  1. Boundary discipline
  • which cyghtml methods should stay hidden
  • which cyghtml capabilities are safe to expose through cygtut

Current direction:

  • expose only what strengthens tutorial control
  • keep raw low-level DOM rendering concerns inside cyghtml

7. Testing and example coverage

This is more of a delivery gap than a design gap, but it matters a lot.

Goal

Examples and tests should prove the contract between tutorial authoring, runtime state, compilation, and final cyghtml rendering.

Still needed

  1. A minimal real tutorial example
  • target-based highlight
  • target-based tooltip
  • button-triggered step progression
  1. A group example
  • group parent positioned from a target
  • children authored with local coordinates
  • parent CSS animation affecting all children
  1. An animation example
  • enter preset
  • loop preset
  • custom animation object
  1. Runtime resilience tests
  • missing target
  • target appearing later
  • resize reflow
  • scroll reflow
  • grouped child local coordinates
  1. Compilation inspection example
  • show exactly how a CygTut object set becomes a final cyghtml document

Current direction

Examples should not be treated as decoration. They should act as executable architecture checks.

Important Features

1. Overlay-first model

CygTut is overlay-first. The final UI always lives above the host page.

There is no separate tutorial layer system in the new direction. The overlay root from cyghtml is enough. If CygTut needs internal containers, it can compile them as normal HTML group nodes.

2. Target-based placement

Tutorial objects may point to host page targets. Examples:

  • .login-button
  • #search
  • [data-product-card='1']

CygTut reads the target rectangle and places overlay objects accordingly. The original host element is not modified.

3. Anchors

When a target exists, CygTut can use:

  • targetAnchor
  • selfAnchor
  • offset
  • autoPlacement

This allows:

  • exact highlight overlays
  • tooltips below, above, left, or right of targets
  • callouts aligned to precise target points
  • optional auto-flip behavior when a target-based overlay would leave the viewport

4. Fixed tutorial object types

CygTut still benefits from high-level tutorial object meaning. Suggested object types:

  • highlight
  • tooltip
  • text
  • button
  • mask
  • group
  • optional later: image, progress

These are tutorial concepts. They do not need to exist as low-level cyghtml concepts. CygTut compiles them into plain HTML/CSS.

5. Grouping

Grouping should become real DOM grouping. A group compiles to a real parent div.

Rules:

  • the group itself is an overlay object
  • its children render inside it
  • child positioning is absolute by default
  • child coordinates are resolved relative to the group's final position
  • parent style and parent animation naturally affect children

This is much more stable than trying to animate separate siblings independently.

6. CSS-based animation

Animation should still be expressed through final CSS, but the tutorial model is now simpler.

CygTut supports two animation phases:

  • enter
  • loop

Each phase may be defined in one of two ways:

  • preset
  • custom

Examples:

  • enter.preset = "fade-in"
  • loop.preset = "pulse"
  • enter.custom = { "animation": "fadeIn 220ms ease-out forwards" }
  • loop.custom = { "animation": "pulse 1.2s ease-in-out infinite" }

The compiler turns these definitions into plain CSS values such as:

  • animation
  • transition
  • transform
  • opacity

and passes the final result down to cyghtml.

7. Runtime variables

CygTut still needs runtime variables. Common examples:

  • global.step
  • global.scene
  • global.device
  • global.completed
  • object.clicks

These variables drive tutorial behavior.

8. Triggers

CygTut remains trigger-driven.

Normal trigger model:

  1. a variable changes
  2. matching triggers are re-evaluated
  3. commands run
  4. tutorial object state changes
  5. a new cyghtml document is compiled
  6. the overlay is re-rendered

9. External control

CygTut should allow external reads and writes. Examples:

  • getVar("global.step")
  • setVar("global.step", 2)
  • updateVars({ "global.device": "mobile" })

That lets host applications control tutorial state directly.

Mount note:

  • call mount() or mountBy*() before start() if you want the first render to use a custom mount host
  • if you never call them, cyghtml falls back to its default root mounting behavior

Top-Level Authoring Keys

A CygTut script is tutorial-oriented, not render-oriented.

The top-level authoring keys are:

  • meta
  • vars
  • boot
  • steps
  • scenes
  • triggers

Example:

{
  "meta": {
    "bindings": {
      "steps": { "path": "global.step", "initial": 1 },
      "device": { "path": "global.device", "initial": "window" }
    }
  },
  "vars": {
    "global.completed": false
  },
  "boot": [
    {
      "type": "ADD_OBJECT",
      "object": {
        "id": "login-highlight",
        "type": "highlight",
        "target": ".login-button"
      }
    }
  ],
  "steps": {
    "1": [
      {
        "type": "ADD_OBJECT",
        "object": {
          "id": "login-tooltip",
          "type": "tooltip",
          "target": ".login-button",
          "targetAnchor": "bottom",
          "selfAnchor": "top",
          "offset": { "x": 0, "y": 12 },
          "title": "Login Guide",
          "text": "Click the login button to continue."
        }
      }
    ]
  }
}

This is still CygTut authoring JSON. It is not the final cyghtml document.

Tutorial Object Model

Every tutorial object starts from a common shape.

Common keys:

  • id
  • type
  • target
  • targetAnchor
  • selfAnchor
  • offset
  • x
  • y
  • width
  • height
  • className
  • attrs
  • style
  • vars
  • events
  • triggers
  • animation
  • groupId

Common meaning

  • id: unique tutorial object id
  • type: tutorial object kind such as tooltip, highlight, button, group
  • target: host selector used for geometry lookup
  • targetAnchor: anchor point on the host target
  • selfAnchor: anchor point on the overlay object itself
  • offset: extra x/y shift after anchor placement
  • autoPlacement: optional collision-aware fallback placement for target-based objects
  • x, y, width, height: direct geometry values when not target-driven
  • className, attrs, style: extra compiled HTML/CSS surface
  • vars: object-local runtime values
  • events: object-level event commands
  • triggers: object-level trigger rules
  • animation: animation definition using enter / loop, each with preset or custom
  • groupId: parent group reference in runtime state

Object-specific keys

Examples:

  • tooltip: title, text
  • highlight: padding
  • button: text
  • text: text
  • image: src, alt
  • progress: value, max

Top-Level meta

meta defines authoring-time or runtime wiring information.

The most useful early use is bindings.

Example:

{
  "meta": {
    "bindings": {
      "steps": { "path": "global.step", "initial": 1 },
      "scenes": { "path": "global.scene", "initial": "intro" },
      "device": { "path": "global.device", "initial": "window" }
    }
  }
}

Recommended uses:

  • initial runtime bindings
  • naming common progression variables
  • keeping authoring convenient without hard-coding engine internals

Top-Level vars

vars defines runtime tutorial state.

Example:

{
  "vars": {
    "global.step": 1,
    "global.scene": "intro",
    "global.device": "window",
    "global.completed": false
  }
}

Recommended scopes:

  • global.*
  • object.*
  • later if needed: group.*

Important rule:

  • step and scene are not special engine systems
  • they are just common variable names

Top-Level boot

boot contains commands that should run when the tutorial starts.

Example:

{
  "boot": [
    {
      "type": "ADD_OBJECT",
      "object": {
        "id": "intro-tooltip",
        "type": "tooltip",
        "target": ".hero-button"
      }
    }
  ]
}

Typical uses:

  • create initial tutorial objects
  • initialize stateful UI pieces
  • prepare default tutorial context

Top-Level steps

steps is a convenience authoring section for ordered progression.

Example:

{
  "steps": {
    "1": [
      {
        "type": "ADD_OBJECT",
        "object": {
          "id": "tip-1",
          "type": "tooltip",
          "target": ".login-button"
        }
      }
    ],
    "2": [
      {
        "type": "UPDATE_OBJECT",
        "id": "tip-1",
        "patch": {
          "text": "Now click the password field."
        }
      }
    ]
  }
}

This remains authoring sugar over normal variable-driven runtime behavior. Internally, step is still just state.

Top-Level scenes

scenes is a convenience authoring section for named tutorial states.

Example:

{
  "scenes": {
    "intro": [
      {
        "type": "ADD_OBJECT",
        "object": {
          "id": "intro-copy",
          "type": "text",
          "text": "Welcome."
        }
      }
    ],
    "done": [
      {
        "type": "SET_VAR",
        "key": "global.completed",
        "value": true
      }
    ]
  }
}

Like step, scene is still just variable-driven state. The section exists for authoring comfort.

Top-Level triggers

triggers define runtime reactions to variable changes.

Example:

{
  "triggers": [
    {
      "watch": ["global.step", "global.scene"],
      "when": "global.step === 2 && global.scene === 'intro'",
      "run": [
        { "type": "SET_VAR", "key": "global.scene", "value": "done" }
      ]
    }
  ]
}

Conditions should support:

  • &&
  • ||
  • !
  • ===
  • !==
  • >
  • >=
  • <
  • <=
  • parentheses ()

Commands

CygTut still owns tutorial commands.

Structure commands

  • CREATE_GROUP
  • ADD_OBJECT
  • UPDATE_OBJECT
  • REMOVE_OBJECT
  • ADD_TO_GROUP
  • REMOVE_FROM_GROUP
  • UNGROUP

Variable commands

  • SET_VAR
  • INC_VAR
  • TOGGLE_VAR

Flow commands

  • RUN
  • IF
  • WAIT_UNTIL
  • EMIT

Animation model

Animation is an object-level definition, not a separate low-level renderer command system.

Recommended shape:

  • animation.enter
  • animation.loop

Each one may contain:

  • preset
  • custom

Example:

{
  "animation": {
    "enter": { "preset": "fade-in" },
    "loop": { "preset": "pulse" }
  }
}

The compiler resolves preset names into final CSS animation values and injects the required support styles into the compiled cyghtml document.

Target Lifecycle

Target handling is central to CygTut, so the runtime needs clear rules.

Current direction:

  • if a target exists, compute geometry from its current getBoundingClientRect()
  • target-following is always on for target-based objects
  • if autoPlacement is enabled, try fallback anchor combinations when the first placement would leave the viewport
  • if a target does not exist yet, skip rendering that object for the current pass
  • if the host layout changes, recompute geometry and render again

The current runtime already re-renders on:

  • window resize
  • window scroll

That keeps target-bound objects attached to moving page geometry during the normal tutorial lifecycle.

Positioning Model

Positioning remains a CygTut concern. cyghtml should receive already-computed values.

CygTut should handle:

  • host target lookup
  • reading getBoundingClientRect()
  • target anchor resolution
  • self anchor resolution
  • offset application
  • target-following as a default behavior for all target-based objects
  • optional autoPlacement fallback when a placement would leave the viewport
  • final absolute left/top/width/height
  • optional viewport clamping
  • device-based layout branching

So the boundary stays simple:

  • CygTut computes geometry
  • cyghtml renders geometry

Events And Object-Local State

CygTut may still expose object-level events and object-level variables.

Example:

{
  "id": "next-btn",
  "type": "button",
  "vars": {
    "clicks": 0
  },
  "events": {
    "onClick": [
      { "type": "INC_VAR", "key": "object.clicks", "value": 1 }
    ]
  },
  "triggers": [
    {
      "watch": ["object.clicks"],
      "when": "object.clicks >= 1",
      "run": [
        { "type": "SET_VAR", "key": "global.step", "value": 2 }
      ]
    }
  ]
}

That keeps local state local while still allowing it to move the global tutorial flow forward.

Final Render Shape

The compiled result should be a plain cyghtml document.

Example:

{
  "vars": {
    "--tip-left": "120px",
    "--tip-top": "240px"
  },
  "css": {
    ".cygtut-tooltip": {
      "position": "absolute",
      "left": "var(--tip-left)",
      "top": "var(--tip-top)",
      "width": "280px",
      "padding": "16px",
      "borderRadius": "14px",
      "background": "#111827",
      "color": "#ffffff"
    }
  },
  "html": [
    {
      "tag": "div",
      "attrs": {
        "id": "login-tooltip",
        "class": "cygtut-tooltip"
      },
      "appendTo": "root",
      "items": [
        {
          "tag": "h3",
          "text": "Login Guide"
        },
        {
          "tag": "p",
          "text": "Click the login button to continue."
        }
      ]
    }
  ]
}

That final document is the only thing cyghtml needs to know.

Processing Pipeline

This section describes how a CygTut script should move through the runtime, which internal areas are responsible for each stage, and in what order the work happens.

End-To-End Flow

At a high level, the runtime flow is:

authoring script
-> normalizeScript
-> StateStore
-> CommandRunner / TriggerRuntime
-> target + anchor resolution
-> compileToCygHtml
-> CygHtmlEngine.render()

Step 1: Script Enters The Runtime

Files:

  • src/spec/Types.ts
  • src/spec/normalizeScript.ts

The script begins as tutorial authoring data.

Responsibilities here:

  • validate top-level keys
  • normalize optional authoring sugar
  • prepare a runtime-friendly representation

At this stage, nothing has been rendered yet.

Step 2: Initialize State

Files:

  • src/runtime/StateStore.ts
  • src/runtime/CygTutEngine.ts

Responsibilities here:

  • build the initial variable state
  • apply meta.bindings
  • apply top-level vars
  • prepare subscriptions and runtime snapshots

At this point, CygTut has runtime state but still no overlay DOM.

Step 3: Run boot

Files:

  • src/runtime/CommandRunner.ts
  • src/runtime/CygTutEngine.ts

Responsibilities here:

  • run initial startup commands
  • create initial tutorial object state
  • prepare the initial tutorial context

Step 4: Evaluate Triggers

Files:

  • src/runtime/TriggerRuntime.ts
  • src/runtime/CommandRunner.ts

Responsibilities here:

  • watch variable changes
  • evaluate trigger conditions
  • run matching command lists

This happens both at startup and after later state changes.

Step 5: Produce The Current Tutorial Object Tree

Files:

  • src/runtime/CygTutEngine.ts
  • src/presets/*

Responsibilities here:

  • keep the current tutorial object state
  • maintain groups and object relationships
  • resolve which tutorial objects currently exist

This is still tutorial-level state, not final HTML.

Step 6: Resolve Targets And Anchors

Files:

  • src/compiler/positionResolver.ts
  • src/compiler/anchorResolver.ts

Responsibilities here:

  • find current host targets
  • read target rectangles
  • resolve target anchor points
  • resolve self anchor points
  • compute final absolute overlay coordinates
  • optionally clamp or branch by device

At the end of this step, geometry is ready.

Step 7: Compile To cyghtml

Files:

  • src/compiler/compileToCygHtml.ts
  • src/compiler/objectCompilers.ts

Responsibilities here:

  • convert tutorial objects into final vars, css, and html
  • compile groups into real parent nodes
  • compile animation meaning into CSS
  • emit a complete cyghtml document

After this step, CygTut has stopped being a tutorial-only model and produced a renderable overlay document.

Step 8: Render Through cyghtml

Files:

  • src/runtime/CygTutEngine.ts
  • internal CygHtmlEngine instance from cyghtml

Responsibilities here:

  • pass the compiled document to cyghtml
  • let cyghtml mount the overlay root
  • let cyghtml render the final DOM

This is the point where the tutorial becomes visible on screen.

Step 9: React To Changes

Files:

  • src/runtime/StateStore.ts
  • src/runtime/TriggerRuntime.ts
  • src/compiler/compileToCygHtml.ts
  • cyghtml

When an event, command, or external setVar changes runtime state:

  1. vars change
  2. triggers are re-evaluated
  3. tutorial object state updates
  4. geometry is recomputed if needed
  5. a new cyghtml document is compiled
  6. cyghtml.render() runs again

This is the normal rerender loop.

Why This Pipeline Matters

This processing order makes the architecture clear.

It allows:

  • tutorial-friendly authoring
  • target-based geometry
  • real grouping
  • CSS-based animation
  • clean separation from the low-level renderer

The design boundary stays simple:

  • cygtut turns tutorial meaning into final overlay data
  • cyghtml turns that final overlay data into DOM

Public Runtime API

The public runtime shape is:

const tut = new CygTut({ script })

The runtime currently exposes these methods:

  • start()
  • destroy()
  • mount(target?)
  • mountById(id)
  • mountByClass(className)
  • mountByTag(tagName)
  • unmount()
  • run(name)
  • getVar(path)
  • setVar(path, value)
  • updateVars(record)
  • getSnapshot()
  • getRendererDocument()
  • subscribe(listener)

The engine also has an internal compile step and an internal CygHtmlEngine. getRendererDocument() is exposed as a debug-oriented API for inspecting the compiled cyghtml document, while low-level renderer control should still remain behind the CygTut boundary for normal users.

Recommended Public Usage

Vanilla usage

import { CygTut } from "cygtut";

const tut = new CygTut({ script });

tut.mountById("app");
await tut.start();
tut.setVar("global.step", 2);
tut.setVar("global.device", "mobile");
console.log(tut.getVar("global.scene"));
tut.destroy();

React usage

import { useRef } from "react";
import { CygTut, type CygTutHandle } from "cygtut/react";

export default function App() {
  const tutRef = useRef<CygTutHandle>(null);

  return (
    <>
      <CygTut ref={tutRef} script={script} />
      <button onClick={() => tutRef.current?.setVar("global.step", 2)}>
        next
      </button>
    </>
  );
}

Source Structure

The file structure reflects the new architecture.

src/
  compiler/
    compileToCygHtml.ts
    objectCompilers.ts
    positionResolver.ts
    anchorResolver.ts

  runtime/
    CygTutEngine.ts
    CommandRunner.ts
    TriggerRuntime.ts
    StateStore.ts

  spec/
    Types.ts
    normalizeScript.ts

  presets/
    tooltip.ts
    highlight.ts
    button.ts
    mask.ts
    group.ts

  wrappers/
    vanilla.ts
    react.tsx

  index.ts

This is the intended direction for the new version of CygTut.

File Roles

src/compiler/compileToCygHtml.ts

Main compiler entry.

Responsibilities:

  • convert current tutorial state into a final cyghtml document

src/compiler/objectCompilers.ts

Responsibilities:

  • turn tutorial object kinds into HTML/CSS structures
  • for example: tooltip -> compiled HTML container + CSS vars + classes

src/compiler/positionResolver.ts

Responsibilities:

  • compute final overlay coordinates
  • convert target-relative rules into final absolute values

src/compiler/anchorResolver.ts

Responsibilities:

  • resolve targetAnchor and selfAnchor
  • compute anchor points from target and object rectangles

src/runtime/CygTutEngine.ts

Responsibilities:

  • serve as the public runtime entry
  • own tutorial state and lifecycle
  • own the internal CygHtmlEngine instance

src/runtime/CommandRunner.ts

Responsibilities:

  • execute tutorial commands

src/runtime/TriggerRuntime.ts

Responsibilities:

  • evaluate watched variables
  • run matching command lists

src/runtime/StateStore.ts

Responsibilities:

  • store tutorial vars and snapshots
  • expose get/set/update/subscribe-style behavior

src/spec/Types.ts

Responsibilities:

  • define authoring types
  • define tutorial object definitions
  • define command and trigger types

src/spec/normalizeScript.ts

Responsibilities:

  • normalize authoring input into internal runtime-ready data

src/presets/*

Responsibilities:

  • define built-in tutorial object presets
  • define shared default HTML/CSS generation behavior

src/wrappers/*

Responsibilities:

  • provide Vanilla and React integration

src/index.ts

Responsibilities:

  • expose the package public API

Why This Structure Fits The New Direction

This structure makes the split clear.

  • spec defines the tutorial language
  • runtime owns state and flow
  • compiler turns that state into cyghtml
  • presets define tutorial object meaning
  • wrappers expose the runtime to applications

That is much closer to the actual architecture we want than the older engine/renderer split.

Installation

npm install cygtut

CygTut uses cyghtml internally. If you want both directly:

npm install cygtut cyghtml

Summary

CygTut is a tutorial engine above cyghtml.

It should:

  • keep tutorial meaning and runtime state
  • resolve target-based placement
  • compile groups into real parent nodes
  • express final animation as CSS
  • compile the final result into a cyghtml document
  • let cyghtml render that document

And it should deliberately stop there.

Low-level overlay rendering belongs in cyghtml. High-level tutorial meaning belongs in cygtut.