This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
A significant part of this AI collaboration is an experiment to teach Claude Code how to develop in Lively4. By working together on real development tasks, we are:
This documentation serves dual purposes: guiding AI development work and creating human-readable documentation of Lively4’s development practices. The demos/claude/
directory contains examples and experiments from this collaborative learning process.
Testing:
npm test
- Run all tests with Karma (single run)npm run test-single <test-file>
- Run single test file with custom test runner (fast, ~8-13s)npm run test-list
- List all available test filesnpm run test-debug <test-file>
- Run tests with debugging (headless=false, devtools enabled)Example:
npm run test-single test/client/strings-test.js
# ✅ 6 tests passed, 0 failed in 0.01s
Development:
npm run explore-lively4
- Explore the Lively4 codebase programmaticallynpm run explore-lively4:debug
- Same as above with debug output enabledIn-Browser Development:
Lively4 System: Self-supporting browser-based development environment using:
Morph
(from src/components/widgets/lively-morph.js
)src/plugin-babel.js
initialize()
, connectedCallback()
, livelyExample()
, livelyMigrate()
Module System:
src/systemjs-config.js
defines transpilation rules per file patternliveES7
, moduleOptionsNon
, aexprViaDirective
, workspace
lively.reloadModule(path)
updates JS and templates at runtimesrc/client/morphic/component-loader.js
Central API (src/client/lively.js
):
lively.create(tagName)
- creates web componentslively.openComponentInWindow(name)
- opens components in windowslively.components.loadByName(name)
- loads component definitionslively.files.loadFile()
, lively.files.saveFile()
- file operations through lively4-serverSpecial URL Schemes (via fetch() with eval):
fetch("open://component-name")
- opens component in windowfetch("edit://path/to/file")
- opens file in editorfetch("browse://path/to/file")
- opens file in browser/container.then(r => r.object())
- returns the actual component/container objectServer Integration:
../lively4-server
(parallel directory)Directory Structure:
src/components/
- Web components (tools/, widgets/, demo/, halo/)templates/
- Reusable component templatessrc/client/
- Core runtime and utilities (lively.js, boot.js, etc.)src/external/
- Third-party librariestest/
- Test files (Karma + Mocha)doc/
- Documentation and project notesComponent Development Pattern:
templates/my-component.html
+ templates/my-component.js
Morph
and follow this structure:export default class MyComponent extends Morph {
async initialize() {
this.windowTitle = "Component Title";
this.registerButtons(); // auto-registers onButtonName handlers
lively.html.registerKeys(this); // auto-registers onKeyDown handlers
// IMPORTANT: Preserve existing state during live updates
// Use || operator to keep existing data during livelyMigrate
this._cachedData = this._cachedData || [];
this._processedResults = this._processedResults || new Map();
// Always reset volatile state:
this._currentOperation = null;
// IMPORTANT: Don't block in initialize() with async operations
// Use non-blocking calls for data loading:
this.loadData(); // NOT: await this.loadData()
}
async loadData() {
// Heavy async operations should be separate from initialize()
// This allows the component to render immediately
}
livelyExample() {
// Customize instance with example content
}
livelyMigrate(other) {
// Handle live updates during development
this.someProperty = other.someProperty;
}
}
Template Pattern (HTML):
<template id="my-component">
<style data-src="/templates/livelystyle.css"></style>
<style>/* component-specific styles */</style>
<div id="content">
<button id="myButton">Click Me</button>
</div>
</template>
Container System: lively-container
(src/components/tools/lively-container.js
)
Event System: Use lively.addEventListener()
for proper cleanup:
lively.addEventListener("myId", this, "click", evt => this.onClick(evt))
// Automatically cleaned up with lively.removeEventListener("myId", this)
Component Access:
this.get("#selector") // querySelector in component and shadowRoot
await lively.openComponentInWindow("component-name")
lively.components.searchTemplateFilename()
to locate templates programmaticallylivelyExample()
method to provide example content for componentsdemos/claude/
directory to avoid cluttering main demosNaming Conventions:
onButtonName()
(e.g., onRefreshButton()
, onSaveButton()
)
this.registerButtons()
auto-registrationBtn
suffix - always full Button
evt
as parameter name (e.g., onClick(evt)
, onKeyDown(evt)
)id="refreshButton"
→ onRefreshButton()
)class="refresh-button"
)Event Handler Registration:
// Automatic button registration
this.registerButtons(); // Finds buttons by ID and registers on[ButtonName] handlers
// Manual event registration with cleanup
lively.addEventListener("myId", this, "click", evt => this.onClick(evt))
Script Integration in Markdown Files:
import lib from 'https://cdn.example.com/lib.js'
lively.query(this, "lively-markdown")
markdownComponent.shadowRoot
for proper DOM scopingdoc/journal/
as directories named YYYY-MM-DD.md/
containing index.md
- [ ]
and - [x]
for task lists (renders as checkboxes)[filename](edit://path/to/file)
syntax for direct editing (not in code blocks)bash date +"%Y-%m-%d"
to get correct dates, not environment contextJournal Entry Format:
## YYYY-MM-DD General Day Title #hashtags #topics #keywords
*Author: @JensLincke [with @BlindGoldie]*
Brief technical description of what was implemented/changed.
- **Added**: [file.js](edit://path/to/file.js), [file.html](edit://path/to/file.html)
- **Modified**: [existing-file.js](edit://path/to/file.js) - description of changes
- **Feature**: Technical details with method names and implementation specifics
- **UI**: Interface changes and user-facing features
**Technical details:**
- Specific implementation notes
- Method signatures or key code patterns
- Integration points
**TODO**:
- [ ] #TODO Future improvements needed
Model Context Protocol (MCP) enables Claude Code to interact directly with live Lively4 environments:
Architecture:
lively-mcp
establishes WebSocket connection to server../lively4-server/src/services/mcp-server.js
implements MCP protocol../lively4-server/tools.json
defines available MCP toolsAvailable Tools:
evaluate_code
- Execute JavaScript in live browser sessionslist_sessions
- List active browser sessionsping_sessions
- Check session connectivityAdding New Tools:
../lively4-server/tools.json
with description, inputSchema, and endpointmcp-server.js
following existing patternsUsage:
// Open MCP component in browser
lively.openComponentInWindow('lively-mcp')
// Claude Code can then execute code in the live environment
TODO:
open://
, edit://
, browse://
) to support direct file/component operations without eval