ScriptLMS - Drop-In Components
==============================

Each folder in here is one extra Interactive Page template. ScriptLMS
finds them automatically. The easy way to add or update one is
📦 Install Component, at the bottom of the Interactive Page Builder's
template list - choose the component's .zip and it's put in place for
you. Unzipping a component's folder here by hand works too. Either way
it shows up under "Added Components". Delete the folder and it's gone. Nothing else needs editing, and ScriptLMS updates
never touch this folder.

Pages already built with a component keep working for staff and in
Preview even if that component is later removed. They just can't be
opened for editing again until it's reinstalled.


Folder layout
-------------

components/
  my-component/
    component.json   required - see below
    builder.js       required - the builder
    panel.html       optional - its editor panel and ? help (plain HTML)
    images/          optional - any images the component itself uses

The folder name is the component's id: lowercase letters, numbers,
dashes and underscores only. It can't reuse a built-in template's id
(image_choice, click_order, multiple_choice, truefalse,
sort_categories, match_pairs, scenario, hotspot, story, rank_priority).


component.json
--------------

{
  "id": "my-component",           must match the folder name
  "name": "My Component",         shown on the picker button
  "icon": "🧩",                   shown before the name
  "version": "1.0",
  "min_scriptlms": "",            optional - e.g. "2.0"; older installs
                                  show the button greyed out
  "description": "..."            optional - tooltip on the button
}


builder.js - the contract
-------------------------

builder.js calls registerDropInComponent() once, as it loads:

registerDropInComponent({
    id: 'my-component',   // same as the folder name
    wide: true,           // optional - false keeps the builder at its
                          //   normal width (default is the wide 90vw
                          //   workspace the canvas templates use)

    start: function () { ... },
        // Set up a brand-new, blank activity.

    open: function (data) { ... },
        // Reopen a saved one. data is exactly what save() returned as
        // "data", plus ScriptLMS's own fields (type, dropIn, dropInName,
        // dropInVersion) - ignore those.

    hasContent: function () { return true/false; },
        // Optional. Is there anything Cancel would throw away? If left
        // out, Cancel always asks before closing.

    save: function () {
        // Check required fields first. If something's missing, tell the
        // admin with showToast(...) (and jump to it), then return null.
        // Otherwise return:
        return {
            html: '...',   // the finished staff page
            data: { ... }, // the "recipe" open() will get back later
            title: '...'   // the name shown in the module's step list
        };
    }
});

Rules for the staff page (html):
  - It's inserted with innerHTML, so <script> tags never run. Use inline
    onclick="" attributes only, the same as the built-in templates.
  - Call markPageComplete() from the final Continue button. That's what
    marks the step finished and unlocks the next one.
  - Give every id a random prefix (e.g. 'r' + Math.random()...) so two
    copies on one module never clash.
  - Start every panel.html id with a short prefix of your own (e.g.
    "tc-") so it never clashes with ScriptLMS or another component.
  - Spacing at the top: the staff portal sets the space above every
    page (currently 50px from the top of the middle panel), so every
    template - built-in or added - starts at the same height. The staff
    markup must start right at the top: no top padding or margin on its
    outer wrapper, and nothing placed lower just to leave a gap. Any
    space wanted above the first item belongs inside the page itself
    (for example, the title's own top position on a canvas page).
  - Line breaks between tags in the html are ignored for staff (the
    portal shows Interactive Pages with white-space: normal), so it can
    be written across several lines. Text that needs its typed line
    breaks kept must set white-space: pre-wrap on its own element.

Available to builder.js: everything the built-in builders use, including
showToast() and showConfirm() for messages, and builder-common.js's
shared tools.

If a component throws an error, ScriptLMS catches it and shows the admin
a plain message instead of freezing the builder.


audio-story
-----------

The Audio Story: a narrated story with pictures that stops for questions,
plays a feedback clip for each answer, then continues. Its audio clips
are uploaded to the audio/ folder, which backups and library exports
include.


test-component
--------------

A tiny working example (title, message, Continue button) used to prove
the system works. Safe to delete anytime.
