Telar: Docs Telar: Documentación

Demo System Architecture

Technical documentation for Telar’s demo content fetching and integration system.

Overview

The demo system provides pre-built example stories (telar-tutorial, paisajes-demo) that are automatically fetched from content.telar.org during the build process and merged with user content.

Key components:

Architecture Diagram

Build Process:
1. fetch_demo_content.py runs (if include_demo_content: true)
   ↓
2. Downloads telar-demo-bundle.json to _demo_content/
   ↓
3. csv_to_json.py runs
   ↓
4. load_demo_bundle() reads _demo_content/telar-demo-bundle.json
   ↓
5. merge_demo_content() integrates demos with user content
   ↓
6. Jekyll processes merged content

Fetch Mechanism

Configuration Check

fetch_demo_content.py first reads _config.yml:

def should_fetch_demos():
    config = load_config('_config.yml')
    return config.get('story_interface', {}).get('include_demo_content', False)

If include_demo_content: false or unset:

Version Matching Algorithm

The system fetches versions.json index to find compatible demo versions:

{
  "versions": ["0.4.0", "0.5.0", "0.6.0"],
  "latest": "0.6.0"
}

Matching logic:

  1. Read telar.version from _config.yml (e.g., “v0.6.0-beta”)
  2. Strip -beta suffix and v prefix → “0.6.0”
  3. Find highest available version ≤ site version
  4. Construct demo URL: https://content.telar.org/demos/v{version}/{lang}/telar-demo-bundle.json

Example scenarios:

Site Version Available Versions Selected Version
v0.6.0-beta 0.4.0, 0.5.0, 0.6.0 0.6.0
v0.5.5 0.4.0, 0.5.0, 0.6.0 0.5.0
v0.7.0 0.4.0, 0.5.0, 0.6.0 0.6.0
v0.3.0 0.4.0, 0.5.0, 0.6.0 None (too old)

Language Selection

Language is determined by telar_language setting:

def get_demo_language():
    config = load_config('_config.yml')
    return config.get('telar_language', 'en')

Maps to demo URLs:

HTTP Fetching

def fetch_demo_bundle(url):
    try:
        response = requests.get(url, timeout=30)
        response.raise_for_status()
        return response.json()
    except requests.RequestException as e:
        log_error(f"Failed to fetch demos: {e}")
        return None

Error handling:

All errors are non-fatal. The site builds without demos if fetching fails.

Bundle Structure

Single-Bundle Format

As of v0.6.0, demos are delivered as a single JSON file:

{
  "version": "0.6.0",
  "language": "en",
  "generated": "2025-11-28T10:00:00Z",
  "projects": [ ... ],
  "objects": [ ... ],
  "stories": {
    "telar-tutorial": [ ... ],
    "paisajes-demo": [ ... ]
  },
  "glossary": [ ... ]
}

Projects Array

"projects": [
  {
    "number": 1,
    "story_id": "telar-tutorial",
    "title": "Telar Tutorial",
    "subtitle": "Interactive Guide to Telar Features",
    "byline": "Start here to learn Telar"
  },
  {
    "number": 2,
    "story_id": "paisajes-demo",
    "title": "Paisajes Coloniales",
    "subtitle": "Colonial Cartography Excerpt",
    "byline": "Scholarly narrative example"
  }
]

Objects Array

"objects": [
  {
    "object_id": "demo-leviathan",
    "title": "Leviathan Frontispiece (1651)",
    "iiif_manifest": "",
    "iiif_source_url": "https://content.telar.org/iiif/demo-leviathan/info.json",
    "creator": "Abraham Bosse",
    "period": "1651",
    "credit": "Public Domain"
  }
]

IIIF Auto-Population:

Stories Object

"stories": {
  "telar-tutorial": [
    {
      "step": 1,
      "object": "demo-tutorial-iiif",
      "x": 0,
      "y": 0,
      "zoom": 0,
      "question": "What is IIIF?",
      "answer": "...",
      "layer1_button": "Learn More",
      "layer1_file": "iiif-intro",
      "layer1_content": "# What is IIIF?\n\nIIIF (pronounced..."
    }
  ]
}

Content Inclusion:

Glossary Array

"glossary": [
  {
    "term": "iiif",
    "title": "IIIF",
    "content": "# IIIF\n\nInternational Image Interoperability Framework..."
  }
]

Content Merging

Integration Point

csv_to_json.py handles merging:

def main():
    # Load user content
    user_projects = process_project_csv()
    user_objects = process_objects_csv()
    user_stories = process_story_csvs()

    # Load and merge demo content
    demo_bundle = load_demo_bundle()
    if demo_bundle:
        merge_demo_content(demo_bundle, user_projects, user_objects, user_stories)

    # Write merged results
    write_json_files(user_projects, user_objects, user_stories)

Merge Process

Projects:

def merge_projects(user_projects, demo_projects):
    # User projects first, then demos
    merged = user_projects + demo_projects
    # Renumber if needed
    for i, project in enumerate(merged, 1):
        project['number'] = i
    return merged

Objects:

def merge_objects(user_objects, demo_objects):
    # Simple concatenation, unique object_ids guaranteed
    return user_objects + demo_objects

Stories:

def merge_stories(user_stories, demo_stories):
    # Demo stories written as separate JSON files
    for story_id, steps in demo_stories.items():
        write_story_json(f"{story_id}.json", steps)

Glossary:

def merge_glossary(demo_glossary):
    # Written to _data/demo-glossary.json (not components/)
    write_json('_data/demo-glossary.json', demo_glossary)

Widget Processing

Demo content undergoes full processing pipeline:

  1. Widgets: Accordion, tabs, carousel syntax converted
  2. Image sizing: Panel images processed for dimensions
  3. Markdown: Full markdown-to-HTML conversion
  4. Glossary links: [term:glossary-term] syntax converted

This happens in csv_to_json.py after merging.

Demo Badges

Demo content is marked with demo: true flag:

{
  "step": 1,
  "object": "demo-tutorial-iiif",
  "demo": true,
  ...
}

Templates check this flag to display badges:

{% if step.demo %}
  <span class="demo-badge">{{ lang.demo.panel_badge }}</span>
{% endif %}

File System

Directory Structure

_demo_content/               # Gitignored
└── telar-demo-bundle.json   # Downloaded bundle

_data/
├── demo-glossary.json       # Demo glossary (generated)
├── objects.json             # Merged objects
├── project.json             # Merged projects
├── telar-tutorial.json      # Demo story
└── paisajes-demo.json       # Demo story

Gitignore Rules

# Demo content (ephemeral)
_demo_content/

# Demo glossary files
components/texts/glossary/_demo_*
_data/demo-glossary.json

Demo content never enters version control.

GitHub Actions Integration

Workflow Step

.github/workflows/build.yml:

- name: Fetch demo content (if enabled)
  run: python3 scripts/fetch_demo_content.py
  continue-on-error: true

- name: Process CSV to JSON
  run: python3 scripts/csv_to_json.py

Key points:

Error Handling

Graceful Degradation

All demo fetch errors are non-fatal:

try:
    bundle = fetch_demo_bundle(url)
    if bundle:
        save_bundle(bundle)
except Exception as e:
    logger.warning(f"Demo fetch failed: {e}")
    logger.info("Building without demo content")
    # No raise, no sys.exit()

Site builds successfully without demos.

Common Errors

Error Cause Behavior
Network timeout content.telar.org unreachable Build continues, no demos
404 Not Found Version not available Build continues, no demos
Invalid JSON Malformed bundle Build continues, no demos
Version mismatch No compatible version Build continues, no demos
Config syntax Invalid YAML Script may fail, build fails

Only config syntax errors are fatal.

Debugging

Enable Verbose Logging

import logging
logging.basicConfig(level=logging.DEBUG)

Check Fetch Status

# Run fetch manually
python3 scripts/fetch_demo_content.py

# Check if bundle downloaded
ls -lh _demo_content/
cat _demo_content/telar-demo-bundle.json | python3 -m json.tool | head

Verify Merge

# Check merged content
cat _data/project.json | grep -i demo
cat _data/objects.json | grep -i demo
ls -1 _data/*demo*.json

Test Version Matching

from scripts.fetch_demo_content import find_compatible_version

site_version = "0.6.0"
available = ["0.4.0", "0.5.0", "0.6.0"]
selected = find_compatible_version(site_version, available)
print(f"Selected: {selected}")  # Should be 0.6.0

Performance

Bundle Size

Typical bundle sizes:

Fetch Time

Caching

No caching currently implemented. Each build fetches fresh content.

Future consideration: Cache with TTL for local development.

Security

HTTPS Enforcement

All demo URLs use HTTPS:

DEMO_BASE_URL = "https://content.telar.org"  # Not http://

Content Validation

Basic validation on fetched bundle:

def validate_bundle(bundle):
    required_keys = ['version', 'language', 'projects', 'objects', 'stories']
    return all(key in bundle for key in required_keys)

No Code Execution

Demo content is data only (JSON). No executable code in bundles.

Troubleshooting

Demos Not Appearing

Check configuration:

grep include_demo_content _config.yml
# Should show: include_demo_content: true

Check fetch log:

python3 scripts/fetch_demo_content.py
# Look for errors or warnings

Check bundle exists:

ls _demo_content/telar-demo-bundle.json
# Should exist if fetch succeeded

Version Mismatch

Check versions.json:

curl https://content.telar.org/versions.json
# Compare with your site version

Check selected version:

python3 scripts/fetch_demo_content.py --verbose
# Shows version selection logic

Network Errors

Test connectivity:

curl -I https://content.telar.org/
# Should return 200 OK

Check firewall:


New in v0.6.0: Automated demo fetching with version matching and language support.