# Testing, import and updates

## ZIP import in the admin panel

In the admin panel's templates section, choose ZIP import. This requires the `templates.import` permission. A valid package becomes a draft with the `testing` status; uploading does not publish the theme or change existing customer pages.

### ZIP package structure

An admin import package contains `archive.json`, GS Loom sources in `theme/`, and additional files in `assets/` when needed. Put these directly at the ZIP root or inside one enclosing folder:

```text
noir-explorer/
├── archive.json
├── theme/
│   ├── loom.json
│   ├── layout/
│   ├── templates/
│   ├── sections/
│   ├── blocks/
│   ├── config/
│   └── locales/
└── assets/
    └── blog.css
```

The `theme/` directory must contain a complete, valid theme, including `loom.json`. This tree illustrates the archive layout rather than every theme file. Do not add another enclosing folder or unrelated files such as `.DS_Store` and `__MACOSX`.

### What archive.json contains

`archive.json` describes the archive format and GrowSite product configuration. Place it next to `theme/`. It does not replace `theme/loom.json` or section and block schema files.

Example for Noir Explorer:

```json
{
  "format": "gs-loom-archive",
  "version": 1,
  "configuration": {
    "key": "noir-explorer",
    "name": "Noir Explorer",
    "version": "1.0.0",
    "tier": "basic",
    "supports_blog": true,
    "price_cents": 0,
    "default_settings": {},
    "schema": {}
  }
}
```

| Field | Meaning and requirements |
| --- | --- |
| `format` | Required: `gs-loom-archive`. |
| `version` | Archive format version: `1`. |
| `configuration.key` | Required; must match the key in `theme/loom.json`. Use 1–40 characters: a lowercase letter followed by lowercase letters, digits or hyphens. |
| `configuration.name` | Required product name, up to 200 characters. |
| `configuration.version` | Required product version, up to 30 characters. Keep it aligned with `loom.json` when preparing a release. |
| `configuration.tier` | Required: `basic`, `plus` or `premium`. |
| `configuration.supports_blog` | Required boolean, `true` or `false`, matching the theme's blog support. |
| `configuration.price_cents` | Required integer price in cents, from `0` to `10000000`. Use `0` for a free theme. |
| `configuration.default_settings` | Required default product settings; use `{}` for no additional values. |
| `configuration.schema` | Optional product schema configuration; omit it or use `{}`. This does not replace the theme's `.schema.json` files. |

### Preparing packages and troubleshooting

Start with the [GrowSite Skeleton ZIP](/downloads/growsite-skeleton.zip), which includes `archive.json`. The [Launch Theme ZIP](/downloads/launch-theme.zip) contains sources for local development: to import it through the panel, move the theme directory's contents into `theme/` and add a valid `archive.json` beside it.

| Message | Action |
| --- | --- |
| `Unsupported archive path` | Check the directory layout and ensure there is exactly one `archive.json` at the package root. The importer uses this file to detect an enclosing folder. |
| `Configuration does not match the Loom manifest` | Make `configuration.key` match the key in `theme/loom.json`. |
| `GS Loom validation failed` | Fix the theme sources according to the compiler's error details. |
| `Duplicate archive entry` | Remove duplicate paths, including paths repeated with both `/` and Windows separators. |

The upload limit is 24 MiB. An archive may contain up to 5000 entries, up to 48,000,000 uncompressed bytes in total and up to 25,000,000 bytes per file. Symbolic links and `.` or `..` path segments are rejected.

## Import a new package

Run these commands from the repository root, using your real directory path:

```bash
npm run loom:import -- /absolute/path/launch-theme
npm run themes:build
npm run themes:check
npm run loom:test
npm run themes:test
npm run build:customer
npm run build:renderer
```

Then synchronize products from the `api` directory:

```bash
php artisan themes:sync --activate
```

Import refuses an existing key. `themes:build` updates both the Loom registry and catalog. `themes:check` detects stale generated data. Synchronization activates new products with `--activate`, preserves existing product prices/status and does not overwrite customer page content.

Source import from the terminal requires repository access. ZIP import in the panel is available to authorized administrators; it is not a public marketplace submission workflow.

## Update a theme

Increase the manifest version, for example from `1.0.0` to `1.0.1`:

```bash
npm run loom:update -- /absolute/path/launch-theme
npm run themes:build
npm run themes:check
npm run build:customer
npm run build:renderer
```

The update command requires a higher numeric version and refuses removed existing sections, blocks, page templates or settings, and incompatible setting type changes. This is a compatibility guard, not a visual regression guarantee. Changing markup, CSS, defaults or targeting may still alter a saved page.

Directly editing installed source and running `themes:build` bypasses the import/update compatibility gate. Use the update command for distributed package releases. Preserve a source revision and deployment artifact so a failed release can be rolled back together with its generated registries.

## Validation checklist

| Area | Verify |
| --- | --- |
| Defaults | New instance controls match the template; opening does not save |
| Persistence | Empty string, zero and false survive save/reload |
| Blocks | Add, reorder, delete; static block remains fixed |
| Editor | Inline text, card styling and schema changes agree |
| Sources | Site/page values and fallback are correct |
| Languages | Storefront and schema dictionaries work independently |
| Rendering | Preview and public page match; no console errors |
| Responsive | Narrow mobile, desktop, long text, keyboard access |
| Migration | Existing customer documents still render correctly |

Additional renderer tests from the repository root:

```bash
node --test scripts/paylio-renderer.test.mjs scripts/theme-renderer.test.mjs
```

API validation tests, from `api`:

```bash
php artisan test --compact --filter=loom_theme_settings
php artisan test --compact tests/Feature/LoomDocumentTest.php
```

## Diagnose common failures

| Message or symptom | Action |
| --- | --- |
| `expected one schema` | Add exactly one valid schema to each section/block |
| `Unknown setting` | Correct the ID or declare it before storing a value |
| `Invalid range` | Check numeric default, min, max and positive step |
| Block missing in picker | Check targeting, private prefix and presets |
| Theme absent from catalog | Check `catalog`, `home.json`, build and API sync |
| Text shows a translation key | Check locale filename, nesting and default locale |
| Build says registry is stale | Run `themes:build`, review generated changes |
| Import says file unsupported | Remove nested folders, binaries or JavaScript |

Read the [supported scope](/en/compatibility/) before extending a theme.
