# HyperclayJS™

A modular JavaScript library for building interactive malleable HTML files with Hyperclay. Load only what you need with automatic dependency resolution.

## Features

- 🎯 **Modular Design** - Pick exactly the features you need
- 🚀 **Self-detecting Loader** - Automatic dependency resolution from URL params
- 📦 **Tree-shakeable** - Optimized for modern bundlers
- 🎨 **Rich Feature Set** - From basic save to advanced UI components
- 💪 **Zero Dependencies** - Core modules have no external dependencies
- 🔧 **Visual Configurator** - Interactive tool to build your custom bundle

## Quick Start

### Using CDN (Self-detecting Loader)

The self-detecting loader reads URL parameters and automatically loads the requested features with all dependencies.

Destructure directly from the import:

```html
<script type="module">
  const { toast, savePage } = await import('https://cdn.jsdelivr.net/npm/hyperclayjs@1/src/hyperclay.js?preset=standard');
  toast('Hello!');
</script>
```

Or with custom features:

```html
<script type="module">
  const { toast, ask } = await import('https://cdn.jsdelivr.net/npm/hyperclayjs@1/src/hyperclay.js?features=toast,dialogs');
</script>
```

**Note:** Presets include `export-to-window` by default, which also exports to `window.hyperclay`. Omit it from custom features if you only want ES module exports.

### Using NPM

```bash
npm install hyperclayjs
```

```javascript
// Import specific modules
import { savePage } from 'hyperclayjs/core/savePage';
import toast from 'hyperclayjs/ui/toast';
```

To load a full preset, use the CDN loader with `?preset=standard`:

```html
<script type="module">
  await import('https://cdn.jsdelivr.net/npm/hyperclayjs@1/src/hyperclay.js?preset=standard');
</script>
```

## Available Modules

### Core Features (Essential functionality)

| Module | Size | Description |
|--------|------|-------------|
| autosave | 1.7KB | Auto-save on DOM changes |
| edit-mode | 1.8KB | Toggle edit mode on hyperclay on/off |
| edit-mode-helpers | 6.8KB | Admin-only functionality: [viewmode:disabled], [editmode:resource], [editmode:onclick] |
| option-visibility | 9.2KB | Dynamic show/hide based on ancestor state with option:attribute="value" |
| persist | 5KB | Persist input/select/textarea values to the DOM with [persist] attribute |
| save-core | 18.9KB | Basic save function only - hyperclay.savePage() |
| save-system | 19.8KB | CMD+S, [trigger-save] button, savestatus attribute |
| save-toast | 1KB | Toast notifications for save events |
| snapshot | 14.7KB | Source of truth for page state - captures DOM snapshots for save and sync |
| unsaved-warning | 1.3KB | Warn before leaving page with unsaved changes |

### Custom Attributes (HTML enhancements)

| Module | Size | Description |
|--------|------|-------------|
| ajax-elements | 3KB | [ajax-form], [ajax-button] for async form submissions |
| dom-helpers | 6.8KB | el.nearest, el.val, el.text, el.exec, el.cycle |
| event-attrs | 5.6KB | [onclickaway], [onclickchildren], [onclone], [onpagemutation], [onrender] |
| input-helpers | 4.2KB | [prevent-enter], [autosize] for textareas |
| movable | 2.6KB | Free-positioning drag with [movable] and [movable-handle], edit mode only |
| onaftersave | 1KB | [onaftersave] attribute - run JS when save status changes |
| refetch-on-save | 0.9KB | Flash-free refetch of href/src resources on save via [refetch-on-save] attribute |
| save-freeze | 5.1KB | [freeze] attribute (legacy alias: save-freeze) - freeze element innerHTML for saves, live DOM changes freely |
| sortable | 4.4KB | Drag-drop sorting with [sortable], lazy-loads ~118KB Sortable.js in edit mode |

### UI Components (User interface elements)

| Module | Size | Description |
|--------|------|-------------|
| data-loss-panel | 20.6KB | Data-clobber guard chip: when a save overwrites saved island data, offers restore-my-data / revert-page / dismiss. Edit-mode only; pairs with the /_/data-loss endpoint. |
| dialogs | 6.9KB | ask(), consent(), tell(), snippet() dialog functions |
| quickcrop | 16.8KB | Image-crop modal for upload flows - quickcrop(file) returns a cropped Blob; uses themodal when available |
| the-modal | 27.5KB | Full modal window creation system - window.theModal |
| toast | 16.2KB | Success/error message notifications, toast(msg, msgType) |

### Utilities (Core utilities (often auto-included))

| Module | Size | Description |
|--------|------|-------------|
| cache-bust | 0.6KB | Cache-bust href/src attributes |
| cookie | 1.4KB | Cookie management (often auto-included) |
| debounce | 0.7KB | Function debouncing |
| mutation | 26.4KB | DOM mutation observation (often auto-included) |
| nearest | 3.6KB | Find nearest elements (often auto-included) |
| throttle | 1.3KB | Function throttling |

### DOM Utilities (DOM manipulation helpers)

| Module | Size | Description |
|--------|------|-------------|
| all-js | 13.6KB | Full DOM manipulation library |
| dom-ready | 0.4KB | DOM ready callback |
| form-data | 2KB | Extract form data as an object |
| style-injection | 4.2KB | Dynamic stylesheet injection |

### String Utilities (String manipulation helpers)

| Module | Size | Description |
|--------|------|-------------|
| copy-to-clipboard | 0.9KB | Clipboard utility |
| query-params | 0.3KB | Parse URL search params |
| slugify | 1KB | URL-friendly slug generator |

### Communication & Files (File handling and messaging)

| Module | Size | Description |
|--------|------|-------------|
| ai-edit | 27KB | Comment-to-edit AI editing over the local bus: hover chip / ⌘K panel per unit, whole-document bubble, streamed morph previews, one-step undo |
| file-upload | 11.3KB | File upload with progress |
| live-sync | 34KB | Real-time DOM sync across browsers (edit mode syncs peers; view mode receives saved updates) |
| send-message | 1.3KB | Message sending utility |

### Data & Undo (Page data and undo history)

| Module | Size | Description |
|--------|------|-------------|
| data | 0.5KB | Read/write structured data from the DOM via named rules tags — window.hyperclay.extractData() / applyData(). Backs the /_/api endpoint shape. |
| undo | 0.8KB | DOM-state undo/redo via MutationObserver inverse-op replay. Cmd+Z works out of the box; integrates with hypercms via window.hyperclay.undo. |
| upgrade | 5.7KB | Template upgrades: update-available popover for forks + one-click data migration from the hyper-source page — window.hyperclay.upgrade. |

### Vendor Libraries (Third-party libraries)

| Module | Size | Description |
|--------|------|-------------|
| hyper-morph | 38.5KB | DOM morphing with content-based element matching |
| hypercms | 258.8KB | Live edit-in-place CMS sidebar driven by a hyper-html-api rules tag. Pairs with [sortable] and [hyper-morph]. |
| richclay | 160.3KB | Rich text editing in place: put editable (tokens: single-line, no-toolbar, toolbar-on-select) on any element for a chromeless inline editor with a floating toolbar, or data-richclay for a card editor. Bundles its own Squire + DOMPurify. Edit-mode-only. window.RichClay / window.hyperclay.RichClay. |
| sap | 48.7KB | sapjs reactive runtime — the DOM is the only state store. Structure-first, one write path. window.Sap / window.hyperclay.Sap. |

## Presets

### Minimal (~78.9KB)
Essential features for basic editing

**Modules:** `save-core`, `snapshot`, `save-system`, `edit-mode-helpers`, `toast`, `save-toast`, `export-to-window`, `view-mode-excludes-edit-modules`

### Standard (~127.9KB)
Standard feature set for most use cases

**Modules:** `save-core`, `snapshot`, `save-system`, `unsaved-warning`, `edit-mode-helpers`, `persist`, `option-visibility`, `event-attrs`, `dom-helpers`, `data`, `data-loss-panel`, `toast`, `save-toast`, `export-to-window`, `view-mode-excludes-edit-modules`

### CMS (~401.2KB)
Visual CMS editing for rules-tag pages: hypercms sidebar, undo, drag-reorder, save

**Modules:** `save-core`, `snapshot`, `save-system`, `unsaved-warning`, `toast`, `save-toast`, `mutation`, `hypercms`, `sortable`, `undo`, `quickcrop`, `data-loss-panel`, `export-to-window`, `view-mode-excludes-edit-modules`

### Smooth Sailing (~736.1KB)
Everything, without gotchas

**Modules:** `save-core`, `save-system`, `unsaved-warning`, `save-toast`, `edit-mode-helpers`, `persist`, `snapshot`, `option-visibility`, `edit-mode`, `event-attrs`, `ajax-elements`, `sortable`, `movable`, `dom-helpers`, `input-helpers`, `onaftersave`, `save-freeze`, `dialogs`, `quickcrop`, `toast`, `the-modal`, `data-loss-panel`, `mutation`, `nearest`, `cookie`, `throttle`, `debounce`, `dom-ready`, `window-load`, `all-js`, `style-injection`, `form-data`, `hypercms`, `richclay`, `undo`, `data`, `upgrade`, `slugify`, `copy-to-clipboard`, `query-params`, `behavior-collector`, `send-message`, `file-upload`, `live-sync`, `refetch-on-save`, `export-to-window`, `view-mode-excludes-edit-modules`

### Everything (~852.6KB)
All available features

Includes all available modules across all categories.

## Lazy-Loaded Modules

Some modules with large vendor dependencies are **lazy-loaded** to optimize page performance:

| Module | Wrapper Size | Vendor Size | Loaded When |
|--------|-------------|-------------|-------------|
| `sortable` | ~3KB | ~118KB | Edit mode only |

**How it works:**
- The wrapper module checks if the page is in edit mode (`isEditMode`)
- If true, it injects a `<script save-remove>` tag that loads the vendor script
- If false, nothing is loaded - viewers don't download the heavy scripts
- The `save-remove` attribute (the legacy alias for `no-save`) strips the script tag when the page is saved

This means:
- **Editors** get full functionality when needed
- **Viewers** never download the heavy vendor scripts
- **Saved pages** stay clean with no leftover script tags

## Visual Configurator

Explore features and build your custom bundle with our interactive configurator:

```bash
npm run dev
```

This will:
1. Generate fresh dependency data
2. Start a local server on port 3535
3. Open the configurator in your browser

The configurator shows:
- Real-time bundle size calculation
- Automatic dependency resolution
- Generated CDN URL
- Feature descriptions and categories

## Development

### Project Structure

```
hyperclayjs/
├── src/hyperclay.js          # Self-detecting module loader
├── src/core/                 # Core hyperclay features
├── src/custom-attributes/    # HTML attribute enhancements
├── src/ui/                   # UI components (toast, modals, prompts)
├── src/utilities/            # General utilities (mutation, cookie, etc.)
├── src/dom-utilities/        # DOM manipulation helpers
├── src/string-utilities/     # String manipulation tools
├── src/communication/        # File upload and messaging
├── src/vendor/               # Third-party libraries (Sortable.js, etc.)
├── scripts/                  # Build and generation scripts
└── website/config.html       # Interactive configurator
```

### Setup

```bash
# Install dependencies
npm install

# Generate dependency graph
npm run generate:deps

# Start development server with configurator
npm run dev

# Build bundles
npm run build

# Run tests
npm test
```

### Automatic Dependency Graph

The project uses Madge to automatically analyze dependencies and generate rich metadata:

```bash
npm run generate:deps
```

This creates `module-dependency-graph.generated.json` with:
- Complete dependency tree
- Actual file sizes
- Category assignments
- Preset configurations

The configurator dynamically loads this file to always show accurate information.

## Browser Support

- Chrome 90+
- Firefox 88+
- Safari 14+
- Edge 90+

The loader uses ES modules with top-level await. Use `await import()` to ensure modules finish loading before your code runs.

## API Examples

### Save System

```javascript
// Manually save the page
hyperclay.savePage();

// Add save button
hyperclay.initHyperclaySaveButton(); // Looks for [trigger-save]

// Keyboard shortcut
hyperclay.initSaveKeyboardShortcut(); // CMD/CTRL+S
```

Any element with the `trigger-save` attribute saves the page on click. For
viewers who can't save (not the owner / not in edit mode), the loader shows a
toast instead — "You're not the owner, changes are local only" — even on pages
that exclude the save modules via `view-mode-excludes-edit-modules`.

### Toast Notifications

```javascript
toast("Operation successful!", "success");
toast("Something went wrong", "error");
```

### Dialog Prompts

```javascript
// Ask for input
const name = await ask("What's your name?");

// Get consent
const agreed = await consent("Do you agree to terms?");

// Show message
tell("Welcome to Hyperclay!");
```

### Custom Attributes

```html
<!-- AJAX form submission -->
<form ajax-form="/api/submit">
  <input name="email" type="email">
  <button>Submit</button>
</form>

<!-- Auto-resize textarea -->
<textarea autosize></textarea>

<!-- Drag-drop sorting (dispatches a bubbling `clay:sorted` event on drop) -->
<ul sortable>
  <li>Item 1</li>
  <li>Item 2</li>
  <li>Item 3</li>
</ul>

<!-- Run code when clicked away -->
<div onclickaway="console.log('Clicked outside')">
  Click outside this div
</div>

<!-- Persist input/select/textarea values -->
<input type="text" name="username" persist>
```

### Admin Features

```html
<!-- Only visible/editable in edit mode -->
<div contenteditable editmode:contenteditable>Admin can edit this</div>
<input type="text" viewmode:disabled>
<script editmode:resource>console.log('Admin only');</script>
```

### Rich Text Editing (richclay)

Requires the `richclay` module (included in the `smooth-sailing` and `everything` presets). Editors activate only in edit mode; the saved file keeps just the author's markup plus the marker attribute.

```html
<!-- Inline editor: edits the element in place, matches the page's own styles,
     floating toolbar appears on focus. Value tokens combine like class names. -->
<h1 editable="single-line">Page title</h1>
<div editable>
  <p>Multi-line rich text with a floating toolbar.</p>
</div>
<p editable="single-line no-toolbar">No toolbar, keyboard shortcuts only</p>
<p editable="toolbar-on-select">Toolbar only while text is selected</p>

<!-- Card editor: bordered editor box with an attached toolbar -->
<div data-richclay aria-label="Article body">
  <p>Saved content.</p>
</div>
```

Full token reference, options, and custom toolbar buttons: the [richclay README](https://github.com/panphora/richclay#readme). `window.RichClay` / `window.hyperclay.RichClay` expose the class for custom buttons and programmatic use.

## Module Creation

Each module should be a self-contained ES module:

```javascript
// features/my-feature.js
import dependency from '../utilities/dependency.js';

export default function myFeature() {
  // Feature implementation
}

// Auto-init when module is imported
myFeature();
```

## Migration from Monolithic Script

### Before
```html
<script src="/public/js/old-hyperclay.js"></script>
```

### After
```html
<!-- Use preset -->
<script src="/public/js/hyperclay.js?preset=standard" type="module"></script>

<!-- Or specific features -->
<script src="/public/js/hyperclay.js?features=save-core,edit-mode-helpers,toast" type="module"></script>
```

## Contributing

1. Fork the repository
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
3. Make your changes
4. Run `npm run generate:deps` to update the dependency graph
5. Test your changes with `npm run dev`
6. Commit your changes (`git commit -m 'Add amazing feature'`)
7. Push to the branch (`git push origin feature/amazing-feature`)
8. Open a Pull Request

## License

MIT © Hyperclay

### Third-Party Credits

This project includes the following open-source libraries:

- **[HyperMorph](https://github.com/hyperclay/hyper-morph)** - DOM morphing with content-based element matching (0BSD)
- **[Sortable.js](https://github.com/SortableJS/Sortable)** - Drag-and-drop library (MIT)

## Links

- [Documentation](https://hyperclay.com/docs)
- [Examples](https://hyperclay.com/examples)
- [Configurator](https://hyperclay.com/configurator)
- [GitHub Issues](https://github.com/panphora/hyperclayjs/issues)
