Metadata-Version: 2.4
Name: scaffold-xblock
Version: 0.1.2
Summary: Open edX XBlock adapter for Scaffold.
License-Expression: AGPL-3.0-only
Project-URL: Homepage, https://scaffold.ac
Project-URL: Repository, https://github.com/brainjamworks/scaffold
Project-URL: Issues, https://github.com/brainjamworks/scaffold/issues
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
License-File: THIRD_PARTY_NOTICES.md
Requires-Dist: XBlock<7,>=5.2
Dynamic: license-file

# Scaffold for Open edX

Scaffold is an Open edX XBlock for creating and delivering interactive learning
content. The public Python distribution is `scaffold-xblock`, and the Open edX
advanced-module identifier is `scaffold`.

`0.1.2` is an alpha release. Test it in a non-production Open edX environment
before enabling it on a live course.

## Requirements

- Python 3.11 or later
- An Open edX deployment that can install `XBlock>=5.2,<7`
- Administrator access to rebuild and restart the LMS and CMS/Studio services

The
[Scaffold 0.1.2 release record](https://github.com/brainjamworks/scaffold/releases/tag/v0.1.2)
records the exact Open edX host version used for the release smoke test, package
checksums, and build provenance.

## Install with Tutor

Add the pinned package to Tutor's persistent Open edX requirements:

```sh
tutor config save --append OPENEDX_EXTRA_PIP_REQUIREMENTS=scaffold-xblock==0.1.2
tutor images build openedx
tutor local reboot -d
```

Do not use a one-off `pip install` inside a running Tutor container as the
normal installation method; it will be lost when the container is replaced.

Verify the installed version in both Open edX services:

```sh
tutor local exec lms python -m pip show scaffold-xblock
tutor local exec cms python -m pip show scaffold-xblock
```

Each command must report `Version: 0.1.2`.

## Install without Tutor

Install the same pinned package in the Python environments used by both the LMS
and CMS/Studio:

```sh
python -m pip install "scaffold-xblock==0.1.2"
python -m pip show scaffold-xblock
```

Restart both services using the deployment's normal service manager. A package
installed in only one service is not a complete Open edX installation.

PyPI is the normal installation channel. If PyPI is temporarily unavailable,
the exact approved wheel is also attached to the
[versioned GitHub Release](https://github.com/brainjamworks/scaffold/releases/tag/v0.1.2);
do not install GitHub's automatically generated source archive.

## Enable Scaffold in a Course

After the administrator installation and service restart:

1. Open the course in Studio.
2. Open **Settings → Advanced Settings**.
3. Add `"scaffold"` to the **Advanced Module List** without removing existing
   module identifiers.
4. Save the settings.
5. In a unit, select **Advanced → Scaffold** and create a test component.

The identifier must be exactly `"scaffold"` because it matches the package's
`xblock.v1` entry point.

## Upgrade

Change the pinned version in `OPENEDX_EXTRA_PIP_REQUIREMENTS`, rebuild the
`openedx` image, reboot Tutor, and verify the package version in both services.
Review [CHANGES.md](./CHANGES.md) and the versioned GitHub Release before every
upgrade.

## Development

The adapter is intentionally thin:

- `scaffold_xblock/` is the Python XBlock package.
- `frontend/src/` mounts `@scaffold/core` into Studio and LMS views.
- `scaffold_xblock/public/` is generated by `vp run @scaffold/adapter-xblock#build`.

### Build

```sh
vp run verify:types
vp run @scaffold/adapter-xblock#build
python3 -m py_compile adapters/xblock/scaffold_xblock/scaffold.py adapters/xblock/scaffold_xblock/__init__.py
```

Python 3.12 or later is required to build and package the distribution.

### Package

From the repository root, build and verify the installable wheel and source
distribution:

```sh
vp run @scaffold/adapter-xblock#package
```

The command requires the root product version, `pyproject.toml`, and the dated
release heading in `CHANGES.md` to agree. It builds the frontend, runs adapter
tests, checks both Python distributions, installs and loads the exact wheel in
a clean virtual environment, and confirms that the source distribution can
build a valid wheel independently.

Successful artifacts and SHA-256 sidecars are written to
`dist/release/X.Y.Z/` at the repository root. Packaging does not upload to PyPI
or create a Git tag or GitHub Release.

The wheel and source distribution include Scaffold's `LICENSE` and generated
`THIRD_PARTY_NOTICES.md`.

## Fullscreen Compatibility

The Studio layout shim detects the host surface:

- Newer Open edX Studio releases with `.fullscreen-button` use the host-native fullscreen control and only stretch the inner Scaffold mount.
- Older Studio modal releases without native fullscreen get the legacy `.modal-type-scaffold.modal-window.modal-editor` 98vw/98vh fallback.
- Iframe-backed runtimes cannot be resized from inside the iframe; the adapter fills the iframe and leaves parent sizing to Open edX.

## Open edX Studio Lifecycle

The XBlock static loaders in `scaffold_xblock/static/` are intentionally tiny
and stable. They unwrap Open edX's jQuery-wrapped element, dynamically import
the Vite-built bundle URL passed by Python, and render a visible error if the
bundle fails to load. Keep this bootstrap shape close to the deployed Brainjam
XBlock pattern.

Studio save/close behavior is adapter-owned:

- First-run document creation goes through `create_artifact`; subsequent
  document persistence goes through `save_content`.
- Explicit Studio host actions use Open edX runtime notifications:
  `runtime.notify('save', { state: 'start' })`,
  `runtime.notify('save', { state: 'end' })`, and
  `runtime.notify('cancel', {})`.
- Do not put Open edX modal close/publish behavior into the shared Scaffold
  port contract. Moodle and other hosts have different authoring
  lifecycles.

## Adapter Seams

- Persistence is JSON content creation/save through `create_artifact` and
  `save_content`; the XBlock owns the stored Tiptap document. Authoring
  mutations require Studio write permission.
- Collaboration is disabled by wiring a null provider. Studio is treated as a
  single-author surface.
- Media upload/resolve uses the Open edX contentstore for managed upload-backed
  blocks and attachments. Workbench or non-Open edX runtimes without
  `StaticContent` return an explicit unavailable error. Uploads require Studio
  write permission; LMS resolves existing assets only.
- Assessment state hydrates from the XBlock user-state field into the core
  assessment store so submitted responses, results, and attempt counts survive
  page reloads.

## Grading Boundary

Browser grading is not treated as production grading. `submit_assessment` and
`check_assessment` run through the XBlock handler and reconstruct answer keys
from the stored Tiptap JSON.

Server-side grading mirrors `packages/grading/src` for `mcq`, `dropdown`,
`multiselect`, `sequencing`, `image_hotspot`, `matching`, `categorise`, and
`fill_blanks`. Unknown future block types return an explicit unsupported error
until their server projections are implemented.

The XBlock derives `blockType`, `points`, `isGraded`, and `maxAttempts` from
stored content, not from browser payloads. Ungraded practice problems can still
return feedback but do not publish a grade event.
