From 39ccf0c4503bca8a6c50f34d0c9395a369c9445f Mon Sep 17 00:00:00 2001 From: Michael Freno Date: Sun, 16 Nov 2025 09:36:46 -0500 Subject: [PATCH] v0.2.1 release --- .github/workflows/release.yml | 228 ++ .gitignore | 4 + FlexLove.lua | 4 +- README.md | 21 +- RELEASE.md | 209 ++ docs/.nojekyll | 0 docs/README.md | 85 +- docs/VERSIONING.md | 80 + docs/WORKFLOW.md | 153 ++ docs/api.html | 3803 +++++++++++++++++++++++++++++++++ docs/build-docs.js | 683 ++++++ docs/doc-filter.js | 47 + docs/doc.json | 12 +- docs/doc.md | 4 +- docs/examples.html | 1075 ++++++++++ docs/index.html | 446 ++-- docs/package-lock.json | 125 ++ docs/package.json | 13 + docs/update-version.js | 34 + docs/versions/README.md | 86 + docs/versions/v0.2.0/api.html | 3732 ++++++++++++++++++++++++++++++++ generate_docs.sh | 40 - scripts/archive-docs.sh | 58 + scripts/create-release.sh | 99 + scripts/generate_docs.sh | 72 + scripts/make-tag.sh | 177 ++ 26 files changed, 11070 insertions(+), 220 deletions(-) create mode 100644 .github/workflows/release.yml create mode 100644 RELEASE.md create mode 100644 docs/.nojekyll create mode 100644 docs/VERSIONING.md create mode 100644 docs/WORKFLOW.md create mode 100644 docs/api.html create mode 100644 docs/build-docs.js create mode 100644 docs/doc-filter.js create mode 100644 docs/examples.html create mode 100644 docs/package-lock.json create mode 100644 docs/package.json create mode 100644 docs/update-version.js create mode 100644 docs/versions/README.md create mode 100644 docs/versions/v0.2.0/api.html delete mode 100755 generate_docs.sh create mode 100755 scripts/archive-docs.sh create mode 100755 scripts/create-release.sh create mode 100755 scripts/generate_docs.sh create mode 100755 scripts/make-tag.sh diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..ef707f6 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,228 @@ +name: Release + +on: + push: + tags: + - 'v*' + workflow_dispatch: + +permissions: + contents: write + +jobs: + build-and-release: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Extract version from tag or FlexLove.lua + id: version + run: | + if [[ "$GITHUB_REF" == refs/tags/v* ]]; then + VERSION=${GITHUB_REF#refs/tags/v} + echo "Triggered by tag: $GITHUB_REF_NAME" + else + VERSION=$(grep -m 1 "_VERSION" FlexLove.lua | sed -E 's/.*"([^"]+)".*/\1/') + echo "Triggered manually, using FlexLove.lua version" + fi + + echo "version=$VERSION" >> $GITHUB_OUTPUT + echo "tag=v${VERSION}" >> $GITHUB_OUTPUT + + FLEX_VERSION=$(grep -m 1 "_VERSION" FlexLove.lua | sed -E 's/.*"([^"]+)".*/\1/') + echo "FlexLove.lua version: $FLEX_VERSION" + echo "Release version: $VERSION" + + if [ "$FLEX_VERSION" != "$VERSION" ]; then + echo "⚠️ Warning: Version mismatch detected" + echo " FlexLove.lua: $FLEX_VERSION" + echo " Release version: $VERSION" + echo " Using: $VERSION" + else + echo "✓ Version check passed" + fi + + - name: Install lua-language-server + run: | + LLS_VERSION="3.7.4" + wget -q "https://github.com/LuaLS/lua-language-server/releases/download/${LLS_VERSION}/lua-language-server-${LLS_VERSION}-linux-x64.tar.gz" + mkdir -p ~/.local/bin/lua-language-server + tar -xzf "lua-language-server-${LLS_VERSION}-linux-x64.tar.gz" -C ~/.local/bin/lua-language-server + echo "$HOME/.local/bin/lua-language-server/bin" >> $GITHUB_PATH + rm "lua-language-server-${LLS_VERSION}-linux-x64.tar.gz" + + - name: Verify lua-language-server installation + run: | + lua-language-server --version + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: '18' + cache: 'npm' + cache-dependency-path: docs/package.json + + - name: Install Node.js dependencies + working-directory: docs + run: npm ci + + - name: Make scripts executable + run: | + chmod +x scripts/generate_docs.sh + chmod +x scripts/create-release.sh + chmod +x scripts/archive-docs.sh + + - name: Archive previous documentation version + run: | + if [ -f "docs/api.html" ]; then + OLD_VERSION=$(grep -o 'FlexLöve v[0-9.]*' docs/api.html | head -1 | sed 's/FlexLöve v//') + if [ -n "$OLD_VERSION" ]; then + echo "Found previous version: v${OLD_VERSION}" + mkdir -p "docs/versions/v${OLD_VERSION}" + cp docs/api.html "docs/versions/v${OLD_VERSION}/api.html" + echo "✓ Archived previous documentation to docs/versions/v${OLD_VERSION}/" + else + echo "No previous version found, skipping archival" + fi + else + echo "No existing documentation to archive" + fi + + - name: Generate documentation + run: | + ./scripts/generate_docs.sh + # Verify api.html was created + if [ ! -f "docs/api.html" ]; then + echo "Error: docs/api.html was not generated" + exit 1 + fi + echo "✓ Documentation generated successfully" + + - name: Commit archived documentation + if: startsWith(github.ref, 'refs/tags/') + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + git add docs/versions/ docs/api.html docs/doc.json docs/doc.md + git commit -m "Archive previous documentation and generate v${{ steps.version.outputs.version }} docs [skip ci]" || echo "No changes to commit" + git push origin HEAD:main || echo "Nothing to push" + + - name: Create release package + run: | + # Run release script non-interactively (auto-confirm overwrite) + echo "y" | ./scripts/create-release.sh || ./scripts/create-release.sh + # Verify files were created + VERSION="${{ steps.version.outputs.version }}" + if [ ! -f "releases/flexlove-v${VERSION}.zip" ]; then + echo "Error: Release zip was not created" + exit 1 + fi + if [ ! -f "releases/flexlove-v${VERSION}.zip.sha256" ]; then + echo "Error: Checksum file was not created" + exit 1 + fi + echo "✓ Release package created successfully" + + - name: Upload release artifacts + uses: actions/upload-artifact@v4 + with: + name: release-assets + path: | + releases/flexlove-v${{ steps.version.outputs.version }}.zip + releases/flexlove-v${{ steps.version.outputs.version }}.zip.sha256 + docs/api.html + retention-days: 90 + + - name: Download release artifacts + uses: actions/download-artifact@v4 + with: + name: release-assets + path: ./ + + - name: Extract checksum for release notes + id: checksum + run: | + VERSION="${{ steps.version.outputs.version }}" + CHECKSUM=$(cat "releases/flexlove-v${VERSION}.zip.sha256" | cut -d ' ' -f 1) + echo "checksum=$CHECKSUM" >> $GITHUB_OUTPUT + echo "Checksum: $CHECKSUM" + + - name: Check if pre-release + id: prerelease + run: | + VERSION="${{ steps.version.outputs.version }}" + if [[ "$VERSION" =~ (alpha|beta|rc|dev) ]]; then + echo "is_prerelease=true" >> $GITHUB_OUTPUT + echo "This is a pre-release version" + else + echo "is_prerelease=false" >> $GITHUB_OUTPUT + echo "This is a stable release" + fi + + - name: Generate release notes + id: release_notes + run: | + VERSION="${{ steps.version.outputs.version }}" + CHECKSUM="${{ steps.checksum.outputs.checksum }}" + cat > release_notes.md << 'EOF' + ## Installation + + Download `flexlove-v${{ steps.version.outputs.version }}.zip` and extract to your LÖVE2D project: + + ```bash + unzip flexlove-v${{ steps.version.outputs.version }}.zip + cp -r flexlove/modules ./ + cp flexlove/FlexLove.lua ./ + ``` + + ## Verification + + Verify the download integrity using SHA256: + + ```bash + shasum -a 256 -c flexlove-v${{ steps.version.outputs.version }}.zip.sha256 + ``` + + **SHA256 Checksum:** + ``` + ${{ steps.checksum.outputs.checksum }} + ``` + + ## Documentation + + 📚 [View Documentation](https://github.com/${{ github.repository }}/tree/main/docs) + + ## What's Included + + - `FlexLove.lua` - Main library file + - `modules/` - All required module files (27 files) + - `LICENSE` - MIT License + - `README.txt` - Installation instructions + + ## Requirements + + - LÖVE2D 11.0 or higher + + --- + + _For examples and full source code, visit the [repository](https://github.com/${{ github.repository }})._ + EOF + cat release_notes.md + + - name: Create GitHub Release + uses: softprops/action-gh-release@v1 + with: + tag_name: v${{ steps.version.outputs.version }} + name: FlexLöve v${{ steps.version.outputs.version }} + body_path: release_notes.md + files: | + releases/flexlove-v${{ steps.version.outputs.version }}.zip + releases/flexlove-v${{ steps.version.outputs.version }}.zip.sha256 + prerelease: ${{ steps.prerelease.outputs.is_prerelease }} + draft: false + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore index 9baa3ec..b1e5bc7 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,7 @@ themes/space/ tasks testoutput luacov.* +docs/doc.json +docs/doc.md +docs/node_modules +releases/ diff --git a/FlexLove.lua b/FlexLove.lua index 70a332c..2d11a96 100644 --- a/FlexLove.lua +++ b/FlexLove.lua @@ -63,8 +63,8 @@ Units.initialize(Context) Units.initializeErrorHandler(ErrorHandler) -- Add version and metadata -flexlove._VERSION = "0.2.0" -flexlove._DESCRIPTION = "UI Library for LÖVE Framework based on flexbox" +flexlove._VERSION = "0.2.1" +flexlove._DESCRIPTION = "0I Library for LÖVE Framework based on flexbox" flexlove._URL = "https://github.com/mikefreno/FlexLove" flexlove._LICENSE = [[ MIT License diff --git a/README.md b/README.md index 3fd4766..b228580 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# FlexLöve v0.2.0 +# FlexLöve v0.2.1 **A comprehensive UI library providing flexbox/grid layouts, theming, animations, and event handling for LÖVE2D games.** @@ -67,6 +67,25 @@ function love.draw() end ``` +## Documentation + +📚 **[View Full API Documentation](https://mikefreno.github.io/FlexLove/api.html)** + +Complete API reference with all classes, methods, and properties is available on GitHub Pages. The documentation includes: + +- Searchable sidebar navigation +- Syntax-highlighted code examples +- Version selector (access docs for previous versions) +- Detailed parameter and return value descriptions + +### Documentation Versions + +Access documentation for specific versions: +- **Latest:** [https://mikefreno.github.io/FlexLove/api.html](https://mikefreno.github.io/FlexLove/api.html) +- **Specific version:** `https://mikefreno.github.io/FlexLove/versions/v0.2.0/api.html` + +Use the version dropdown in the documentation header to switch between versions. + ## API Conventions ### Method Patterns diff --git a/RELEASE.md b/RELEASE.md new file mode 100644 index 0000000..28a63ae --- /dev/null +++ b/RELEASE.md @@ -0,0 +1,209 @@ +# FlexLöve Release Process + +This document describes how to create and publish a new release of FlexLöve. + +## Automated Release (Recommended) + +The easiest way to create a release is using the automated script: + +```bash +./scripts/make-tag.sh +``` + +This interactive script will: +1. Show your current version +2. Ask you to select: Major / Minor / Patch / Custom version bump +3. Calculate the new version (resetting lower components to 0) +4. Update `FlexLove.lua` with the new version +5. Update `README.md` first line with the new version +6. Create a git commit: `v{version} release` +7. Create a git tag: `v{version}` +8. Prompt you to push the changes + +After pushing the tag, GitHub Actions automatically: +- Archives previous documentation +- Generates new documentation +- Creates release package with SHA256 checksums +- Publishes GitHub release with download assets + +### Example Usage + +```bash +$ ./scripts/make-tag.sh + +═══════════════════════════════════════ + FlexLöve Version Bump & Tag Tool +═══════════════════════════════════════ + +Current version: v0.2.0 + +Select version bump type: + 1) Major (breaking changes) 0.2.0 → 1.0.0 + 2) Minor (new features) 0.2.0 → 0.3.0 + 3) Patch (bug fixes) 0.2.0 → 0.2.1 + 4) Custom version + 5) Cancel + +Enter choice (1-5): 2 + +New version: v0.3.0 + +This will: + 1. Update FlexLove.lua → flexlove._VERSION = "0.3.0" + 2. Update README.md → first line version + 3. Stage changes for commit + 4. Create git tag v0.3.0 + +Proceed? (y/n) y + +✓ Version bump complete! + +Next steps: + 1. Push commit and tag: + git push && git push origin v0.3.0 + + 2. GitHub Actions will automatically: + • Archive previous documentation + • Generate new documentation + • Create release package with checksums + • Publish GitHub release +``` + +## Manual Release Workflow + +If you need more control, follow these steps: + +### 1. Update Version + +Edit `FlexLove.lua` and update the version: + +```lua +flexlove._VERSION = "0.3.0" -- Update this line +``` + +Also update `README.md` first line: +```markdown +# FlexLöve v0.3.0 +``` + +### 2. Commit and Tag + +```bash +git add FlexLove.lua README.md +git commit -m "v0.3.0 release" +git tag -a v0.3.0 -m "Release version 0.3.0" +git push && git push origin v0.3.0 +``` + +### 3. GitHub Actions Takes Over + +Once you push the tag, the automated workflow handles everything else. + +## Local Release Package (Optional) + +To create a local release package without GitHub Actions: + +```bash +./scripts/create-release.sh +``` + +Output files: +- `releases/flexlove-v{version}.zip` +- `releases/flexlove-v{version}.zip.sha256` + +### Verify Local Package + +```bash +cd releases +shasum -a 256 -c flexlove-v0.3.0.zip.sha256 +# Expected: flexlove-v0.3.0.zip: OK +``` + +## Release Checklist + +- [ ] Version updated in `FlexLove.lua` +- [ ] Documentation regenerated (`./scripts/generate_docs.sh`) +- [ ] Changes committed and pushed +- [ ] Release package created (`./scripts/create-release.sh`) +- [ ] Checksum verified (`shasum -a 256 -c *.sha256`) +- [ ] Release package tested +- [ ] Git tag created and pushed +- [ ] GitHub release published with zip and checksum files + +## Versioning + +FlexLöve follows [Semantic Versioning](https://semver.org/): + +- **MAJOR** version: Incompatible API changes +- **MINOR** version: New functionality (backwards-compatible) +- **PATCH** version: Bug fixes (backwards-compatible) + +Example: `0.2.0` → `0.2.1` (bug fix) or `0.3.0` (new feature) + +## What Gets Released + +The release package includes **only** the files needed to use FlexLöve: + +✅ **Included:** +- `FlexLove.lua` - Main library +- `modules/` - All module files +- `LICENSE` - License terms +- `README.txt` - Installation instructions + +❌ **Not included:** +- `docs/` - Documentation (hosted on GitHub Pages) +- `examples/` - Example code (available in repository) +- `testing/` - Test suite +- `themes/` - Theme examples +- Development tools + +Users who want examples, documentation source, or development tools should clone the full repository. + +## Checksum Verification + +Every release includes a SHA256 checksum file for security verification. + +### For Developers (Creating Release) + +The checksum is automatically generated when running `./scripts/create-release.sh`: + +```bash +./scripts/create-release.sh +# Creates: +# - releases/flexlove-v0.3.0.zip +# - releases/flexlove-v0.3.0.zip.sha256 + +# Verify before publishing +cd releases +shasum -a 256 -c flexlove-v0.3.0.zip.sha256 +# Output: flexlove-v0.3.0.zip: OK +``` + +### For End Users (Downloading Release) + +After downloading a release from GitHub: + +```bash +# Download both files: +# - flexlove-v0.3.0.zip +# - flexlove-v0.3.0.zip.sha256 + +# Verify integrity +shasum -a 256 -c flexlove-v0.3.0.zip.sha256 + +# If OK, safe to use +unzip flexlove-v0.3.0.zip +``` + +**macOS/Linux:** Use `shasum -a 256 -c` +**Windows:** Use `certutil -hashfile flexlove-v0.3.0.zip SHA256` and compare manually + +## Automated Releases (Future) + +Consider adding GitHub Actions workflow to automate: +- Version extraction +- Release package creation +- Documentation deployment +- GitHub release creation + +See `.github/workflows/release.yml` (to be created) diff --git a/docs/.nojekyll b/docs/.nojekyll new file mode 100644 index 0000000..e69de29 diff --git a/docs/README.md b/docs/README.md index 39ec5c1..6fae3f8 100644 --- a/docs/README.md +++ b/docs/README.md @@ -4,23 +4,28 @@ This directory contains auto-generated API documentation from LuaLS annotations. ## Files +- **api.html** - Beautiful, searchable API documentation (2.2MB) - **index.html** - GitHub Pages landing page -- **doc.md** - Markdown API reference (47,000+ lines) -- **doc.json** - JSON API reference for tooling (11MB) +- **build-docs.js** - Node.js script to convert markdown to HTML +- **package.json** - Node.js dependencies for HTML generation +- **.nojekyll** - Tells GitHub Pages to bypass Jekyll processing +- **doc.md** - Raw markdown (gitignored, 960KB) +- **doc.json** - Raw JSON (gitignored, 11MB) ## Regenerating Documentation To regenerate the documentation after making changes: ```bash -./generate_docs.sh +./scripts/generate_docs.sh ``` -Or manually: - -```bash -lua-language-server --doc=. --doc_out_path=./docs -``` +This will: +1. Extract version from `FlexLove.lua` (single source of truth) +2. Generate markdown from LuaLS annotations +3. Convert to beautiful, searchable HTML with syntax highlighting +4. Create navigation sidebar with search functionality +5. Display version in page titles and headers ## Viewing Locally @@ -66,4 +71,66 @@ The documentation is generated from LuaLS (Lua Language Server) annotations usin ## Requirements -- lua-language-server (install via `brew install lua-language-server` on macOS) +- **lua-language-server** - For generating markdown from annotations + - macOS: `brew install lua-language-server` + - Linux: See https://github.com/LuaLS/lua-language-server + +- **Node.js** - For converting markdown to beautiful HTML + - macOS: `brew install node` + - Linux: Use your package manager or https://nodejs.org + +## Features + +The generated HTML documentation includes: +- 🔍 **Live search** - Find classes and methods instantly +- 📱 **Responsive design** - Works on all devices +- 🌙 **Dark theme** - Easy on the eyes +- 🎨 **Syntax highlighting** - Code examples are beautifully formatted +- 🗂️ **Collapsible navigation** - Organized class/method structure +- ⚡ **Fast** - Single-page application, no page reloads +- 🎯 **Filtered** - Only user-facing classes, no internal implementation +- 🏷️ **Versioned** - Auto-displays version from `FlexLove.lua` + +## Customizing Documentation + +Edit `doc-filter.js` to control which classes appear in the documentation: + +```javascript +module.exports = { + // Whitelist mode: Only these classes will be included + include: [ + 'Animation', + 'Color', + 'Element', + 'Theme', + // ... add more + ], + + // Blacklist mode: These classes will be excluded + exclude: [ + 'Context', + 'Performance', + // ... add more + ], + + // Which mode to use + mode: 'whitelist' // or 'blacklist' +}; +``` + +**Current filter:** Whitelist mode with 20 classes (down from 33) + +## Version Management + +The documentation automatically pulls the version from `FlexLove.lua`: + +```lua +flexlove._VERSION = "0.2.0" -- Single source of truth +``` + +To update the version: +1. Change `_VERSION` in `FlexLove.lua` +2. Run `./scripts/generate_docs.sh` +3. Version appears in page titles, sidebar, and footer + +See `VERSIONING.md` for detailed version management workflow. diff --git a/docs/VERSIONING.md b/docs/VERSIONING.md new file mode 100644 index 0000000..026d5ba --- /dev/null +++ b/docs/VERSIONING.md @@ -0,0 +1,80 @@ +# Version Management for FlexLöve Documentation + +The documentation automatically pulls the version from `FlexLove.lua` and displays it throughout the docs. + +## How It Works + +1. **Version Source**: `FlexLove.lua` contains the authoritative version: + ```lua + flexlove._VERSION = "0.2.0" + ``` + +2. **Automatic Detection**: The build script reads this value and injects it into: + - Page title: `FlexLöve v0.2.0 - API Reference` + - Sidebar header: `FlexLöve v0.2.0` + - Landing page: `FlexLöve v0.2.0` + +3. **Single Source of Truth**: Update the version in ONE place (`FlexLove.lua`) and docs auto-update + +## Updating the Version + +### Option 1: Manual Update +Edit `FlexLove.lua`: +```lua +flexlove._VERSION = "0.3.0" -- Change here +``` + +Then regenerate docs: +```bash +./scripts/generate_docs.sh +``` + +### Option 2: Script-Based (Recommended for Releases) +Create a release script that: +1. Updates version in `FlexLove.lua` +2. Regenerates documentation +3. Commits changes +4. Tags release + +Example `release.sh`: +```bash +#!/bin/bash +NEW_VERSION=$1 + +if [ -z "$NEW_VERSION" ]; then + echo "Usage: ./release.sh " + echo "Example: ./release.sh 0.3.0" + exit 1 +fi + +# Update version in FlexLove.lua +sed -i '' "s/flexlove._VERSION = \".*\"/flexlove._VERSION = \"$NEW_VERSION\"/" FlexLove.lua + +# Regenerate docs +./scripts/generate_docs.sh + +# Commit and tag +git add FlexLove.lua docs/ +git commit -m "Release v$NEW_VERSION" +git tag "v$NEW_VERSION" + +echo "✓ Released v$NEW_VERSION" +echo "Don't forget to: git push && git push --tags" +``` + +## Version Display Locations + +- **API Reference** (`api.html`): + - Browser tab title + - Sidebar header (smaller, grayed out) + +- **Landing Page** (`index.html`): + - Footer: "FlexLöve v0.2.0 | MIT License" + +## Future Enhancements + +Consider adding: +- **CHANGELOG.md** - Track changes between versions +- **Version dropdown** - View docs for older versions +- **GitHub Releases link** - Link to release notes +- **Breaking changes banner** - Warn users about API changes diff --git a/docs/WORKFLOW.md b/docs/WORKFLOW.md new file mode 100644 index 0000000..87f64e0 --- /dev/null +++ b/docs/WORKFLOW.md @@ -0,0 +1,153 @@ +# Documentation Workflow + +## Overview + +FlexLöve's documentation system automatically manages versioning and archival. When you generate new documentation, the previous version is automatically archived. + +## How It Works + +### 1. Manual Documentation Updates (No Version Change) + +When you update annotations without bumping the version: + +```bash +./scripts/generate_docs.sh +``` + +**What happens:** +- Script detects current version (e.g., v0.2.0) from `docs/api.html` +- Compares with `FlexLove.lua` version +- If versions match: **Overwrites** `docs/api.html` (same version) +- Previous archived version remains unchanged + +**Use case:** You added better documentation, fixed typos, or improved examples without releasing a new version. + +### 2. Version Bump (New Release) + +When you bump the version in `FlexLove.lua`: + +```bash +# 1. Update version in FlexLove.lua +# flexlove._VERSION = "0.3.0" + +# 2. Generate documentation +./scripts/generate_docs.sh +``` + +**What happens:** +- Script detects old version (v0.2.0) from `docs/api.html` +- Compares with new version (v0.3.0) from `FlexLove.lua` +- **Archives** old `docs/api.html` → `docs/versions/v0.2.0/api.html` +- Generates new `docs/api.html` for v0.3.0 + +### 3. Automated Release (via GitHub Actions) + +When you push a git tag: + +```bash +git tag v0.3.0 +git push origin v0.3.0 +``` + +**What happens:** +1. GitHub Actions workflow triggers +2. Archives previous documentation version +3. Generates new documentation for v0.3.0 +4. Commits both archived and new docs to repository +5. Creates release package with checksums +6. Creates GitHub release with assets + +## Directory Structure + +``` +docs/ +├── api.html # Always the LATEST version +├── index.html # Landing page +└── versions/ + ├── v0.1.0/ + │ └── api.html # Documentation for v0.1.0 + ├── v0.2.0/ + │ └── api.html # Documentation for v0.2.0 + └── v0.3.0/ + └── api.html # Documentation for v0.3.0 +``` + +## Version Detection + +The system automatically detects versions by: +1. **Current docs version**: Reads from `docs/api.html` header (`FlexLöve v0.2.0`) +2. **Code version**: Reads from `FlexLove.lua` (`flexlove._VERSION = "0.2.0"`) + +### Behavior Matrix + +| Old Version | New Version | Action | +|-------------|-------------|--------| +| v0.2.0 | v0.2.0 | Overwrite current (same version update) | +| v0.2.0 | v0.3.0 | Archive v0.2.0, generate v0.3.0 | +| None | v0.2.0 | Generate v0.2.0 (first time) | + +## Examples + +### Scenario 1: Fix Documentation Typo + +```bash +# Fix typo in annotations +# Version still 0.2.0 in FlexLove.lua + +./scripts/generate_docs.sh +# Output: "Same version (v0.2.0), will overwrite current documentation" +# Result: docs/api.html updated, no archival +``` + +### Scenario 2: Release New Version + +```bash +# Update FlexLove.lua +# flexlove._VERSION = "0.3.0" + +./scripts/generate_docs.sh +# Output: "Found previous version v0.2.0, archiving before generating new docs..." +# Output: "✓ Archived previous documentation to docs/versions/v0.2.0/" +# Result: +# - docs/versions/v0.2.0/api.html (archived) +# - docs/api.html (new v0.3.0) +``` + +### Scenario 3: Automated Release + +```bash +# Tag and push +git tag v0.3.0 +git push origin v0.3.0 + +# GitHub Actions will: +# 1. Archive v0.2.0 automatically +# 2. Generate v0.3.0 docs +# 3. Commit both to repository +# 4. Create GitHub release +``` + +## Benefits + +✅ **No manual archival needed** - Automatically handled +✅ **Safe overwrites** - Same version updates won't create duplicate archives +✅ **Version history preserved** - All previous versions accessible +✅ **Seamless workflow** - Just run `./scripts/generate_docs.sh` +✅ **Automated releases** - Tag and forget + +## Version Dropdown + +Users can access any version via the dropdown in the documentation header: +- Current version shows "(Latest)" badge +- Previous versions listed chronologically +- Click to navigate to archived documentation + +## Manual Archival (If Needed) + +If you ever need to manually archive a version: + +```bash +./scripts/archive-docs.sh +``` + +This creates `docs/versions/v{version}/api.html` based on the current `FlexLove.lua` version. diff --git a/docs/api.html b/docs/api.html new file mode 100644 index 0000000..5763170 --- /dev/null +++ b/docs/api.html @@ -0,0 +1,3803 @@ + + + + + + FlexLöve v0.2.1 - API Reference + + + + +
+ + +
+

Animation

+

apply

+
(method) Animation:apply(element: Element)
+
+

duration

+
number
+
+

elapsed

+
number
+
+

fade

+
function Animation.fade(duration: number, fromOpacity: number, toOpacity: number)
+  -> Animation
+
+

Create a simple fade animation

+

final

+
{ width: number, height: number, opacity: number }
+
+

interpolate

+
(method) Animation:interpolate()
+  -> table
+
+

new

+
function Animation.new(props: AnimationProps)
+  -> Animation
+
+

scale

+
function Animation.scale(duration: number, fromScale: table, toScale: table)
+  -> Animation
+
+

Create a simple scale animation

+

@param fromScale — {width:number,height:number}

+

@param toScale — {width:number,height:number}

+

start

+
{ width: number, height: number, opacity: number }
+
+

transform

+
table?
+
+

transition

+
table?
+
+

update

+
(method) Animation:update(dt: number)
+  -> boolean
+
+
+
+

⚠️ Internal Properties

+
+

Warning: The following properties are internal implementation details and should not be accessed directly. They are prefixed with _ to indicate they are private. Accessing these properties may break in future versions without notice.

+
+
+

__index

+
Animation
+
+

_resultDirty

+
boolean
+
+

AnimationProps

+

duration

+
number
+
+

final

+
{ width: number, height: number, opacity: number }
+
+

start

+
{ width: number, height: number, opacity: number }
+
+

transform

+
table?
+
+

transition

+
table?
+
+
+

Color

+

a

+
number
+
+

Alpha component (0-1)

+

b

+
number
+
+

Blue component (0-1)

+

fromHex

+
function Color.fromHex(hexWithTag: string)
+  -> Color
+
+

Convert hex string to color +Supports both 6-digit (#RRGGBB) and 8-digit (#RRGGBBAA) hex formats

+

@param hexWithTag — e.g. “#RRGGBB” or “#RRGGBBAA”

+

g

+
number
+
+

Green component (0-1)

+

isValidColorFormat

+
function Color.isValidColorFormat(value: any)
+  -> format: string?
+
+

Check if a value is a valid color format

+

@param value — Value to check

+

@return format — Format type (hex, rgb, rgba, named, table, nil if invalid)

+

new

+
function Color.new(r?: number, g?: number, b?: number, a?: number)
+  -> Color
+
+

Create a new color instance

+

parse

+
function Color.parse(value: any)
+  -> Color
+
+

Parse a color from various formats

+

@param value — Color value (hex, named, table)

+

@return — Parsed color

+

r

+
number
+
+

Red component (0-1)

+

sanitizeColor

+
function Color.sanitizeColor(value: any, default?: Color)
+  -> Color
+
+

Sanitize a color value

+

@param value — Color value to sanitize

+

@param default — Default color if invalid

+

@return — Sanitized color

+

toRGBA

+
(method) Color:toRGBA()
+  -> r: number
+  2. g: number
+  3. b: number
+  4. a: number
+
+

validateColor

+
function Color.validateColor(value: any, options?: table)
+  -> valid: boolean
+  2. error: string?
+
+

Validate a color value

+

@param value — Color value to validate

+

@param options — Validation options

+

@return valid — True if valid

+

@return error — Error message if invalid

+

validateColorChannel

+
function Color.validateColorChannel(value: any, max?: number)
+  -> valid: boolean
+  2. clamped: number?
+
+

Validate a single color channel value

+

@param value — Value to validate

+

@param max — Maximum value (255 for 0-255 range, 1 for 0-1 range)

+

@return valid — True if valid

+

@return clamped — Clamped value in 0-1 range

+

validateHexColor

+
function Color.validateHexColor(hex: string)
+  -> valid: boolean
+  2. error: string?
+
+

Validate hex color format

+

@param hex — Hex color string (with or without #)

+

@return valid — True if valid format

+

@return error — Error message if invalid

+

validateNamedColor

+
function Color.validateNamedColor(name: string)
+  -> valid: boolean
+  2. error: string?
+
+

Validate named color

+

@param name — Color name

+

@return valid — True if valid

+

@return error — Error message if invalid

+

validateRGBColor

+
function Color.validateRGBColor(r: number, g: number, b: number, a?: number, max?: number)
+  -> valid: boolean
+  2. error: string?
+
+

Validate RGB/RGBA color values

+

@param r — Red component

+

@param g — Green component

+

@param b — Blue component

+

@param a — Alpha component (optional)

+

@param max — Maximum value (255 or 1)

+

@return valid — True if valid

+

@return error — Error message if invalid

+
+
+

⚠️ Internal Properties

+
+

Warning: The following properties are internal implementation details and should not be accessed directly. They are prefixed with _ to indicate they are private. Accessing these properties may break in future versions without notice.

+
+
+

__index

+
Color
+
+

Utility class for color handling

+

Element

+

active

+
boolean?
+
+

Whether the element is active/focused (for inputs, default: false)

+

addChild

+
(method) Element:addChild(child: Element)
+
+

Add child to element

+

alignContent

+
AlignContent
+
+

Alignment of lines in multi-line flex containers (default: STRETCH)

+

alignItems

+
AlignItems
+
+

Alignment of items along cross axis (default: STRETCH)

+

alignSelf

+
AlignSelf
+
+

Alignment of the item itself along cross axis (default: AUTO)

+

animation

+
table?
+
+

Animation instance for this element

+

applyPositioningOffsets

+
(method) Element:applyPositioningOffsets(element: any)
+
+

Apply positioning offsets (top, right, bottom, left) to an element +@param element The element to apply offsets to

+

autoGrow

+
boolean
+
+

Whether element auto-grows with text (default: false)

+

autoScaleText

+
boolean
+
+

Whether text should auto-scale with window size (default: true)

+

autosizing

+
{ width: boolean, height: boolean }
+
+

Whether the element should automatically size to fit its children

+

backdropBlur

+
{ intensity: number, quality: number }?
+
+

Blur content behind the element (intensity: 0-100, quality: 1-10)

+

backgroundColor

+
Color
+
+

Background color of the element

+

blur

+
(method) Element:blur()
+
+

Remove focus from this element

+

border

+
Border
+
+

Border configuration for the element

+

borderColor

+
Color
+
+

Color of the border

+

bottom

+
number?
+
+

Offset from bottom edge (CSS-style positioning)

+

calculateAutoHeight

+
(method) Element:calculateAutoHeight()
+  -> number
+
+

Calculate auto height based on children

+

calculateAutoWidth

+
(method) Element:calculateAutoWidth()
+  -> number
+
+

calculateTextHeight

+
(method) Element:calculateTextHeight()
+  -> number
+
+

calculateTextWidth

+
(method) Element:calculateTextWidth()
+  -> number
+
+

Calculate text width for button

+

children

+
table<integer, Element>
+
+

Children of this element

+

clearChildren

+
(method) Element:clearChildren()
+
+

Remove all children from this element

+

clearSelection

+
(method) Element:clearSelection()
+
+

Clear selection

+

columnGap

+
(string|number)?
+
+

Gap between grid columns

+

contains

+
(method) Element:contains(x: number, y: number)
+  -> boolean
+
+

Check if point is inside element bounds

+

contentAutoSizingMultiplier

+
{ width: number?, height: number? }?
+
+

Multiplier for auto-sized content dimensions

+

contentBlur

+
{ intensity: number, quality: number }?
+
+

Blur the element’s content including children (intensity: 0-100, quality: 1-10)

+

cornerRadius

+
(number|{ topLeft: number?, topRight: number?, bottomLeft: number?, bottomRight: number? })?
+
+

Corner radius for rounded corners (default: 0)

+

cursorBlinkRate

+
number
+
+

Cursor blink rate in seconds (default: 0.5)

+

cursorColor

+
Color?
+
+

Cursor color (default: nil, uses textColor)

+

deleteSelection

+
(method) Element:deleteSelection()
+  -> boolean
+
+

Delete selected text

+

@return — True if text was deleted

+

deleteText

+
(method) Element:deleteText(startPos: number, endPos: number)
+
+

@param startPos — Start position (inclusive)

+

@param endPos — End position (inclusive)

+

destroy

+
(method) Element:destroy()
+
+

Destroy element and its children

+

disableHighlight

+
boolean?
+
+

Whether to disable the pressed state highlight overlay (default: false)

+

disabled

+
boolean?
+
+

Whether the element is disabled (default: false)

+

draw

+
(method) Element:draw(backdropCanvas: any)
+
+

Draw element and its children

+

editable

+
boolean
+
+

Whether the element is editable (default: false)

+

flexDirection

+
FlexDirection
+
+

Direction of flex layout (default: HORIZONTAL)

+

flexWrap

+
FlexWrap
+
+

Whether children wrap to multiple lines (default: NOWRAP)

+

focus

+
(method) Element:focus()
+
+

Focus this element for keyboard input

+

fontFamily

+
string?
+
+

Font family name from theme or path to font file

+

gap

+
string|number
+
+

Space between children elements (default: 10)

+

getAvailableContentHeight

+
(method) Element:getAvailableContentHeight()
+  -> number
+
+

Get available content height for children (accounting for 9-patch content padding) +This is the height that children should use when calculating percentage heights

+

getAvailableContentWidth

+
(method) Element:getAvailableContentWidth()
+  -> number
+
+

Get available content width for children (accounting for 9-patch content padding) +This is the width that children should use when calculating percentage widths

+

getBlurInstance

+
(method) Element:getBlurInstance()
+  -> table?
+
+

Get or create blur instance for this element

+

@return — Blur instance or nil if no blur configured

+

getBorderBoxHeight

+
(method) Element:getBorderBoxHeight()
+  -> number
+
+

Get border-box height (including padding)

+

getBorderBoxWidth

+
(method) Element:getBorderBoxWidth()
+  -> number
+
+

Get border-box width (including padding)

+

getBounds

+
(method) Element:getBounds()
+  -> { x: number, y: number, width: number, height: number }
+
+

Get element bounds (content box)

+

getChildCount

+
(method) Element:getChildCount()
+  -> number
+
+

Get the number of children this element has

+

getContentSize

+
(method) Element:getContentSize()
+  -> contentWidth: number
+  2. contentHeight: number
+
+

Get content dimensions (including overflow) (delegates to ScrollManager)

+

getCursorPosition

+
(method) Element:getCursorPosition()
+  -> number
+
+

Get cursor position

+

@return — Character index (0-based)

+

getMaxScroll

+
(method) Element:getMaxScroll()
+  -> maxScrollX: number
+  2. maxScrollY: number
+
+

Get maximum scroll bounds (delegates to ScrollManager)

+

getScaledContentPadding

+
(method) Element:getScaledContentPadding()
+  -> table|nil
+
+

Get the current state’s scaled content padding +Returns the contentPadding for the current theme state, scaled to the element’s size

+

@return — {left, top, right, bottom} or nil if no contentPadding

+

getScrollPercentage

+
(method) Element:getScrollPercentage()
+  -> percentX: number
+  2. percentY: number
+
+

Get scroll percentage (0-1) (delegates to ScrollManager)

+

getScrollPosition

+
(method) Element:getScrollPosition()
+  -> scrollX: number
+  2. scrollY: number
+
+

Get current scroll position (delegates to ScrollManager)

+

getSelectedText

+
(method) Element:getSelectedText()
+  -> string?
+
+

Get selected text

+

@return — Selected text or nil if no selection

+

getSelection

+
(method) Element:getSelection()
+  -> number?
+  2. number?
+
+

Get selection range

+

@return — Start and end positions, or nil if no selection

+

getText

+
(method) Element:getText()
+  -> string
+
+

Get current text buffer

+

gridColumns

+
number?
+
+

Number of columns in the grid

+

gridRows

+
number?
+
+

Number of rows in the grid

+

hasOverflow

+
(method) Element:hasOverflow()
+  -> hasOverflowX: boolean
+  2. hasOverflowY: boolean
+
+

Check if element has overflow (delegates to ScrollManager)

+

hasSelection

+
(method) Element:hasSelection()
+  -> boolean
+
+

Check if there is an active selection

+

height

+
string|number
+
+

Height of the element

+

hide

+
(method) Element:hide()
+
+

same as calling updateOpacity(0)

+

hideScrollbars

+
(boolean|{ vertical: boolean, horizontal: boolean })?
+
+

Hide scrollbars (boolean for both, or table for individual control)

+

id

+
string
+
+

image

+
(love.Image)?
+
+

Image object to display

+

imageOpacity

+
number?
+
+

Image opacity 0-1 (default: 1, combines with element opacity)

+

imagePath

+
string?
+
+

Path to image file (auto-loads via ImageCache)

+

inputType

+
"email"|"number"|"text"|"url"
+
+

Input type for validation (default: “text”)

+

insertText

+
(method) Element:insertText(text: string, position?: number)
+
+

Insert text at position

+

@param text — Text to insert

+

@param position — Position to insert at (default: cursor position)

+

isFocused

+
(method) Element:isFocused()
+  -> boolean
+
+

Check if this element is focused

+

justifyContent

+
JustifyContent
+
+

Alignment of items along main axis (default: FLEX_START)

+

justifySelf

+
JustifySelf
+
+

Alignment of the item itself along main axis (default: AUTO)

+

keypressed

+
(method) Element:keypressed(key: string, scancode: string, isrepeat: boolean)
+
+

Handle key press (special keys)

+

@param key — Key name

+

@param scancode — Scancode

+

@param isrepeat — Whether this is a key repeat

+

layoutChildren

+
(method) Element:layoutChildren()
+
+

left

+
number?
+
+

Offset from left edge (CSS-style positioning)

+

margin

+
{ top: number, right: number, bottom: number, left: number }
+
+

Margin around children (default: {top=0, right=0, bottom=0, left=0})

+

maxLength

+
number?
+
+

Maximum text length in characters (default: nil)

+

maxLines

+
number?
+
+

Maximum number of lines (default: nil)

+

maxTextSize

+
number?
+
+

minTextSize

+
number?
+
+

moveCursorBy

+
(method) Element:moveCursorBy(delta: number)
+
+

Move cursor by delta characters

+

@param delta — Number of characters to move (positive or negative)

+

moveCursorToEnd

+
(method) Element:moveCursorToEnd()
+
+

Move cursor to end of text

+

moveCursorToLineEnd

+
(method) Element:moveCursorToLineEnd()
+
+

Move cursor to end of current line

+

moveCursorToLineStart

+
(method) Element:moveCursorToLineStart()
+
+

Move cursor to start of current line

+

moveCursorToNextWord

+
(method) Element:moveCursorToNextWord()
+
+

Move cursor to start of next word

+

moveCursorToPreviousWord

+
(method) Element:moveCursorToPreviousWord()
+
+

Move cursor to start of previous word

+

moveCursorToStart

+
(method) Element:moveCursorToStart()
+
+

Move cursor to start of text

+

multiline

+
boolean
+
+

Whether the element supports multiple lines (default: false)

+

new

+
function Element.new(props: ElementProps, deps: table)
+  -> Element
+
+

@param deps — Required dependency table (provided by FlexLove)

+

objectFit

+
("contain"|"cover"|"fill"|"none"|"scale-down")?
+
+

Image fit mode (default: “fill”)

+

objectPosition

+
string?
+
+

Image position like “center center”, “top left”, “50% 50%” (default: “center center”)

+

onBlur

+
fun(element: Element)?
+
+

Callback function when element loses focus

+

onEnter

+
fun(element: Element)?
+
+

Callback function when Enter key is pressed

+

onEvent

+
fun(element: Element, event: InputEvent)?
+
+

Callback function for interaction events

+

onFocus

+
fun(element: Element)?
+
+

Callback function when element receives focus

+

onTextChange

+
fun(element: Element, text: string)?
+
+

Callback function when text changes

+

onTextInput

+
fun(element: Element, text: string)?
+
+

Callback function for text input

+

opacity

+
number
+
+

overflow

+
string?
+
+

Overflow behavior for both axes

+

overflowX

+
string?
+
+

Overflow behavior for horizontal axis

+

overflowY

+
string?
+
+

Overflow behavior for vertical axis

+

padding

+
{ top: number, right: number, bottom: number, left: number }?
+
+

Padding around children (default: {top=0, right=0, bottom=0, left=0})

+

parent

+
Element?
+
+

Parent element (nil if top-level)

+

passwordMode

+
boolean
+
+

Whether to display text as password (default: false)

+

placeholder

+
string?
+
+

Placeholder text when empty (default: nil)

+

positioning

+
Positioning
+
+

Layout positioning mode (default: RELATIVE)

+

prevGameSize

+
{ width: number, height: number }
+
+

Previous game size for resize calculations

+

recalculateUnits

+
(method) Element:recalculateUnits(newViewportWidth: number, newViewportHeight: number)
+
+

removeChild

+
(method) Element:removeChild(child: Element)
+
+

Remove a specific child from this element

+

replaceText

+
(method) Element:replaceText(startPos: number, endPos: number, newText: string)
+
+

Replace text in range

+

@param startPos — Start position (inclusive)

+

@param endPos — End position (inclusive)

+

@param newText — Replacement text

+

resize

+
(method) Element:resize(newGameWidth: number, newGameHeight: number)
+
+

Resize element and its children based on game window size change

+ +
number?
+
+

Offset from right edge (CSS-style positioning)

+

rowGap

+
(string|number)?
+
+

Gap between grid rows

+

scaleCorners

+
number?
+
+

Scale multiplier for 9-patch corners/edges. E.g., 2 = 2x size (overrides theme setting)

+

scalingAlgorithm

+
("bilinear"|"nearest")?
+
+

Scaling algorithm for 9-patch corners: “nearest” (sharp/pixelated) or “bilinear” (smooth) (overrides theme setting)

+

scrollBy

+
(method) Element:scrollBy(dx?: number, dy?: number)
+
+

Scroll by delta amount (delegates to ScrollManager)

+

@param dx — X delta (nil for no change)

+

@param dy — Y delta (nil for no change)

+

scrollSpeed

+
number?
+
+

Scroll speed multiplier

+

scrollToBottom

+
(method) Element:scrollToBottom()
+
+

Scroll to bottom

+

scrollToLeft

+
(method) Element:scrollToLeft()
+
+

Scroll to left

+

scrollToRight

+
(method) Element:scrollToRight()
+
+

Scroll to right

+

scrollToTop

+
(method) Element:scrollToTop()
+
+

Scroll to top

+

scrollable

+
boolean
+
+

Whether text is scrollable (default: false for single-line, true for multi-line)

+

scrollbarColor

+
Color?
+
+

Scrollbar thumb color

+

scrollbarPadding

+
number?
+
+

Scrollbar padding from edges

+

scrollbarRadius

+
number?
+
+

Scrollbar corner radius

+

scrollbarTrackColor

+
Color?
+
+

Scrollbar track color

+

scrollbarWidth

+
number?
+
+

Scrollbar width in pixels

+

selectAll

+
(method) Element:selectAll()
+
+

Select all text

+

selectOnFocus

+
boolean
+
+

Whether to select all text on focus (default: false)

+

selectionColor

+
Color?
+
+

Selection background color (default: nil, uses theme or default)

+

setCursorPosition

+
(method) Element:setCursorPosition(position: number)
+
+

Set cursor position

+

@param position — Character index (0-based)

+

setScrollPosition

+
(method) Element:setScrollPosition(x?: number, y?: number)
+
+

Set scroll position with bounds clamping (delegates to ScrollManager)

+

@param x — X scroll position (nil to keep current)

+

@param y — Y scroll position (nil to keep current)

+

setSelection

+
(method) Element:setSelection(startPos: number, endPos: number)
+
+

Set selection range

+

@param startPos — Start position (inclusive)

+

@param endPos — End position (inclusive)

+

setText

+
(method) Element:setText(text: string)
+
+

Set text buffer and mark dirty

+

show

+
(method) Element:show()
+
+

same as calling updateOpacity(1)

+

text

+
string?
+
+

Text content to display in the element

+

textAlign

+
TextAlign
+
+

Alignment of the text content

+

textColor

+
Color
+
+

Color of the text content

+

textOverflow

+
"clip"|"ellipsis"|"scroll"
+
+

Text overflow behavior (default: “clip”)

+

textSize

+
number?
+
+

Resolved font size for text content in pixels

+

textWrap

+
boolean|"char"|"word"
+
+

Text wrapping mode (default: false for single-line, “word” for multi-line)

+

textinput

+
(method) Element:textinput(text: string)
+
+

Handle text input (character input)

+

@param text — Character(s) to insert

+

theme

+
string?
+
+

Theme component to use for rendering

+

themeComponent

+
string?
+
+

top

+
number?
+
+

Offset from top edge (CSS-style positioning)

+

transform

+
TransformProps
+
+

Transform properties for animations and styling

+

transition

+
TransitionProps
+
+

Transition settings for animations

+

units

+
table
+
+

Original unit specifications for responsive behavior

+

update

+
(method) Element:update(dt: number)
+
+

Update element (propagate to children)

+

updateOpacity

+
(method) Element:updateOpacity(newOpacity: number)
+
+

updateText

+
(method) Element:updateText(newText: string, autoresize?: boolean)
+
+

@param autoresize — default: false

+

userdata

+
table?
+
+

width

+
string|number
+
+

Width of the element

+

x

+
string|number
+
+

X coordinate of the element

+

y

+
string|number
+
+

Y coordinate of the element

+

z

+
number
+
+

Z-index for layering (default: 0)

+
+
+

⚠️ Internal Properties

+
+

Warning: The following properties are internal implementation details and should not be accessed directly. They are prefixed with _ to indicate they are private. Accessing these properties may break in future versions without notice.

+
+
+

__index

+
Element
+
+

_blurInstance

+
table?
+
+

Internal: cached blur effect instance

+

_borderBoxHeight

+
number?
+
+

Internal: cached border-box height

+

_borderBoxWidth

+
number?
+
+

Internal: cached border-box width

+

_calculateScrollbarDimensions

+
(method) Element:_calculateScrollbarDimensions()
+  -> table
+
+

Calculate scrollbar dimensions and positions (delegates to ScrollManager)

+

@return — {vertical: {visible, trackHeight, thumbHeight, thumbY}, horizontal: {visible, trackWidth, thumbWidth, thumbX}}

+

_contentHeight

+
number?
+
+

Internal: total content height

+

_contentWidth

+
number?
+
+

Internal: total content width

+

_cursorBlinkPauseTimer

+
number?
+
+

Internal: timer for how long cursor blink has been paused

+

_cursorBlinkPaused

+
boolean?
+
+

Internal: whether cursor blink is paused (e.g., while typing)

+

_cursorBlinkTimer

+
number?
+
+

Internal: cursor blink timer

+

_cursorColumn

+
number?
+
+

Internal: cursor column within line

+

_cursorLine

+
number?
+
+

Internal: cursor line number (1-based)

+

_cursorPosition

+
number?
+
+

Internal: cursor character position (0-based)

+

_cursorVisible

+
boolean?
+
+

Internal: cursor visibility state

+

_detectOverflow

+
(method) Element:_detectOverflow()
+
+

Detect if content overflows container bounds (delegates to ScrollManager)

+

_eventHandler

+
EventHandler
+
+

Event handler instance for input processing

+

_explicitlyAbsolute

+
boolean?
+
+

_focused

+
boolean?
+
+

Internal: focus state

+

_getFont

+
(method) Element:_getFont()
+  -> love.Font
+
+

_getScrollbarAtPosition

+
(method) Element:_getScrollbarAtPosition(mouseX: number, mouseY: number)
+  -> table|nil
+
+

Get scrollbar at mouse position (delegates to ScrollManager)

+

@return — {component: “vertical”|“horizontal”, region: “thumb”|“track”}

+

_handleScrollbarDrag

+
(method) Element:_handleScrollbarDrag(mouseX: number, mouseY: number)
+  -> boolean
+
+

Handle scrollbar drag (delegates to ScrollManager)

+

@return — True if event was consumed

+

_handleScrollbarPress

+
(method) Element:_handleScrollbarPress(mouseX: number, mouseY: number, button: number)
+  -> boolean
+
+

Handle scrollbar mouse press

+

@return — True if event was consumed

+

_handleScrollbarRelease

+
(method) Element:_handleScrollbarRelease(button: number)
+  -> boolean
+
+

Handle scrollbar release (delegates to ScrollManager)

+

@return — True if event was consumed

+

_handleTextClick

+
(method) Element:_handleTextClick(mouseX: number, mouseY: number, clickCount: number)
+
+

Handle mouse click on text (set cursor position or start selection)

+

@param mouseX — Mouse X coordinate

+

@param mouseY — Mouse Y coordinate

+

@param clickCount — Number of clicks (1=single, 2=double, 3=triple)

+

_handleTextDrag

+
(method) Element:_handleTextDrag(mouseX: number, mouseY: number)
+
+

Handle mouse drag for text selection

+

@param mouseX — Mouse X coordinate

+

@param mouseY — Mouse Y coordinate

+

_handleWheelScroll

+
(method) Element:_handleWheelScroll(x: number, y: number)
+  -> boolean
+
+

Handle mouse wheel scrolling (delegates to ScrollManager)

+

@param x — Horizontal scroll amount

+

@param y — Vertical scroll amount

+

@return — True if scroll was handled

+

_hoveredScrollbar

+
table?
+
+

Internal: currently hovered scrollbar info

+

_layoutEngine

+
LayoutEngine
+
+

Internal: LayoutEngine instance for layout calculations

+

_lines

+
table?
+
+

Internal: split lines for multi-line text

+

_loadedImage

+
(love.Image)?
+
+

Internal: cached loaded image

+

_maxScrollX

+
number?
+
+

Internal: maximum horizontal scroll

+

_maxScrollY

+
number?
+
+

Internal: maximum vertical scroll

+

_mouseDownPosition

+
number?
+
+

Internal: mouse down position for drag tracking

+

_originalPositioning

+
Positioning?
+
+

Original positioning value set by user

+

_overflowX

+
boolean?
+
+

Internal: whether content overflows horizontally

+

_overflowY

+
boolean?
+
+

Internal: whether content overflows vertically

+

_pressed

+
table?
+
+

Internal: button press state tracking

+

_renderer

+
Renderer
+
+

Internal: Renderer instance for visual rendering

+

_scrollManager

+
ScrollManager?
+
+

Internal: ScrollManager instance for scroll handling

+

_scrollX

+
number?
+
+

Internal: horizontal scroll position

+

_scrollY

+
number?
+
+

Internal: vertical scroll position

+

_scrollbarDragOffset

+
number?
+
+

Internal: scrollbar drag offset

+

_scrollbarDragging

+
boolean?
+
+

Internal: scrollbar dragging state

+

_scrollbarHoveredHorizontal

+
boolean?
+
+

Internal: horizontal scrollbar hover state

+

_scrollbarHoveredVertical

+
boolean?
+
+

Internal: vertical scrollbar hover state

+

_scrollbarPressHandled

+
boolean?
+
+

Internal: scrollbar press handled flag

+

_selectionAnchor

+
number?
+
+

Internal: selection anchor point

+

_selectionEnd

+
number?
+
+

Internal: selection end position

+

_selectionStart

+
number?
+
+

Internal: selection start position

+

_stateId

+
string?
+
+

State manager ID for this element

+

_syncScrollManagerState

+
(method) Element:_syncScrollManagerState()
+
+

Sync ScrollManager state to Element properties for backward compatibility +This ensures Renderer and StateManager can access scroll state from Element

+

_textBuffer

+
string?
+
+

Internal: text buffer for editable elements

+

_textDirty

+
boolean?
+
+

Internal: flag to recalculate lines/wrapping

+

_textDragOccurred

+
boolean?
+
+

Internal: whether text drag occurred

+

_textEditor

+
TextEditor?
+
+

Internal: TextEditor instance for editable elements

+

_themeManager

+
ThemeManager
+
+

Internal: theme manager instance

+

_themeState

+
string?
+
+

Current theme state (normal, hover, pressed, active, disabled)

+

_wrapLine

+
(method) Element:_wrapLine(line: string, maxWidth: number)
+  -> table
+
+

Wrap a single line of text

+

@param line — Line to wrap

+

@param maxWidth — Maximum width in pixels

+

@return — Array of wrapped line parts

+

_wrappedLines

+
table?
+
+

Internal: wrapped line data

+

ElementProps

+

active

+
boolean?
+
+

Whether the element is active/focused (for inputs, default: false)

+

alignContent

+
AlignContent?
+
+

Alignment of lines in multi-line flex containers (default: STRETCH)

+

alignItems

+
AlignItems?
+
+

Alignment of items along cross axis (default: STRETCH)

+

alignSelf

+
AlignSelf?
+
+

Alignment of the item itself along cross axis (default: AUTO)

+

autoGrow

+
boolean?
+
+

Whether element auto-grows with text (default: false for single-line, true for multi-line)

+

autoScaleText

+
boolean?
+
+

Whether text should auto-scale with window size (default: true)

+

backdropBlur

+
{ intensity: number, quality: number }?
+
+

Blur content behind the element (intensity: 0-100, quality: 1-10, default: nil)

+

backgroundColor

+
Color?
+
+

Background color (default: transparent)

+

border

+
Border?
+
+

Border configuration for the element

+

borderColor

+
Color?
+
+

Color of the border (default: black)

+

bottom

+
(string|number)?
+
+

Offset from bottom edge (CSS-style positioning)

+

columnGap

+
(string|number)?
+
+

Gap between grid columns (default: 0)

+

contentAutoSizingMultiplier

+
{ width: number?, height: number? }?
+
+

Multiplier for auto-sized content dimensions (default: sourced from theme or {1, 1})

+

contentBlur

+
{ intensity: number, quality: number }?
+
+

Blur the element’s content including children (intensity: 0-100, quality: 1-10, default: nil)

+

cornerRadius

+
(number|{ topLeft: number?, topRight: number?, bottomLeft: number?, bottomRight: number? })?
+
+

Corner radius: number (all corners) or table for individual corners (default: 0)

+

cursorBlinkRate

+
number?
+
+

Cursor blink rate in seconds (default: 0.5)

+

cursorColor

+
Color?
+
+

Cursor color (default: nil, uses textColor)

+

disableHighlight

+
boolean?
+
+

Whether to disable the pressed state highlight overlay (default: false, or true when using themeComponent)

+

disabled

+
boolean?
+
+

Whether the element is disabled (default: false)

+

editable

+
boolean?
+
+

Whether the element is editable (default: false)

+

flexDirection

+
FlexDirection?
+
+

Direction of flex layout: “horizontal”|“vertical” (default: HORIZONTAL)

+

flexWrap

+
FlexWrap?
+
+

Whether children wrap to multiple lines: “nowrap”|“wrap”|“wrap-reverse” (default: NOWRAP)

+

fontFamily

+
string?
+
+

Font family name from theme or path to font file (default: theme default or system default, inherits from parent)

+

gap

+
(string|number)?
+
+

Space between children elements (default: 0)

+

gridColumns

+
number?
+
+

Number of columns in the grid (default: 1)

+

gridRows

+
number?
+
+

Number of rows in the grid (default: 1)

+

height

+
(string|number)?
+
+

Height of the element (default: calculated automatically)

+

hideScrollbars

+
(boolean|{ vertical: boolean, horizontal: boolean })?
+
+

Hide scrollbars (boolean for both, or table for individual control, default: false)

+

id

+
string?
+
+

Unique identifier for the element (auto-generated in immediate mode if not provided)

+

image

+
(love.Image)?
+
+

Image object to display

+

imageOpacity

+
number?
+
+

Image opacity 0-1 (default: 1, combines with element opacity)

+

imagePath

+
string?
+
+

Path to image file (auto-loads via ImageCache)

+

inputType

+
("email"|"number"|"text"|"url")?
+
+

Input type for validation (default: “text”)

+

justifyContent

+
JustifyContent?
+
+

Alignment of items along main axis (default: FLEX_START)

+

justifySelf

+
JustifySelf?
+
+

Alignment of the item itself along main axis (default: AUTO)

+

left

+
(string|number)?
+
+

Offset from left edge (CSS-style positioning)

+

margin

+
{ top: (string|number)?, right: (string|number)?, bottom: (string|number)?, left: (string|number)?, horizontal: (string|number)?, vertical: (string|number)? }?
+
+

Margin around element (default: {top=0, right=0, bottom=0, left=0})

+

maxLength

+
number?
+
+

Maximum text length in characters (default: nil)

+

maxLines

+
number?
+
+

Maximum number of lines (default: nil)

+

maxTextSize

+
number?
+
+

Maximum text size in pixels for auto-scaling

+

minTextSize

+
number?
+
+

Minimum text size in pixels for auto-scaling

+

multiline

+
boolean?
+
+

Whether the element supports multiple lines (default: false)

+

objectFit

+
("contain"|"cover"|"fill"|"none"|"scale-down")?
+
+

Image fit mode (default: “fill”)

+

objectPosition

+
string?
+
+

Image position like “center center”, “top left”, “50% 50%” (default: “center center”)

+

onBlur

+
fun(element: Element, event: InputEvent)?
+
+

Callback when element loses focus

+

onEnter

+
fun(element: Element)?
+
+

Callback when Enter key is pressed

+

onEvent

+
fun(element: Element, event: InputEvent)?
+
+

Callback function for interaction events

+

onFocus

+
fun(element: Element, event: InputEvent)?
+
+

Callback when element receives focus

+

onTextChange

+
fun(element: Element, text: string)?
+
+

Callback when text content changes

+

onTextInput

+
fun(element: Element, text: string)?
+
+

Callback when text is input

+

opacity

+
number?
+
+

Element opacity 0-1 (default: 1)

+

overflow

+
("auto"|"hidden"|"scroll"|"visible")?
+
+

Overflow behavior (default: “hidden”)

+

overflowX

+
("auto"|"hidden"|"scroll"|"visible")?
+
+

X-axis overflow (overrides overflow)

+

overflowY

+
("auto"|"hidden"|"scroll"|"visible")?
+
+

Y-axis overflow (overrides overflow)

+

padding

+
{ top: (string|number)?, right: (string|number)?, bottom: (string|number)?, left: (string|number)?, horizontal: (string|number)?, vertical: (string|number)? }?
+
+

Padding around children (default: {top=0, right=0, bottom=0, left=0})

+

parent

+
Element?
+
+

Parent element for hierarchical structure

+

passwordMode

+
boolean?
+
+

Whether to display text as password (default: false, disables multiline)

+

placeholder

+
string?
+
+

Placeholder text when empty (default: nil)

+

positioning

+
Positioning?
+
+

Layout positioning mode: “absolute”|“relative”|“flex”|“grid” (default: RELATIVE)

+

right

+
(string|number)?
+
+

Offset from right edge (CSS-style positioning)

+

rowGap

+
(string|number)?
+
+

Gap between grid rows (default: 0)

+

scaleCorners

+
number?
+
+

Scale multiplier for 9-patch corners/edges. E.g., 2 = 2x size (overrides theme setting)

+

scalingAlgorithm

+
("bilinear"|"nearest")?
+
+

Scaling algorithm for 9-patch corners: “nearest” (sharp/pixelated) or “bilinear” (smooth) (overrides theme setting)

+

scrollSpeed

+
number?
+
+

Pixels per wheel notch (default: 20)

+

scrollable

+
boolean?
+
+

Whether text is scrollable (default: false for single-line, true for multi-line)

+

scrollbarColor

+
Color?
+
+

Scrollbar thumb color (default: Color.new(0.5, 0.5, 0.5, 0.8))

+

scrollbarPadding

+
number?
+
+

Padding between scrollbar and edge (default: 2)

+

scrollbarRadius

+
number?
+
+

Corner radius for scrollbar (default: 6)

+

scrollbarTrackColor

+
Color?
+
+

Scrollbar track color (default: Color.new(0.2, 0.2, 0.2, 0.5))

+

scrollbarWidth

+
number?
+
+

Width of scrollbar track in pixels (default: 12)

+

selectOnFocus

+
boolean?
+
+

Whether to select all text on focus (default: false)

+

selectionColor

+
Color?
+
+

Selection background color (default: nil, uses theme or default)

+

text

+
string?
+
+

Text content to display (default: nil)

+

textAlign

+
TextAlign?
+
+

Alignment of the text content (default: START)

+

textColor

+
Color?
+
+

Color of the text content (default: black or theme text color)

+

textOverflow

+
("clip"|"ellipsis"|"scroll")?
+
+

Text overflow behavior (default: “clip”)

+

textSize

+
(string|number)?
+
+

Font size: number (px), string with units (“2vh”, “10%”), or preset (“xxs”|“xs”|“sm”|“md”|“lg”|“xl”|“xxl”|“3xl”|“4xl”) (default: “md” or 12px)

+

textWrap

+
(boolean|"char"|"word")?
+
+

Text wrapping mode (default: false for single-line, “word” for multi-line)

+

theme

+
string?
+
+

Theme name to use (e.g., “space”, “metal”). Defaults to theme from flexlove.init()

+

themeComponent

+
string?
+
+

Theme component to use (e.g., “panel”, “button”, “input”). If nil, no theme is applied

+

top

+
(string|number)?
+
+

Offset from top edge (CSS-style positioning)

+

transform

+
TransformProps?
+
+

Transform properties for animations and styling

+

transition

+
TransitionProps?
+
+

Transition settings for animations

+

userdata

+
table?
+
+

User-defined data storage for custom properties

+

width

+
(string|number)?
+
+

Width of the element (default: calculated automatically)

+

x

+
(string|number)?
+
+

X coordinate of the element (default: 0)

+

y

+
(string|number)?
+
+

Y coordinate of the element (default: 0)

+

z

+
number?
+
+

Z-index for layering (default: 0)

+
+
+

⚠️ Internal Properties

+
+

Warning: The following properties are internal implementation details and should not be accessed directly. They are prefixed with _ to indicate they are private. Accessing these properties may break in future versions without notice.

+
+
+

_scrollX

+
number?
+
+

Internal: scroll X position (restored in immediate mode)

+

_scrollY

+
number?
+
+

Internal: scroll Y position (restored in immediate mode)

+

EventHandler

+

getState

+
(method) EventHandler:getState()
+  -> State: table
+
+

Get state for persistence (for immediate mode)

+

@return State — data

+

initialize

+
(method) EventHandler:initialize(element: Element)
+
+

Initialize EventHandler with parent element reference

+

@param element — The parent element

+

isAnyButtonPressed

+
(method) EventHandler:isAnyButtonPressed()
+  -> True: boolean
+
+

Check if any mouse button is pressed

+

@return True — if any button is pressed

+

isButtonPressed

+
(method) EventHandler:isButtonPressed(button: number)
+  -> True: boolean
+
+

Check if a specific button is pressed

+

@param button — Mouse button (1=left, 2=right, 3=middle)

+

@return True — if button is pressed

+

new

+
function EventHandler.new(config: table, deps: table)
+  -> EventHandler
+
+

Create a new EventHandler instance

+

@param config — Configuration options

+

@param deps — Dependencies {InputEvent, Context, utils}

+

onEvent

+
fun(element: Element, event: InputEvent)?
+
+

processMouseEvents

+
(method) EventHandler:processMouseEvents(mx: number, my: number, isHovering: boolean, isActiveElement: boolean)
+
+

Process mouse button events in the update cycle

+

@param mx — Mouse X position

+

@param my — Mouse Y position

+

@param isHovering — Whether mouse is over element

+

@param isActiveElement — Whether this is the top element at mouse position

+

processTouchEvents

+
(method) EventHandler:processTouchEvents()
+
+

Process touch events in the update cycle

+

resetScrollbarPressFlag

+
(method) EventHandler:resetScrollbarPressFlag()
+
+

Reset scrollbar press flag (called each frame)

+

setState

+
(method) EventHandler:setState(state: table)
+
+

Restore state from persistence (for immediate mode)

+

@param state — State data

+
+
+

⚠️ Internal Properties

+
+

Warning: The following properties are internal implementation details and should not be accessed directly. They are prefixed with _ to indicate they are private. Accessing these properties may break in future versions without notice.

+
+
+

_Context

+
table
+
+

_InputEvent

+
table
+
+

__index

+
EventHandler
+
+

_clickCount

+
number
+
+

_dragStartX

+
table<number, number>
+
+

_dragStartY

+
table<number, number>
+
+

_element

+
Element?
+
+

_handleMouseDrag

+
(method) EventHandler:_handleMouseDrag(mx: number, my: number, button: number, isHovering: boolean)
+
+

Handle mouse drag (while button is pressed and mouse moves)

+

@param mx — Mouse X position

+

@param my — Mouse Y position

+

@param button — Mouse button

+

@param isHovering — Whether mouse is over element

+

_handleMousePress

+
(method) EventHandler:_handleMousePress(mx: number, my: number, button: number)
+
+

Handle mouse button press

+

@param mx — Mouse X position

+

@param my — Mouse Y position

+

@param button — Mouse button (1=left, 2=right, 3=middle)

+

_handleMouseRelease

+
(method) EventHandler:_handleMouseRelease(mx: number, my: number, button: number)
+
+

Handle mouse button release

+

@param mx — Mouse X position

+

@param my — Mouse Y position

+

@param button — Mouse button

+

_hovered

+
boolean
+
+

_lastClickButton

+
number?
+
+

_lastClickTime

+
number?
+
+

_lastMouseX

+
table<number, number>
+
+

_lastMouseY

+
table<number, number>
+
+

_pressed

+
table<number, boolean>
+
+

_scrollbarPressHandled

+
boolean
+
+

_touchPressed

+
table<number, boolean>
+
+

_utils

+
table
+
+

FlexLove

+

Animation

+
Animation
+
+

Color

+
Color
+
+

Utility class for color handling

+

Theme

+
Theme
+
+

baseScale

+
table
+
+

beginFrame

+
function FlexLove.beginFrame()
+
+

Begin a new immediate mode frame

+

clearAllStates

+
function FlexLove.clearAllStates()
+
+

Clear all immediate mode states

+

clearState

+
function FlexLove.clearState(id: string)
+
+

Clear state for a specific element ID

+

defaultTheme

+
(string|ThemeDefinition)?
+
+

destroy

+
function FlexLove.destroy()
+
+

draw

+
function FlexLove.draw(gameDrawFunc: function|nil, postDrawFunc: function|nil)
+
+

endFrame

+
function FlexLove.endFrame()
+
+

enums

+
unknown
+
+

getElementAtPosition

+
function FlexLove.getElementAtPosition(x: number, y: number)
+  -> Element?
+
+

Find the topmost element at given coordinates

+

getMode

+
function FlexLove.getMode()
+  -> "immediate"|"retained"
+
+
return #1:
+    | "immediate"
+    | "retained"
+
+

getStateCount

+
function FlexLove.getStateCount()
+  -> number
+
+

getStateStats

+
function FlexLove.getStateStats()
+  -> table
+
+

Get state statistics (for debugging)

+

init

+
function FlexLove.init(config: { baseScale: { width: number?, height: number? }?, theme: (string|ThemeDefinition)?, immediateMode: boolean?, stateRetentionFrames: number?, maxStateEntries: number?, autoFrameManagement: boolean? })
+
+

keypressed

+
function FlexLove.keypressed(key: string, scancode: string, isrepeat: boolean)
+
+

new

+
function FlexLove.new(props: ElementProps)
+  -> Element
+
+

resize

+
function FlexLove.resize()
+
+

scaleFactors

+
table
+
+

setMode

+
function FlexLove.setMode(mode: "immediate"|"retained")
+
+
mode:
+    | "immediate"
+    | "retained"
+
+

textinput

+
function FlexLove.textinput(text: string)
+
+

topElements

+
table
+
+

update

+
function FlexLove.update(dt: any)
+
+

wheelmoved

+
function FlexLove.wheelmoved(dx: any, dy: any)
+
+
+
+

⚠️ Internal Properties

+
+

Warning: The following properties are internal implementation details and should not be accessed directly. They are prefixed with _ to indicate they are private. Accessing these properties may break in future versions without notice.

+
+
+

_DESCRIPTION

+
string
+
+

_LICENSE

+
string
+
+

_URL

+
string
+
+

_VERSION

+
string
+
+

Add version and metadata

+

_activeEventElement

+
Element?
+
+

_autoBeganFrame

+
boolean
+
+

_autoFrameManagement

+
boolean
+
+

_backdropCanvas

+
nil
+
+

A Canvas is used for off-screen rendering. Think of it as an invisible screen that you can draw to, but that will not be visible until you draw it to the actual visible screen. It is also known as “render to texture”.

+

By drawing things that do not change position often (such as background items) to the Canvas, and then drawing the entire Canvas instead of each item, you can reduce the number of draw operations performed each frame.

+

In versions prior to love.graphics.isSupported(“canvas”) could be used to check for support at runtime.

+

Open in Browser

+

_cachedViewport

+
table
+
+

_canvasDimensions

+
table
+
+

_currentFrameElements

+
table
+
+

_focusedElement

+
nil
+
+

_frameNumber

+
integer
+
+

_frameStarted

+
boolean
+
+

_gameCanvas

+
nil
+
+

A Canvas is used for off-screen rendering. Think of it as an invisible screen that you can draw to, but that will not be visible until you draw it to the actual visible screen. It is also known as “render to texture”.

+

By drawing things that do not change position often (such as background items) to the Canvas, and then drawing the entire Canvas instead of each item, you can reduce the number of draw operations performed each frame.

+

In versions prior to love.graphics.isSupported(“canvas”) could be used to check for support at runtime.

+

Open in Browser

+

_immediateMode

+
boolean
+
+

_immediateModeState

+
unknown
+
+

FontFamily

+

path

+
string
+
+

Path to the font file (relative to FlexLove or absolute)

+
+
+

⚠️ Internal Properties

+
+

Warning: The following properties are internal implementation details and should not be accessed directly. They are prefixed with _ to indicate they are private. Accessing these properties may break in future versions without notice.

+
+
+

_loadedFont

+
(love.Font)?
+
+

Internal: cached loaded font

+

InputEvent

+

button

+
number
+
+

Mouse button: 1 (left), 2 (right), 3 (middle)

+

clickCount

+
number
+
+

Number of clicks (for double/triple click detection)

+

dx

+
number?
+
+

Delta X from drag start (only for drag events)

+

dy

+
number?
+
+

Delta Y from drag start (only for drag events)

+

modifiers

+
{ shift: boolean, ctrl: boolean, alt: boolean, super: boolean }
+
+

new

+
function InputEvent.new(props: InputEventProps)
+  -> InputEvent
+
+

Create a new input event

+

timestamp

+
number
+
+

Time when event occurred

+

type

+
"click"|"drag"|"middleclick"|"press"|"release"...(+1)
+
+

x

+
number
+
+

Mouse X position

+

y

+
number
+
+

Mouse Y position

+
+
+

⚠️ Internal Properties

+
+

Warning: The following properties are internal implementation details and should not be accessed directly. They are prefixed with _ to indicate they are private. Accessing these properties may break in future versions without notice.

+
+
+

__index

+
InputEvent
+
+

TextEditor

+

allowNewlines

+
boolean
+
+

allowTabs

+
boolean
+
+

autoGrow

+
boolean
+
+

blur

+
(method) TextEditor:blur()
+
+

Remove focus from this element

+

clearSelection

+
(method) TextEditor:clearSelection()
+
+

Clear selection

+

cursorBlinkRate

+
number
+
+

cursorColor

+
Color?
+
+

Utility class for color handling

+

customSanitizer

+
function?
+
+

deleteSelection

+
(method) TextEditor:deleteSelection()
+  -> boolean
+
+

Delete selected text

+

@return — True if text was deleted

+

deleteText

+
(method) TextEditor:deleteText(startPos: number, endPos: number)
+
+

Delete text in range

+

@param startPos — Start position (inclusive)

+

@param endPos — End position (inclusive)

+

editable

+
boolean
+
+

focus

+
(method) TextEditor:focus()
+
+

Focus this element for keyboard input

+

getCursorPosition

+
(method) TextEditor:getCursorPosition()
+  -> number
+
+

Get cursor position

+

@return — Character index (0-based)

+

getSelectedText

+
(method) TextEditor:getSelectedText()
+  -> string?
+
+

Get selected text

+

@return — Selected text or nil if no selection

+

getSelection

+
(method) TextEditor:getSelection()
+  -> number?
+  2. number?
+
+

Get selection range

+

@return — Start and end positions, or nil if no selection

+

getText

+
(method) TextEditor:getText()
+  -> string
+
+

Get current text buffer

+

handleKeyPress

+
(method) TextEditor:handleKeyPress(key: string, scancode: string, isrepeat: boolean)
+
+

Handle key press (special keys)

+

@param key — Key name

+

@param scancode — Scancode

+

@param isrepeat — Whether this is a key repeat

+

handleTextClick

+
(method) TextEditor:handleTextClick(mouseX: number, mouseY: number, clickCount: number)
+
+

Handle mouse click on text

+

@param clickCount — 1=single, 2=double, 3=triple

+

handleTextDrag

+
(method) TextEditor:handleTextDrag(mouseX: number, mouseY: number)
+
+

Handle mouse drag for text selection

+

handleTextInput

+
(method) TextEditor:handleTextInput(text: string)
+
+

Handle text input (character insertion)

+

hasSelection

+
(method) TextEditor:hasSelection()
+  -> boolean
+
+

Check if there is an active selection

+

initialize

+
(method) TextEditor:initialize(element: table)
+
+

Initialize TextEditor with parent element reference

+

@param element — The parent Element instance

+

inputType

+
"email"|"number"|"text"|"url"
+
+

insertText

+
(method) TextEditor:insertText(text: string, position?: number, skipSanitization?: boolean)
+
+

Insert text at position

+

@param text — Text to insert

+

@param position — Position to insert at (default: cursor position)

+

@param skipSanitization — Skip sanitization (for internal use)

+

isFocused

+
(method) TextEditor:isFocused()
+  -> boolean
+
+

Check if this element is focused

+

maxLength

+
number?
+
+

maxLines

+
number?
+
+

mouseToTextPosition

+
(method) TextEditor:mouseToTextPosition(mouseX: number, mouseY: number)
+  -> number
+
+

Convert mouse coordinates to cursor position in text

+

@param mouseX — Mouse X coordinate (absolute)

+

@param mouseY — Mouse Y coordinate (absolute)

+

@return — Cursor position (character index)

+

moveCursorBy

+
(method) TextEditor:moveCursorBy(delta: number)
+
+

Move cursor by delta characters

+

@param delta — Number of characters to move (positive or negative)

+

moveCursorToEnd

+
(method) TextEditor:moveCursorToEnd()
+
+

Move cursor to end of text

+

moveCursorToLineEnd

+
(method) TextEditor:moveCursorToLineEnd()
+
+

Move cursor to end of current line

+

moveCursorToLineStart

+
(method) TextEditor:moveCursorToLineStart()
+
+

Move cursor to start of current line

+

moveCursorToNextWord

+
(method) TextEditor:moveCursorToNextWord()
+
+

Move cursor to start of next word

+

moveCursorToPreviousWord

+
(method) TextEditor:moveCursorToPreviousWord()
+
+

Move cursor to start of previous word

+

moveCursorToStart

+
(method) TextEditor:moveCursorToStart()
+
+

Move cursor to start of text

+

multiline

+
boolean
+
+

new

+
function TextEditor.new(config: TextEditorConfig, deps: table)
+  -> TextEditor: table
+
+

Create a new TextEditor instance

+

@param deps — Dependencies {Context, StateManager, Color, utils}

+

@return TextEditor — instance

+

onBlur

+
fun(element: Element)?
+
+

onEnter

+
fun(element: Element)?
+
+

onFocus

+
fun(element: Element)?
+
+

onSanitize

+
fun(element: Element, original: string, sanitized: string)?
+
+

onTextChange

+
fun(element: Element, text: string)?
+
+

onTextInput

+
fun(element: Element, text: string)?
+
+

passwordMode

+
boolean
+
+

placeholder

+
string?
+
+

replaceText

+
(method) TextEditor:replaceText(startPos: number, endPos: number, newText: string)
+
+

Replace text in range

+

@param startPos — Start position (inclusive)

+

@param endPos — End position (inclusive)

+

@param newText — Replacement text

+

sanitize

+
boolean
+
+

scrollable

+
boolean
+
+

selectAll

+
(method) TextEditor:selectAll()
+
+

Select all text

+

selectOnFocus

+
boolean
+
+

selectionColor

+
Color?
+
+

Utility class for color handling

+

setCursorPosition

+
(method) TextEditor:setCursorPosition(position: number)
+
+

Set cursor position

+

@param position — Character index (0-based)

+

setSelection

+
(method) TextEditor:setSelection(startPos: number, endPos: number)
+
+

Set selection range

+

@param startPos — Start position (inclusive)

+

@param endPos — End position (inclusive)

+

setText

+
(method) TextEditor:setText(text: string, skipSanitization?: boolean)
+
+

Set text buffer and mark dirty

+

@param skipSanitization — Skip sanitization (for trusted input)

+

textOverflow

+
"clip"|"ellipsis"|"scroll"
+
+

textWrap

+
boolean|"char"|"word"
+
+

update

+
(method) TextEditor:update(dt: number)
+
+

Update cursor blink animation

+

@param dt — Delta time

+

updateAutoGrowHeight

+
(method) TextEditor:updateAutoGrowHeight()
+
+

Update element height based on text content (for autoGrow)

+
+
+

⚠️ Internal Properties

+
+

Warning: The following properties are internal implementation details and should not be accessed directly. They are prefixed with _ to indicate they are private. Accessing these properties may break in future versions without notice.

+
+
+

_Color

+
table
+
+

_Context

+
table
+
+

_FONT_CACHE

+
table
+
+

_StateManager

+
table
+
+

__index

+
TextEditor
+
+

_calculateWrapping

+
(method) TextEditor:_calculateWrapping()
+
+

Calculate text wrapping

+

_cursorBlinkPauseTimer

+
number
+
+

_cursorBlinkPaused

+
boolean
+
+

_cursorBlinkTimer

+
number
+
+

_cursorColumn

+
number
+
+

_cursorLine

+
number
+
+

_cursorPosition

+
number
+
+

_cursorVisible

+
boolean
+
+

_element

+
Element?
+
+

_focused

+
boolean
+
+

_getCursorScreenPosition

+
(method) TextEditor:_getCursorScreenPosition()
+  -> number
+  2. number
+
+

Get cursor screen position for rendering (handles multiline text)

+

@return — Cursor X and Y position relative to content area

+

_getFont

+
(method) TextEditor:_getFont()
+  -> (love.Font)?
+
+

Get font for text rendering

+

_getModifiers

+
function
+
+

_getSelectionRects

+
(method) TextEditor:_getSelectionRects(selStart: number, selEnd: number)
+  -> table
+
+

Get selection rectangles for rendering

+

@param selStart — Selection start position

+

@param selEnd — Selection end position

+

@return — Array of rectangles {x, y, width, height}

+

_lines

+
table?
+
+

_markTextDirty

+
(method) TextEditor:_markTextDirty()
+
+

Mark text as dirty (needs recalculation)

+

_mouseDownPosition

+
number
+
+ +
(method) TextEditor:_resetCursorBlink(pauseBlink: boolean|nil)
+
+

Reset cursor blink (show cursor immediately)

+

@param pauseBlink — Whether to pause blinking (for typing)

+

_sanitizeText

+
(method) TextEditor:_sanitizeText(text: string)
+  -> string
+
+

Internal: Sanitize text input

+

@param text — Text to sanitize

+

@return — Sanitized text

+

_saveState

+
(method) TextEditor:_saveState()
+
+

Save state to StateManager (for immediate mode)

+

_selectWordAtPosition

+
(method) TextEditor:_selectWordAtPosition(position: number)
+
+

Select word at given position

+

_selectionAnchor

+
number?
+
+

_selectionEnd

+
number?
+
+

_selectionStart

+
number?
+
+

_splitLines

+
(method) TextEditor:_splitLines()
+
+

Split text into lines (for multi-line text)

+

_textBuffer

+
string
+
+

_textDirty

+
boolean
+
+

_textDragOccurred

+
boolean?
+
+

_textScrollX

+
number
+
+

_updateTextIfDirty

+
(method) TextEditor:_updateTextIfDirty()
+
+

Update text if dirty (recalculate lines and wrapping)

+

_updateTextScroll

+
(method) TextEditor:_updateTextScroll()
+
+

Update text scroll offset to keep cursor visible

+

_utils

+
table
+
+

_validateCursorPosition

+
(method) TextEditor:_validateCursorPosition()
+
+

Validate cursor position (ensure it’s within text bounds)

+

_wrapLine

+
(method) TextEditor:_wrapLine(line: string, maxWidth: number)
+  -> table
+
+

Wrap a single line of text

+

@param line — Line to wrap

+

@param maxWidth — Maximum width in pixels

+

@return — Array of wrapped line parts

+

_wrappedLines

+
table?
+
+

TextEditorConfig

+

allowNewlines

+
boolean?
+
+

Whether to allow newline characters (default: true in multiline)

+

allowTabs

+
boolean?
+
+

Whether to allow tab characters (default: true)

+

autoGrow

+
boolean
+
+

Whether element auto-grows with text

+

cursorBlinkRate

+
number
+
+

Cursor blink rate in seconds

+

cursorColor

+
Color?
+
+

Cursor color

+

customSanitizer

+
function?
+
+

Custom sanitization function

+

editable

+
boolean
+
+

Whether text is editable

+

inputType

+
"email"|"number"|"text"|"url"
+
+

Input validation type

+

maxLength

+
number?
+
+

Maximum text length in characters

+

maxLines

+
number?
+
+

Maximum number of lines

+

multiline

+
boolean
+
+

Whether multi-line is supported

+

passwordMode

+
boolean
+
+

Whether to mask text

+

placeholder

+
string?
+
+

Placeholder text when empty

+

sanitize

+
boolean?
+
+

Whether to sanitize text input (default: true)

+

scrollable

+
boolean
+
+

Whether text is scrollable

+

selectOnFocus

+
boolean
+
+

Whether to select all text on focus

+

selectionColor

+
Color?
+
+

Selection background color

+

textOverflow

+
"clip"|"ellipsis"|"scroll"
+
+

Text overflow behavior

+

textWrap

+
boolean|"char"|"word"
+
+

Text wrapping mode

+
+

Theme

+

Manager

+
ThemeManager
+
+

Export both Theme and ThemeManager

+

atlas

+
(love.Image)?
+
+

Optional: global atlas

+

atlasData

+
(love.ImageData)?
+
+

Raw (decoded) image data.

+

You can’t draw ImageData directly to screen. See Image for that.

+

Open in Browser

+

colors

+
table<string, Color>
+
+

components

+
table<string, ThemeComponent>
+
+

contentAutoSizingMultiplier

+
{ width: number?, height: number? }?
+
+

Optional: default multiplier for auto-sized content dimensions

+

fonts

+
table<string, string>
+
+

Font family definitions

+

get

+
function Theme.get(themeName: string)
+  -> Theme|nil
+
+

Get a theme by name

+

@param themeName — Name of the theme

+

@return — Returns theme or nil if not found

+

getActive

+
function Theme.getActive()
+  -> Theme?
+
+

Get the active theme

+

getAllColors

+
function Theme.getAllColors()
+  -> table<string, Color>|nil
+
+

Get all colors from the active theme

+

@return — Table of all colors, or nil if no theme active

+

getColor

+
function Theme.getColor(colorName: string)
+  -> Color?
+
+

Get a color from the active theme

+

@param colorName — Name of the color (e.g., “primary”, “secondary”)

+

@return — Returns Color instance or nil if not found

+

getColorNames

+
function Theme.getColorNames()
+  -> table<string>|nil
+
+

Get all available color names from the active theme

+

@return — Array of color names, or nil if no theme active

+

getColorOrDefault

+
function Theme.getColorOrDefault(colorName: string, fallback: Color|nil)
+  -> Color
+
+

Get a color with a fallback if not found

+

@param colorName — Name of the color to retrieve

+

@param fallback — Fallback color if not found (default: white)

+

@return — The color or fallback

+

getComponent

+
function Theme.getComponent(componentName: string, state?: string)
+  -> ThemeComponent?
+
+

Get a component from the active theme

+

@param componentName — Name of the component (e.g., “button”, “panel”)

+

@param state — Optional state (e.g., “hover”, “pressed”, “disabled”)

+

@return — Returns component or nil if not found

+

getFont

+
function Theme.getFont(fontName: string)
+  -> string?
+
+

Get a font from the active theme

+

@param fontName — Name of the font family (e.g., “default”, “heading”)

+

@return — Returns font path or nil if not found

+

getRegisteredThemes

+
function Theme.getRegisteredThemes()
+  -> table<string>
+
+

Get all registered theme names

+

@return — Array of theme names

+

hasActive

+
function Theme.hasActive()
+  -> boolean
+
+

Check if a theme is currently active

+

@return — Returns true if a theme is active

+

load

+
function Theme.load(path: string)
+  -> Theme
+
+

Load a theme from a Lua file

+

@param path — Path to theme definition file (e.g., “space” or “mytheme”)

+

name

+
string
+
+

new

+
function Theme.new(definition: any)
+  -> Theme
+
+

sanitizeTheme

+
function Theme.sanitizeTheme(theme?: table)
+  -> sanitized: table
+
+

Sanitize a theme definition by removing invalid values and providing defaults

+

@param theme — The theme to sanitize

+

@return sanitized — The sanitized theme

+

setActive

+
function Theme.setActive(themeOrName: string|Theme)
+
+

validateTheme

+
function Theme.validateTheme(theme?: table, options?: table)
+  -> valid: boolean
+  2. errors: table
+
+

Validate a theme definition for structural correctness (non-aggressive)

+

@param theme — The theme to validate

+

@param options — Optional validation options {strict: boolean}

+

@return valid,errors — List of validation errors

+
+
+

⚠️ Internal Properties

+
+

Warning: The following properties are internal implementation details and should not be accessed directly. They are prefixed with _ to indicate they are private. Accessing these properties may break in future versions without notice.

+
+
+

__index

+
Theme
+
+

ThemeComponent

+

atlas

+
(string|love.Image)?
+
+

Optional: component-specific atlas (overrides theme atlas). Files ending in .9.png are auto-parsed

+

contentAutoSizingMultiplier

+
{ width: number?, height: number? }?
+
+

Optional: multiplier for auto-sized content dimensions

+

insets

+
{ left: number, top: number, right: number, bottom: number }?
+
+

Optional: 9-patch insets (auto-extracted from .9.png files or manually defined)

+

regions

+
{ topLeft: ThemeRegion, topCenter: ThemeRegion, topRight: ThemeRegion, middleLeft: ThemeRegion, middleCenter: ThemeRegion, middleRight: ThemeRegion, bottomLeft: ThemeRegion, bottomCenter: ThemeRegion, bottomRight: ThemeRegion }
+
+

scaleCorners

+
number?
+
+

Optional: scale multiplier for non-stretched regions (corners/edges). E.g., 2 = 2x size. Default: nil (no scaling)

+

scalingAlgorithm

+
("bilinear"|"nearest")?
+
+

Optional: scaling algorithm for non-stretched regions. Default: “bilinear”

+

states

+
table<string, ThemeComponent>?
+
+

stretch

+
{ horizontal: table<integer, string>, vertical: table<integer, string> }
+
+
+
+

⚠️ Internal Properties

+
+

Warning: The following properties are internal implementation details and should not be accessed directly. They are prefixed with _ to indicate they are private. Accessing these properties may break in future versions without notice.

+
+
+

_loadedAtlas

+
(string|love.Image)?
+
+

Internal: cached loaded atlas image

+

_loadedAtlasData

+
(love.ImageData)?
+
+

Internal: cached loaded atlas ImageData for pixel access

+

_ninePatchData

+
{ insets: table, contentPadding: table, stretchX: table, stretchY: table }?
+
+

Internal: parsed 9-patch data with stretch regions and content padding

+

_scaledRegionCache

+
table<string, love.Image>?
+
+

Internal: cache for scaled corner/edge images

+

ThemeDefinition

+

atlas

+
(string|love.Image)?
+
+

Optional: global atlas (can be overridden per component)

+

colors

+
table<string, Color>?
+
+

components

+
table<string, ThemeComponent>
+
+

contentAutoSizingMultiplier

+
{ width: number?, height: number? }?
+
+

Optional: default multiplier for auto-sized content dimensions

+

fonts

+
table<string, string>?
+
+

Optional: font family definitions (name -> path)

+

name

+
string
+
+
+

TransformProps

+

rotate

+
number?
+
+

scale

+
{ x: number, y: number }?
+
+

skew

+
{ x: number, y: number }?
+
+

translate

+
{ x: number, y: number }?
+
+
+

TransitionProps

+

duration

+
number?
+
+

easing

+
string?
+
+
+ +
+
+ + ↑ Top + + + + \ No newline at end of file diff --git a/docs/build-docs.js b/docs/build-docs.js new file mode 100644 index 0000000..bff4f51 --- /dev/null +++ b/docs/build-docs.js @@ -0,0 +1,683 @@ +const fs = require('fs'); +const path = require('path'); +const MarkdownIt = require('markdown-it'); +const anchor = require('markdown-it-anchor'); +const hljs = require('highlight.js'); +const filter = require('./doc-filter'); + +// Extract version from FlexLove.lua +function getVersion() { + try { + const flexlovePath = path.join(__dirname, '..', 'FlexLove.lua'); + const content = fs.readFileSync(flexlovePath, 'utf8'); + const match = content.match(/flexlove\._VERSION\s*=\s*["']([^"']+)["']/); + return match ? match[1] : 'unknown'; + } catch (e) { + return 'unknown'; + } +} + +const VERSION = getVersion(); +console.log(`Building docs for FlexLöve v${VERSION}`); + +const md = new MarkdownIt({ + html: true, + linkify: true, + typographer: true, + highlight: function (str, lang) { + if (lang && hljs.getLanguage(lang)) { + try { + return hljs.highlight(str, { language: lang }).value; + } catch (__) {} + } + return ''; + } +}).use(anchor, { + permalink: anchor.permalink.headerLink() +}); + +// Read the markdown file +let markdownContent = fs.readFileSync(path.join(__dirname, 'doc.md'), 'utf8'); + +// Filter content based on doc-filter.js configuration +function filterMarkdown(content) { + const lines = content.split('\n'); + const filtered = []; + let currentClass = null; + let skipUntilNextClass = false; + let classContent = []; + + for (let i = 0; i < lines.length; i++) { + const line = lines[i]; + const h1Match = line.match(/^# (.+)$/); + + if (h1Match) { + // New class found - decide if we should keep previous class + if (currentClass && !skipUntilNextClass) { + filtered.push(...classContent); + } + + currentClass = h1Match[1]; + classContent = [line]; + + // Check if this class should be included + if (filter.mode === 'whitelist') { + skipUntilNextClass = !filter.include.includes(currentClass); + } else { + skipUntilNextClass = filter.exclude.includes(currentClass); + } + } else { + classContent.push(line); + } + } + + // Don't forget the last class + if (currentClass && !skipUntilNextClass) { + filtered.push(...classContent); + } + + return filtered.join('\n'); +} + +markdownContent = filterMarkdown(markdownContent); + +// Sort properties: public first, then internal (prefixed with _) +function sortAndInjectWarning(content) { + const lines = content.split('\n'); + const result = []; + let i = 0; + + while (i < lines.length) { + const line = lines[i]; + + // Check if this is a class heading (h1) + if (line.match(/^# .+$/)) { + // Found a class, collect all its properties/methods + result.push(line); + i++; + + const properties = []; + let currentProperty = null; + + // Collect all properties until next class or end of file + while (i < lines.length && !lines[i].match(/^# .+$/)) { + const propLine = lines[i]; + const h2Match = propLine.match(/^## (.+)$/); + + if (h2Match) { + // Save previous property if exists + if (currentProperty) { + properties.push(currentProperty); + } + // Start new property + currentProperty = { + name: h2Match[1], + lines: [propLine], + isInternal: h2Match[1].startsWith('_') + }; + } else if (currentProperty) { + // Add line to current property + currentProperty.lines.push(propLine); + } else { + // Line before any property (e.g., class description) + result.push(propLine); + } + i++; + } + + // Save last property + if (currentProperty) { + properties.push(currentProperty); + } + + // Sort: public first, then internal + const publicProps = properties.filter(p => !p.isInternal); + const internalProps = properties.filter(p => p.isInternal); + + // Add public properties + publicProps.forEach(prop => { + result.push(...prop.lines); + }); + + // Add warning and internal properties if any exist + if (internalProps.length > 0) { + result.push(''); + result.push('---'); + result.push(''); + result.push('## ⚠️ Internal Properties'); + result.push(''); + result.push('> **Warning:** The following properties are internal implementation details and should not be accessed directly. They are prefixed with `_` to indicate they are private. Accessing these properties may break in future versions without notice.'); + result.push(''); + result.push('---'); + result.push(''); + + internalProps.forEach(prop => { + result.push(...prop.lines); + }); + } + } else { + result.push(line); + i++; + } + } + + return result.join('\n'); +} + +markdownContent = sortAndInjectWarning(markdownContent); + +// Parse markdown structure to build navigation +const lines = markdownContent.split('\n'); +const navigation = []; +let currentClass = null; + +lines.forEach(line => { + const h1Match = line.match(/^# (.+)$/); + const h2Match = line.match(/^## (.+)$/); + + if (h1Match) { + currentClass = { + name: h1Match[1], + id: h1Match[1].toLowerCase().replace(/[^a-z0-9]+/g, '-'), + members: [] + }; + navigation.push(currentClass); + } else if (h2Match && currentClass) { + currentClass.members.push({ + name: h2Match[1], + id: h2Match[1].toLowerCase().replace(/[^a-z0-9]+/g, '-') + }); + } +}); + +// Scan for available documentation versions +function getAvailableVersions() { + const versionsDir = path.join(__dirname, 'versions'); + const versions = []; + + try { + if (fs.existsSync(versionsDir)) { + const entries = fs.readdirSync(versionsDir, { withFileTypes: true }); + for (const entry of entries) { + if (entry.isDirectory() && entry.name.startsWith('v')) { + const apiPath = path.join(versionsDir, entry.name, 'api.html'); + if (fs.existsSync(apiPath)) { + versions.push(entry.name); + } + } + } + } + } catch (e) { + console.warn('Warning: Could not scan versions directory:', e.message); + } + + // Sort versions (newest first) + versions.sort((a, b) => { + const parseVersion = (v) => { + const parts = v.substring(1).split('.').map(Number); + return parts[0] * 10000 + parts[1] * 100 + parts[2]; + }; + return parseVersion(b) - parseVersion(a); + }); + + return versions; +} + +const availableVersions = getAvailableVersions(); +console.log(`Found ${availableVersions.length} archived version(s):`, availableVersions.join(', ')); + +// Convert markdown to HTML +const htmlContent = md.render(markdownContent); + +// Create HTML template +const template = ` + + + + + FlexLöve v${VERSION} - API Reference + + + + +
+ + +
+${htmlContent} +
+
+ + ↑ Top + + + +`; + +// Write the HTML file +fs.writeFileSync(path.join(__dirname, 'api.html'), template, 'utf8'); +console.log('✓ Generated api.html'); diff --git a/docs/doc-filter.js b/docs/doc-filter.js new file mode 100644 index 0000000..4208fa6 --- /dev/null +++ b/docs/doc-filter.js @@ -0,0 +1,47 @@ +// Classes to INCLUDE in documentation (whitelist approach) +// Only these classes and their related types will appear in the docs +module.exports = { + // Main user-facing classes + include: [ + "Animation", + "AnimationProps", + //"Border", + "Color", + "Element", + "ElementProps", + "EventHandler", + "FlexLove", + "FontFamily", + "InputEvent", + //"InputEventProps", + //"LayoutEngine", + //"LayoutEngineProps", + "Theme", + "ThemeComponent", + "ThemeDefinition", + "TextEditor", + "TextEditorConfig", + "TransformProps", + "TransitionProps", + ], + + // Alternative: exclude specific classes (blacklist) + exclude: [ + "Context", + "Performance", + "Trace", + "Proto", + "LuaLS", + "ImageCache", + "ImageRenderer", + "Renderer", + "StateManager", + "ScrollManager", + "ErrorCodes", + "ThemeManager", + "ThemeRegion", + ], + + // Which mode to use: 'whitelist' or 'blacklist' + mode: "whitelist", +}; diff --git a/docs/doc.json b/docs/doc.json index 499587c..b91efc2 100644 --- a/docs/doc.json +++ b/docs/doc.json @@ -430,10 +430,10 @@ 16 ], "type": "doc.type.table", - "view": "{ width: number?, height: number?, opacity: number? }" + "view": "{ width: number, height: number, opacity: number }" } ], - "view": "{ width: number?, height: number?, opacity: number? }" + "view": "{ width: number, height: number, opacity: number }" }, "file": "modules/Animation.lua", "finish": [ @@ -446,7 +446,7 @@ 10 ], "type": "doc.field", - "view": "{ width: number?, height: number?, opacity: number? }", + "view": "{ width: number, height: number, opacity: number }", "visible": "public" }, { @@ -736,10 +736,10 @@ 16 ], "type": "doc.type.table", - "view": "{ width: number?, height: number?, opacity: number? }" + "view": "{ width: number, height: number, opacity: number }" } ], - "view": "{ width: number?, height: number?, opacity: number? }" + "view": "{ width: number, height: number, opacity: number }" }, "file": "modules/Animation.lua", "finish": [ @@ -752,7 +752,7 @@ 10 ], "type": "doc.field", - "view": "{ width: number?, height: number?, opacity: number? }", + "view": "{ width: number, height: number, opacity: number }", "visible": "public" }, { diff --git a/docs/doc.md b/docs/doc.md index 378991e..ecc4b60 100644 --- a/docs/doc.md +++ b/docs/doc.md @@ -49,7 +49,7 @@ function Animation.fade(duration: number, fromOpacity: number, toOpacity: number ```lua -{ width: number?, height: number?, opacity: number? } +{ width: number, height: number, opacity: number } ``` ## interpolate @@ -86,7 +86,7 @@ function Animation.scale(duration: number, fromScale: table, toScale: table) ```lua -{ width: number?, height: number?, opacity: number? } +{ width: number, height: number, opacity: number } ``` ## transform diff --git a/docs/examples.html b/docs/examples.html new file mode 100644 index 0000000..3a0aa96 --- /dev/null +++ b/docs/examples.html @@ -0,0 +1,1075 @@ + + + + + + FlexLöve Examples + + + + + + + +
+

FlexLöve Examples

+ +
+ + + +
+
+

🎨 Flexbox & Grid Layouts

+

+ FlexLöve provides a powerful CSS-like layout system using flexbox and + grid positioning. Build complex responsive UIs with nested containers + and automatic layout calculations. +

+ +

Basic Flex Layout

+
-- Create a main window with vertical flex layout
+local window = FlexLove.new({
+  x = "10%",
+  y = "10%",
+  width = "80%",
+  height = "80%",
+  themeComponent = "framev3",
+  positioning = "flex",
+  flexDirection = "vertical",
+  gap = 20,
+  padding = { horizontal = 20, vertical = 20 }
+})
+
+-- Add a title
+FlexLove.new({
+  parent = window,
+  text = "Advanced Layout Example",
+  textAlign = "center",
+  textSize = "3xl",
+  width = "100%"
+})
+ +

Grid Layout with Wrapping

+
-- Create a grid container
+local gridContainer = FlexLove.new({
+  parent = window,
+  positioning = "flex",
+  flexDirection = "horizontal",
+  flexWrap = "wrap",
+  justifyContent = "space-between",
+  alignItems = "flex-start",
+  gap = 10,
+  height = "80%"
+})
+
+-- Add grid items (will automatically wrap)
+for i = 1, 6 do
+  FlexLove.new({
+    parent = gridContainer,
+    width = "45%",
+    height = "40%",
+    themeComponent = "buttonv2",
+    text = "Item " .. i,
+    textAlign = "center",
+    textSize = "md",
+    onEvent = function(_, event)
+      if event.type == "release" then
+        print("Grid item " .. i .. " clicked")
+      end
+    end
+  })
+end
+ +

Nested Flex Containers

+
-- Horizontal container with space-around
+local nestedFlex = FlexLove.new({
+  parent = window,
+  positioning = "flex",
+  flexDirection = "horizontal",
+  justifyContent = "space-around",
+  alignItems = "center",
+  gap = 10,
+  height = "40%"
+})
+
+-- Add buttons that will be spaced evenly
+FlexLove.new({
+  parent = nestedFlex,
+  text = "Button A",
+  themeComponent = "buttonv1",
+  width = "25%",
+  textAlign = "center"
+})
+
+FlexLove.new({
+  parent = nestedFlex,
+  text = "Button B",
+  themeComponent = "buttonv2",
+  width = "25%",
+  textAlign = "center"
+})
+ +
+

+ 💡 Tip: Use flexDirection to control + layout direction (horizontal/vertical), + justifyContent for main axis alignment, and + alignItems for cross axis alignment. +

+
+ + View Complete Example → +
+ +
+

🎭 Theme System

+

+ FlexLöve includes a powerful 9-patch NinePatch theming system with + automatic state management (normal, hover, pressed, disabled). +

+ +

Initialize with Theme

+
local FlexLove = require("FlexLove")
+
+-- Initialize with a built-in theme
+FlexLove.init({
+  baseScale = { width = 1920, height = 1080 },
+  theme = "space"  -- Options: "space", "metal"
+})
+ +

Using Theme Components

+
-- Create a themed button (automatic state handling)
+local button = FlexLove.new({
+  themeComponent = "buttonv2",
+  text = "Themed Button",
+  width = "20vw",
+  height = "10vh",
+  textAlign = "center",
+  onEvent = function(elem, event)
+    if event.type == "release" then
+      print("Button clicked!")
+    end
+  end
+})
+
+-- Available theme components:
+-- "buttonv1", "buttonv2", "buttonv3"
+-- "framev1", "framev2", "framev3"
+-- "inputv1", "inputv2"
+-- "cardv1", "cardv2"
+-- "panel"
+ +

Theme Switching

+
local themes = { "space", "metal" }
+local themeIndex = 1
+
+-- Create theme toggle button
+FlexLove.new({
+  themeComponent = "buttonv2",
+  text = "Switch Theme",
+  onEvent = function(_, event)
+    if event.type == "release" then
+      themeIndex = (themeIndex % #themes) + 1
+      FlexLove.setTheme(themes[themeIndex])
+      print("Switched to:", themes[themeIndex])
+    end
+  end
+})
+ +
+

+ 💡 Tip: Theme components automatically handle + hover, pressed, and disabled states using 9-patch image scaling for + perfect corners at any size. +

+
+ + View Complete Example → +
+ +
+

✨ State Management

+

+ Build interactive UIs with state tracking, counters, toggles, and + dynamic updates. Perfect for game menus and settings screens. +

+ +

Counter with State

+
local state = {
+  counter = 0,
+  isToggled = false,
+  inputValue = ""
+}
+
+-- Create counter display
+local counterSection = FlexLove.new({
+  positioning = "flex",
+  flexDirection = "horizontal",
+  justifyContent = "space-between",
+  alignItems = "center",
+  gap = 10
+})
+
+local counterText = FlexLove.new({
+  parent = counterSection,
+  text = "Counter: " .. state.counter,
+  textSize = "lg"
+})
+
+-- Increment button
+FlexLove.new({
+  parent = counterSection,
+  themeComponent = "buttonv2",
+  text = "Increment",
+  width = "25%",
+  onEvent = function(_, event)
+    if event.type == "release" then
+      state.counter = state.counter + 1
+      counterText.text = "Counter: " .. state.counter
+      print("Counter incremented to:", state.counter)
+    end
+  end
+})
+ +

Toggle Switch

+
-- Create toggle button with color change
+local toggleButton = FlexLove.new({
+  positioning = "flex",
+  justifyContent = "center",
+  alignItems = "center",
+  width = 60,
+  height = 30,
+  backgroundColor = state.isToggled and "#48bb78" or "#a0aec0",
+  borderRadius = 15,
+  padding = { horizontal = 5 },
+  onEvent = function(elem, event)
+    if event.type == "release" then
+      state.isToggled = not state.isToggled
+      -- Update button color
+      elem.backgroundColor = state.isToggled and "#48bb78" or "#a0aec0"
+      print("Toggle switched to:", state.isToggled)
+    end
+  end
+})
+
+FlexLove.new({
+  parent = toggleButton,
+  text = state.isToggled and "ON" or "OFF",
+  textAlign = "center",
+  textSize = "sm",
+  color = "#ffffff"
+})
+ +

Text Input with State

+
-- Input field that updates state
+local inputField = FlexLove.new({
+  themeComponent = "inputv2",
+  text = state.inputValue,
+  textAlign = "left",
+  textSize = "md",
+  width = "50%",
+  onEvent = function(_, event)
+    if event.type == "textinput" then
+      state.inputValue = state.inputValue .. event.text
+      print("Input value:", state.inputValue)
+    elseif event.type == "keypressed" and event.key == "backspace" then
+      state.inputValue = state.inputValue:sub(1, -2)
+    end
+  end
+})
+ +
+

+ 💡 Tip: Use local state tables to manage UI state + and update element properties directly when state changes. +

+
+ + View Complete Example → +
+ +
+

📜 Scrollable Content

+

+ Create smooth scrolling containers with backdrop blur effects and + overflow handling for long content lists. +

+ +

Scrollable Container

+
local Color = FlexLove.Color
+
+-- Create main window with backdrop blur
+local window = FlexLove.new({
+  x = "25%",
+  y = "10%",
+  width = "50vw",
+  height = "80vh",
+  themeComponent = "framev3",
+  positioning = "flex",
+  flexDirection = "vertical",
+  gap = 10,
+  backdropBlur = { intensity = 50, quality = 10 },
+  backgroundColor = Color.new(0.1, 0.1, 0.1, 0.8)
+})
+
+-- Create scroll container
+local scrollContainer = FlexLove.new({
+  parent = window,
+  width = "90%",
+  height = "70%",
+  positioning = "flex",
+  flexDirection = "vertical",
+  overflowY = "scroll",  -- Enable vertical scrolling
+  gap = 5,
+  padding = { horizontal = 10, vertical = 5 },
+  themeComponent = "framev3",
+  backgroundColor = Color.new(0.2, 0.2, 0.2, 0.5)
+})
+ +

Add Scrollable Items

+
-- Add multiple items to demonstrate scrolling
+for i = 1, 30 do
+  local text = string.format(
+    "Item %d - This is scrollable content that exceeds the container bounds",
+    i
+  )
+
+  FlexLove.new({
+    parent = scrollContainer,
+    text = text,
+    textAlign = "start",
+    textSize = "md",
+    width = "100%",
+    textColor = Color.new(0.9, 0.9, 0.9, 1),
+    padding = { vertical = 5 },
+    themeComponent = i % 3 == 0 and "panel" or "cardv2",
+    backgroundColor = i % 3 == 0
+      and Color.new(0.3, 0.3, 0.3, 0.7)
+      or Color.new(0.4, 0.4, 0.4, 0.5)
+  })
+end
+
+-- Footer with instructions
+FlexLove.new({
+  parent = window,
+  text = "Scroll using the mouse wheel or drag the scrollbar",
+  textAlign = "center",
+  textSize = "sm",
+  textColor = Color.new(0.7, 0.7, 0.7, 1)
+})
+ +
+

+ 💡 Tip: Use overflowY = "scroll" to + enable vertical scrolling. The backdrop blur creates a nice + glassmorphism effect behind the container. +

+
+ + View Complete Example → +
+ +
+

🎚️ Sliders & Controls

+

+ Implement draggable sliders with value tracking, perfect for settings + menus and adjustable parameters. +

+ +

Create a Slider Control

+
local function createSlider(parent, label, min, max, initialValue)
+  local value = initialValue or min
+  local normalized = (value - min) / (max - min)
+
+  local row = FlexLove.new({
+    parent = parent,
+    width = "100%",
+    height = "5vh",
+    positioning = "flex",
+    flexDirection = "horizontal",
+    justifyContent = "space-between",
+    alignItems = "center",
+    gap = 10
+  })
+
+  -- Label
+  FlexLove.new({
+    parent = row,
+    text = label,
+    textAlign = "start",
+    textSize = "md",
+    width = "30%"
+  })
+
+  local sliderContainer = FlexLove.new({
+    parent = row,
+    width = "50%",
+    height = "100%",
+    positioning = "flex",
+    flexDirection = "horizontal",
+    alignItems = "center",
+    gap = 5
+  })
+
+  -- Slider track
+  local sliderTrack = FlexLove.new({
+    parent = sliderContainer,
+    width = "80%",
+    height = "75%",
+    positioning = "flex",
+    flexDirection = "horizontal",
+    themeComponent = "framev3"
+  })
+
+  -- Fill bar (visual indicator)
+  local fillBar = FlexLove.new({
+    parent = sliderTrack,
+    width = (normalized * 100) .. "%",
+    height = "100%",
+    themeComponent = "buttonv1"
+  })
+
+  -- Value display
+  local valueDisplay = FlexLove.new({
+    parent = sliderContainer,
+    text = string.format("%d", value),
+    textAlign = "center",
+    textSize = "md",
+    width = "15%"
+  })
+
+  -- Handle drag events
+  local function updateValue(mx)
+    local normalized = (mx - sliderTrack.x) / sliderTrack.width
+    normalized = math.max(0, math.min(1, normalized))
+    value = min + (normalized * (max - min))
+
+    -- Update visuals
+    fillBar.width = (normalized * 100) .. "%"
+    valueDisplay.text = string.format("%d", value)
+  end
+
+  sliderTrack.onEvent = function(elem, event)
+    if event.type == "press" or event.type == "drag" then
+      updateValue(event.x)
+    end
+  end
+
+  return value
+end
+ +

Using Multiple Sliders

+
-- Create settings window
+local settingsWindow = FlexLove.new({
+  x = "10%",
+  y = "10%",
+  width = "80%",
+  height = "80%",
+  themeComponent = "framev3",
+  positioning = "flex",
+  flexDirection = "vertical",
+  gap = 20,
+  padding = { horizontal = "5%", vertical = "3%" }
+})
+
+-- Title
+FlexLove.new({
+  parent = settingsWindow,
+  text = "Settings",
+  textAlign = "center",
+  textSize = "3xl"
+})
+
+-- Create sliders for different settings
+createSlider(settingsWindow, "Volume", 0, 100, 75)
+createSlider(settingsWindow, "Brightness", 0, 100, 50)
+createSlider(settingsWindow, "Sensitivity", 10, 200, 100)
+ +
+

+ 💡 Tip: Use event.type == "drag" to + track mouse movement while pressed for smooth slider interaction. +

+
+ + View Complete Example → +
+ +
+

⌨️ Input & Event Handling

+

+ Handle mouse, keyboard, and touch events with FlexLöve's comprehensive + event system. Includes focus management and input field support. +

+ +

Mouse Event Handling

+
local inputState = {
+  mousePos = { x = 0, y = 0 },
+  isHovered = false,
+  hoverCount = 0
+}
+
+-- Create hoverable area
+local hoverArea = FlexLove.new({
+  width = "30%",
+  height = "100%",
+  backgroundColor = "#4a5568",
+  borderRadius = 8,
+  onEvent = function(elem, event)
+    if event.type == "mousemoved" then
+      inputState.mousePos.x = event.x
+      inputState.mousePos.y = event.y
+    elseif event.type == "mouseenter" then
+      inputState.isHovered = true
+      inputState.hoverCount = inputState.hoverCount + 1
+      elem.backgroundColor = "#48bb78"  -- Green on hover
+    elseif event.type == "mouseleave" then
+      inputState.isHovered = false
+      elem.backgroundColor = "#4a5568"  -- Back to gray
+    elseif event.type == "release" then
+      print("Clicked at:", event.x, event.y)
+    end
+  end
+})
+
+-- Display mouse position
+FlexLove.new({
+  text = "Mouse: (" .. inputState.mousePos.x .. ", " .. inputState.mousePos.y .. ")",
+  textAlign = "left"
+})
+ +

Keyboard Input

+
local keyState = {
+  lastKey = "",
+  textInput = ""
+}
+
+-- Input field with keyboard handling
+local inputField = FlexLove.new({
+  themeComponent = "inputv2",
+  text = keyState.textInput,
+  width = "50%",
+  onEvent = function(elem, event)
+    if event.type == "textinput" then
+      keyState.textInput = keyState.textInput .. event.text
+      keyState.lastKey = event.text
+      elem.text = keyState.textInput
+    elseif event.type == "keypressed" then
+      keyState.lastKey = event.key
+
+      if event.key == "backspace" then
+        keyState.textInput = keyState.textInput:sub(1, -2)
+        elem.text = keyState.textInput
+      elseif event.key == "return" then
+        print("Submitted:", keyState.textInput)
+        keyState.textInput = ""
+        elem.text = ""
+      elseif event.key == "escape" then
+        elem:blur()  -- Remove focus
+      end
+    end
+  end
+})
+ +

Touch Events (Mobile Support)

+
local touchState = {
+  touchPos = { x = 0, y = 0 },
+  isTouching = false
+}
+
+-- Touchable area
+local touchArea = FlexLove.new({
+  width = "100%",
+  height = "100%",
+  backgroundColor = "#4a5568",
+  borderRadius = 8,
+  onEvent = function(_, event)
+    if event.type == "touch" then
+      touchState.isTouching = true
+      touchState.touchPos.x = event.x
+      touchState.touchPos.y = event.y
+      print("Touch started at:", event.x, event.y)
+    elseif event.type == "touchmoved" then
+      touchState.touchPos.x = event.x
+      touchState.touchPos.y = event.y
+    elseif event.type == "touchreleased" then
+      touchState.isTouching = false
+      print("Touch ended")
+    end
+  end
+})
+ +

Available Event Types

+
-- Mouse events
+"press"         -- Mouse button pressed
+"release"       -- Mouse button released
+"drag"          -- Mouse moved while pressed
+"mousemoved"    -- Mouse position changed
+"mouseenter"    -- Mouse entered element bounds
+"mouseleave"    -- Mouse left element bounds
+"wheel"         -- Mouse wheel scrolled
+
+-- Keyboard events
+"keypressed"    -- Key pressed (includes key name)
+"keyreleased"   -- Key released
+"textinput"     -- Text character entered
+
+-- Touch events (mobile)
+"touch"         -- Touch started
+"touchmoved"    -- Touch position changed
+"touchreleased" -- Touch ended
+
+-- Focus events
+"focus"         -- Element gained focus
+"blur"          -- Element lost focus
+ +
+

+ 💡 Tip: Use elem:focus() and + elem:blur() to programmatically control input field + focus. The event object contains contextual data like + x, y, key, etc. +

+
+ + View Complete Example → +
+ +
+

⚡ Performance Tips

+

+ Best practices for optimizing FlexLöve applications and avoiding + common performance pitfalls. +

+ +

Immediate Mode vs Retained Mode

+
-- Retained Mode (default) - elements persist between frames
+FlexLove.init({
+  baseScale = { width = 1920, height = 1080 }
+})
+
+-- Immediate Mode - elements recreated each frame
+FlexLove.init({
+  baseScale = { width = 1920, height = 1080 },
+  immediateMode = true
+})
+ +

Batch Element Creation

+
-- ❌ Bad: Creating elements in love.draw() (retained mode)
+function love.draw()
+  local button = FlexLove.new({ ... })  -- DON'T DO THIS
+end
+
+-- ✅ Good: Create once, reuse
+local button
+function love.load()
+  button = FlexLove.new({ ... })
+end
+
+function love.draw()
+  FlexLove.draw()  -- Just draw existing elements
+end
+ +

Limit Layout Recalculations

+
-- Avoid changing layout properties every frame
+local element = FlexLove.new({ ... })
+
+-- ❌ Bad: Causes relayout every frame
+function love.update(dt)
+  element.width = math.sin(love.timer.getTime()) * 100
+end
+
+-- ✅ Good: Update only when needed
+function love.update(dt)
+  if someCondition then
+    element.width = newWidth
+  end
+end
+ +

Use Element Pooling for Dynamic Content

+
-- Create a pool of reusable elements
+local itemPool = {}
+for i = 1, 50 do
+  itemPool[i] = FlexLove.new({
+    parent = scrollContainer,
+    visible = false,
+    text = "",
+    width = "100%",
+    themeComponent = "cardv2"
+  })
+end
+
+-- Reuse elements instead of creating new ones
+function updateList(items)
+  for i, item in ipairs(items) do
+    if itemPool[i] then
+      itemPool[i].text = item.name
+      itemPool[i].visible = true
+    end
+  end
+
+  -- Hide unused elements
+  for i = #items + 1, #itemPool do
+    itemPool[i].visible = false
+  end
+end
+ +
+

+ 💡 Tip: Use retained mode when you want the + lightest footprint possible, immediate mode is much faster to + prototype with and relative low overhead. +

+
+ + View Complete Example → +
+ +
+

🔗 More Examples

+

+ Find all complete working examples in the + examples/ + directory: +

+
    +
  • + advanced_layout.lua - Complex nested layouts with flex + and grid +
  • +
  • + theme_custom_components.lua - Theme switching and + custom components +
  • +
  • + stateful_ui.lua - Interactive state management patterns +
  • +
  • + scrollable_content.lua - Scrolling with backdrop blur +
  • +
  • + slider_example.lua - Slider controls for settings menus +
  • +
  • + input_handling.lua - Comprehensive input event handling +
  • +
  • + performance_example.lua - Performance optimization + techniques +
  • +
+
+
+ + + + diff --git a/docs/index.html b/docs/index.html index 2d789ec..962439b 100644 --- a/docs/index.html +++ b/docs/index.html @@ -1,152 +1,237 @@ - + - - - + + + FlexLöve Documentation - + - - + +
-
-

FlexLöve

-

A comprehensive UI library for LÖVE Framework based on flexbox

-
+
+

FlexLöve

+

A comprehensive UI library for LÖVE Framework based on flexbox

+
- + -
-
-
-

🎨 Flexbox & Grid Layouts

-

Modern CSS-like layout system with full flexbox and grid support for building responsive UIs.

-
-
-

🎭 Theme System

-

9-patch NinePatch theming with state support (normal, hover, pressed, disabled).

-
-
-

✨ Animations

-

Built-in animation support for smooth transitions and effects with easing functions.

-
-
-

📱 Responsive Design

-

Viewport units (vw, vh, %) for automatic resizing and responsive layouts.

-
-
-

⚡ Immediate Mode

-

Optional immediate mode support alongside traditional retained mode rendering.

-
-
-

🎯 Event System

-

Rich event handling with callbacks, focus management, and input fields.

-
-
+ -
-

Quick Start

-
local FlexLove = require("FlexLove")
+      
+

Quick Start

+
local FlexLove = require("FlexLove")
 
 -- Initialize with base scaling and theme
 FlexLove.init({
@@ -174,29 +259,70 @@ end
 function love.draw()
   FlexLove.draw()
 end
-
+
-
-

Documentation

-

The API reference is available in two formats:

- -

These docs are auto-generated from LuaLS annotations using the Lua Language Server.

-
- -
-

Installation

-

Add the modules directory and FlexLove.lua into your LÖVE project.

-
git clone https://github.com/mikefreno/FlexLove.git
+      
+

Installation

+

+ Add the modules directory and + FlexLove.lua into your LÖVE project. +

+
git clone https://github.com/mikefreno/FlexLove.git
 cp -r FlexLove/modules your-project/
 cp FlexLove/FlexLove.lua your-project/
-
+
- +
- + + diff --git a/docs/package-lock.json b/docs/package-lock.json new file mode 100644 index 0000000..dc6dec4 --- /dev/null +++ b/docs/package-lock.json @@ -0,0 +1,125 @@ +{ + "name": "flexlove-docs", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "flexlove-docs", + "version": "1.0.0", + "dependencies": { + "highlight.js": "^11.9.0", + "markdown-it": "^14.0.0", + "markdown-it-anchor": "^9.0.0" + } + }, + "node_modules/@types/linkify-it": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==", + "license": "MIT" + }, + "node_modules/@types/markdown-it": { + "version": "14.1.2", + "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.2.tgz", + "integrity": "sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/linkify-it": "^5", + "@types/mdurl": "^2" + } + }, + "node_modules/@types/mdurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==", + "license": "MIT" + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "license": "Python-2.0" + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/highlight.js": { + "version": "11.11.1", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.11.1.tgz", + "integrity": "sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/linkify-it": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", + "license": "MIT", + "dependencies": { + "uc.micro": "^2.0.0" + } + }, + "node_modules/markdown-it": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz", + "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==", + "license": "MIT", + "peer": true, + "dependencies": { + "argparse": "^2.0.1", + "entities": "^4.4.0", + "linkify-it": "^5.0.0", + "mdurl": "^2.0.0", + "punycode.js": "^2.3.1", + "uc.micro": "^2.1.0" + }, + "bin": { + "markdown-it": "bin/markdown-it.mjs" + } + }, + "node_modules/markdown-it-anchor": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-9.2.0.tgz", + "integrity": "sha512-sa2ErMQ6kKOA4l31gLGYliFQrMKkqSO0ZJgGhDHKijPf0pNFM9vghjAh3gn26pS4JDRs7Iwa9S36gxm3vgZTzg==", + "license": "Unlicense", + "peerDependencies": { + "@types/markdown-it": "*", + "markdown-it": "*" + } + }, + "node_modules/mdurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==", + "license": "MIT" + }, + "node_modules/punycode.js": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", + "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/uc.micro": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", + "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==", + "license": "MIT" + } + } +} diff --git a/docs/package.json b/docs/package.json new file mode 100644 index 0000000..7cd07a2 --- /dev/null +++ b/docs/package.json @@ -0,0 +1,13 @@ +{ + "name": "flexlove-docs", + "version": "1.0.0", + "private": true, + "scripts": { + "build": "node update-version.js && node build-docs.js" + }, + "dependencies": { + "markdown-it": "^14.0.0", + "markdown-it-anchor": "^9.0.0", + "highlight.js": "^11.9.0" + } +} diff --git a/docs/update-version.js b/docs/update-version.js new file mode 100644 index 0000000..4c297ed --- /dev/null +++ b/docs/update-version.js @@ -0,0 +1,34 @@ +const fs = require('fs'); +const path = require('path'); + +// Extract version from FlexLove.lua +function getVersion() { + try { + const flexlovePath = path.join(__dirname, '..', 'FlexLove.lua'); + const content = fs.readFileSync(flexlovePath, 'utf8'); + const match = content.match(/flexlove\._VERSION\s*=\s*["']([^"']+)["']/); + return match ? match[1] : 'unknown'; + } catch (e) { + return 'unknown'; + } +} + +// Update index.html with current version +function updateIndexVersion() { + const version = getVersion(); + const indexPath = path.join(__dirname, 'index.html'); + let content = fs.readFileSync(indexPath, 'utf8'); + + // Update version in multiple places + content = content.replace( + /FlexLöve v[\d.]+/g, + `FlexLöve v${version}` + ); + + fs.writeFileSync(indexPath, content, 'utf8'); + console.log(`✓ Updated index.html to version ${version}`); +} + +const version = getVersion(); +console.log(`Current version: ${version}`); +updateIndexVersion(); diff --git a/docs/versions/README.md b/docs/versions/README.md new file mode 100644 index 0000000..ee3b584 --- /dev/null +++ b/docs/versions/README.md @@ -0,0 +1,86 @@ +# FlexLöve Documentation Versions + +This directory stores versioned snapshots of the FlexLöve API documentation. + +## Purpose + +Each time a new version of FlexLöve is released, the documentation is archived here so users can reference docs for specific versions they're using. + +## Structure + +``` +docs/ +├── api.html # Latest/current version documentation +├── index.html # Landing page with version selector +└── versions/ + ├── v0.2.0/ + │ └── api.html # Documentation for v0.2.0 + ├── v0.3.0/ + │ └── api.html # Documentation for v0.3.0 + └── v1.0.0/ + └── api.html # Documentation for v1.0.0 +``` + +## Naming Convention + +- Version directories follow the pattern: `v{major}.{minor}.{patch}` +- Examples: `v0.2.0`, `v1.0.0`, `v2.1.3` +- Each directory contains `api.html` (the full API documentation for that version) + +## Access + +### Via GitHub Pages + +Once deployed, versions are accessible at: +- Latest: `https://{user}.github.io/{repo}/api.html` +- Specific version: `https://{user}.github.io/{repo}/versions/v0.2.0/api.html` + +### Via Repository + +Browse directly in the repository: +- `docs/api.html` - Always shows latest +- `docs/versions/v0.2.0/api.html` - Specific version + +## Automation + +Documentation versions are automatically archived when: +1. A new git tag is pushed (e.g., `git push origin v0.3.0`) +2. GitHub Actions workflow runs +3. Documentation is generated and archived +4. Version is committed back to the repository + +See `.github/workflows/release.yml` for implementation details. + +## Manual Archival + +To manually archive a version: + +```bash +# Generate docs for current version +./scripts/generate_docs.sh + +# Create version directory +VERSION="0.2.0" +mkdir -p "docs/versions/v${VERSION}" + +# Copy documentation +cp docs/api.html "docs/versions/v${VERSION}/api.html" + +# Commit +git add docs/versions/ +git commit -m "Archive documentation for v${VERSION}" +git push +``` + +## Storage Considerations + +- Each `api.html` file is approximately 200-300KB +- Storing 10-20 versions = 2-6MB total +- GitHub has a 1GB repository soft limit (we're well within it) +- Old versions are kept indefinitely for reference + +## Version Dropdown + +The main documentation pages (`api.html` and `index.html`) include a version dropdown that automatically detects and lists all available versions from this directory. + +Users can switch between versions without leaving the documentation. diff --git a/docs/versions/v0.2.0/api.html b/docs/versions/v0.2.0/api.html new file mode 100644 index 0000000..9cdcd92 --- /dev/null +++ b/docs/versions/v0.2.0/api.html @@ -0,0 +1,3732 @@ + + + + + + FlexLöve v0.2.0 - API Reference + + + + +
+ + +
+

Animation

+

apply

+
(method) Animation:apply(element: Element)
+
+

duration

+
number
+
+

elapsed

+
number
+
+

fade

+
function Animation.fade(duration: number, fromOpacity: number, toOpacity: number)
+  -> Animation
+
+

Create a simple fade animation

+

final

+
{ width: number, height: number, opacity: number }
+
+

interpolate

+
(method) Animation:interpolate()
+  -> table
+
+

new

+
function Animation.new(props: AnimationProps)
+  -> Animation
+
+

scale

+
function Animation.scale(duration: number, fromScale: table, toScale: table)
+  -> Animation
+
+

Create a simple scale animation

+

@param fromScale — {width:number,height:number}

+

@param toScale — {width:number,height:number}

+

start

+
{ width: number, height: number, opacity: number }
+
+

transform

+
table?
+
+

transition

+
table?
+
+

update

+
(method) Animation:update(dt: number)
+  -> boolean
+
+
+
+

⚠️ Internal Properties

+
+

Warning: The following properties are internal implementation details and should not be accessed directly. They are prefixed with _ to indicate they are private. Accessing these properties may break in future versions without notice.

+
+
+

__index

+
Animation
+
+

_resultDirty

+
boolean
+
+

AnimationProps

+

duration

+
number
+
+

final

+
{ width: number, height: number, opacity: number }
+
+

start

+
{ width: number, height: number, opacity: number }
+
+

transform

+
table?
+
+

transition

+
table?
+
+
+

Color

+

a

+
number
+
+

Alpha component (0-1)

+

b

+
number
+
+

Blue component (0-1)

+

fromHex

+
function Color.fromHex(hexWithTag: string)
+  -> Color
+
+

Convert hex string to color +Supports both 6-digit (#RRGGBB) and 8-digit (#RRGGBBAA) hex formats

+

@param hexWithTag — e.g. “#RRGGBB” or “#RRGGBBAA”

+

g

+
number
+
+

Green component (0-1)

+

isValidColorFormat

+
function Color.isValidColorFormat(value: any)
+  -> format: string?
+
+

Check if a value is a valid color format

+

@param value — Value to check

+

@return format — Format type (hex, rgb, rgba, named, table, nil if invalid)

+

new

+
function Color.new(r?: number, g?: number, b?: number, a?: number)
+  -> Color
+
+

Create a new color instance

+

parse

+
function Color.parse(value: any)
+  -> Color
+
+

Parse a color from various formats

+

@param value — Color value (hex, named, table)

+

@return — Parsed color

+

r

+
number
+
+

Red component (0-1)

+

sanitizeColor

+
function Color.sanitizeColor(value: any, default?: Color)
+  -> Color
+
+

Sanitize a color value

+

@param value — Color value to sanitize

+

@param default — Default color if invalid

+

@return — Sanitized color

+

toRGBA

+
(method) Color:toRGBA()
+  -> r: number
+  2. g: number
+  3. b: number
+  4. a: number
+
+

validateColor

+
function Color.validateColor(value: any, options?: table)
+  -> valid: boolean
+  2. error: string?
+
+

Validate a color value

+

@param value — Color value to validate

+

@param options — Validation options

+

@return valid — True if valid

+

@return error — Error message if invalid

+

validateColorChannel

+
function Color.validateColorChannel(value: any, max?: number)
+  -> valid: boolean
+  2. clamped: number?
+
+

Validate a single color channel value

+

@param value — Value to validate

+

@param max — Maximum value (255 for 0-255 range, 1 for 0-1 range)

+

@return valid — True if valid

+

@return clamped — Clamped value in 0-1 range

+

validateHexColor

+
function Color.validateHexColor(hex: string)
+  -> valid: boolean
+  2. error: string?
+
+

Validate hex color format

+

@param hex — Hex color string (with or without #)

+

@return valid — True if valid format

+

@return error — Error message if invalid

+

validateNamedColor

+
function Color.validateNamedColor(name: string)
+  -> valid: boolean
+  2. error: string?
+
+

Validate named color

+

@param name — Color name

+

@return valid — True if valid

+

@return error — Error message if invalid

+

validateRGBColor

+
function Color.validateRGBColor(r: number, g: number, b: number, a?: number, max?: number)
+  -> valid: boolean
+  2. error: string?
+
+

Validate RGB/RGBA color values

+

@param r — Red component

+

@param g — Green component

+

@param b — Blue component

+

@param a — Alpha component (optional)

+

@param max — Maximum value (255 or 1)

+

@return valid — True if valid

+

@return error — Error message if invalid

+
+
+

⚠️ Internal Properties

+
+

Warning: The following properties are internal implementation details and should not be accessed directly. They are prefixed with _ to indicate they are private. Accessing these properties may break in future versions without notice.

+
+
+

__index

+
Color
+
+

Utility class for color handling

+

Element

+

active

+
boolean?
+
+

Whether the element is active/focused (for inputs, default: false)

+

addChild

+
(method) Element:addChild(child: Element)
+
+

Add child to element

+

alignContent

+
AlignContent
+
+

Alignment of lines in multi-line flex containers (default: STRETCH)

+

alignItems

+
AlignItems
+
+

Alignment of items along cross axis (default: STRETCH)

+

alignSelf

+
AlignSelf
+
+

Alignment of the item itself along cross axis (default: AUTO)

+

animation

+
table?
+
+

Animation instance for this element

+

applyPositioningOffsets

+
(method) Element:applyPositioningOffsets(element: any)
+
+

Apply positioning offsets (top, right, bottom, left) to an element +@param element The element to apply offsets to

+

autoGrow

+
boolean
+
+

Whether element auto-grows with text (default: false)

+

autoScaleText

+
boolean
+
+

Whether text should auto-scale with window size (default: true)

+

autosizing

+
{ width: boolean, height: boolean }
+
+

Whether the element should automatically size to fit its children

+

backdropBlur

+
{ intensity: number, quality: number }?
+
+

Blur content behind the element (intensity: 0-100, quality: 1-10)

+

backgroundColor

+
Color
+
+

Background color of the element

+

blur

+
(method) Element:blur()
+
+

Remove focus from this element

+

border

+
Border
+
+

Border configuration for the element

+

borderColor

+
Color
+
+

Color of the border

+

bottom

+
number?
+
+

Offset from bottom edge (CSS-style positioning)

+

calculateAutoHeight

+
(method) Element:calculateAutoHeight()
+  -> number
+
+

Calculate auto height based on children

+

calculateAutoWidth

+
(method) Element:calculateAutoWidth()
+  -> number
+
+

calculateTextHeight

+
(method) Element:calculateTextHeight()
+  -> number
+
+

calculateTextWidth

+
(method) Element:calculateTextWidth()
+  -> number
+
+

Calculate text width for button

+

children

+
table<integer, Element>
+
+

Children of this element

+

clearChildren

+
(method) Element:clearChildren()
+
+

Remove all children from this element

+

clearSelection

+
(method) Element:clearSelection()
+
+

Clear selection

+

columnGap

+
(string|number)?
+
+

Gap between grid columns

+

contains

+
(method) Element:contains(x: number, y: number)
+  -> boolean
+
+

Check if point is inside element bounds

+

contentAutoSizingMultiplier

+
{ width: number?, height: number? }?
+
+

Multiplier for auto-sized content dimensions

+

contentBlur

+
{ intensity: number, quality: number }?
+
+

Blur the element’s content including children (intensity: 0-100, quality: 1-10)

+

cornerRadius

+
(number|{ topLeft: number?, topRight: number?, bottomLeft: number?, bottomRight: number? })?
+
+

Corner radius for rounded corners (default: 0)

+

cursorBlinkRate

+
number
+
+

Cursor blink rate in seconds (default: 0.5)

+

cursorColor

+
Color?
+
+

Cursor color (default: nil, uses textColor)

+

deleteSelection

+
(method) Element:deleteSelection()
+  -> boolean
+
+

Delete selected text

+

@return — True if text was deleted

+

deleteText

+
(method) Element:deleteText(startPos: number, endPos: number)
+
+

@param startPos — Start position (inclusive)

+

@param endPos — End position (inclusive)

+

destroy

+
(method) Element:destroy()
+
+

Destroy element and its children

+

disableHighlight

+
boolean?
+
+

Whether to disable the pressed state highlight overlay (default: false)

+

disabled

+
boolean?
+
+

Whether the element is disabled (default: false)

+

draw

+
(method) Element:draw(backdropCanvas: any)
+
+

Draw element and its children

+

editable

+
boolean
+
+

Whether the element is editable (default: false)

+

flexDirection

+
FlexDirection
+
+

Direction of flex layout (default: HORIZONTAL)

+

flexWrap

+
FlexWrap
+
+

Whether children wrap to multiple lines (default: NOWRAP)

+

focus

+
(method) Element:focus()
+
+

Focus this element for keyboard input

+

fontFamily

+
string?
+
+

Font family name from theme or path to font file

+

gap

+
string|number
+
+

Space between children elements (default: 10)

+

getAvailableContentHeight

+
(method) Element:getAvailableContentHeight()
+  -> number
+
+

Get available content height for children (accounting for 9-patch content padding) +This is the height that children should use when calculating percentage heights

+

getAvailableContentWidth

+
(method) Element:getAvailableContentWidth()
+  -> number
+
+

Get available content width for children (accounting for 9-patch content padding) +This is the width that children should use when calculating percentage widths

+

getBlurInstance

+
(method) Element:getBlurInstance()
+  -> table?
+
+

Get or create blur instance for this element

+

@return — Blur instance or nil if no blur configured

+

getBorderBoxHeight

+
(method) Element:getBorderBoxHeight()
+  -> number
+
+

Get border-box height (including padding)

+

getBorderBoxWidth

+
(method) Element:getBorderBoxWidth()
+  -> number
+
+

Get border-box width (including padding)

+

getBounds

+
(method) Element:getBounds()
+  -> { x: number, y: number, width: number, height: number }
+
+

Get element bounds (content box)

+

getChildCount

+
(method) Element:getChildCount()
+  -> number
+
+

Get the number of children this element has

+

getContentSize

+
(method) Element:getContentSize()
+  -> contentWidth: number
+  2. contentHeight: number
+
+

Get content dimensions (including overflow) (delegates to ScrollManager)

+

getCursorPosition

+
(method) Element:getCursorPosition()
+  -> number
+
+

Get cursor position

+

@return — Character index (0-based)

+

getMaxScroll

+
(method) Element:getMaxScroll()
+  -> maxScrollX: number
+  2. maxScrollY: number
+
+

Get maximum scroll bounds (delegates to ScrollManager)

+

getScaledContentPadding

+
(method) Element:getScaledContentPadding()
+  -> table|nil
+
+

Get the current state’s scaled content padding +Returns the contentPadding for the current theme state, scaled to the element’s size

+

@return — {left, top, right, bottom} or nil if no contentPadding

+

getScrollPercentage

+
(method) Element:getScrollPercentage()
+  -> percentX: number
+  2. percentY: number
+
+

Get scroll percentage (0-1) (delegates to ScrollManager)

+

getScrollPosition

+
(method) Element:getScrollPosition()
+  -> scrollX: number
+  2. scrollY: number
+
+

Get current scroll position (delegates to ScrollManager)

+

getSelectedText

+
(method) Element:getSelectedText()
+  -> string?
+
+

Get selected text

+

@return — Selected text or nil if no selection

+

getSelection

+
(method) Element:getSelection()
+  -> number?
+  2. number?
+
+

Get selection range

+

@return — Start and end positions, or nil if no selection

+

getText

+
(method) Element:getText()
+  -> string
+
+

Get current text buffer

+

gridColumns

+
number?
+
+

Number of columns in the grid

+

gridRows

+
number?
+
+

Number of rows in the grid

+

hasOverflow

+
(method) Element:hasOverflow()
+  -> hasOverflowX: boolean
+  2. hasOverflowY: boolean
+
+

Check if element has overflow (delegates to ScrollManager)

+

hasSelection

+
(method) Element:hasSelection()
+  -> boolean
+
+

Check if there is an active selection

+

height

+
string|number
+
+

Height of the element

+

hide

+
(method) Element:hide()
+
+

same as calling updateOpacity(0)

+

hideScrollbars

+
(boolean|{ vertical: boolean, horizontal: boolean })?
+
+

Hide scrollbars (boolean for both, or table for individual control)

+

id

+
string
+
+

image

+
(love.Image)?
+
+

Image object to display

+

imageOpacity

+
number?
+
+

Image opacity 0-1 (default: 1, combines with element opacity)

+

imagePath

+
string?
+
+

Path to image file (auto-loads via ImageCache)

+

inputType

+
"email"|"number"|"text"|"url"
+
+

Input type for validation (default: “text”)

+

insertText

+
(method) Element:insertText(text: string, position?: number)
+
+

Insert text at position

+

@param text — Text to insert

+

@param position — Position to insert at (default: cursor position)

+

isFocused

+
(method) Element:isFocused()
+  -> boolean
+
+

Check if this element is focused

+

justifyContent

+
JustifyContent
+
+

Alignment of items along main axis (default: FLEX_START)

+

justifySelf

+
JustifySelf
+
+

Alignment of the item itself along main axis (default: AUTO)

+

keypressed

+
(method) Element:keypressed(key: string, scancode: string, isrepeat: boolean)
+
+

Handle key press (special keys)

+

@param key — Key name

+

@param scancode — Scancode

+

@param isrepeat — Whether this is a key repeat

+

layoutChildren

+
(method) Element:layoutChildren()
+
+

left

+
number?
+
+

Offset from left edge (CSS-style positioning)

+

margin

+
{ top: number, right: number, bottom: number, left: number }
+
+

Margin around children (default: {top=0, right=0, bottom=0, left=0})

+

maxLength

+
number?
+
+

Maximum text length in characters (default: nil)

+

maxLines

+
number?
+
+

Maximum number of lines (default: nil)

+

maxTextSize

+
number?
+
+

minTextSize

+
number?
+
+

moveCursorBy

+
(method) Element:moveCursorBy(delta: number)
+
+

Move cursor by delta characters

+

@param delta — Number of characters to move (positive or negative)

+

moveCursorToEnd

+
(method) Element:moveCursorToEnd()
+
+

Move cursor to end of text

+

moveCursorToLineEnd

+
(method) Element:moveCursorToLineEnd()
+
+

Move cursor to end of current line

+

moveCursorToLineStart

+
(method) Element:moveCursorToLineStart()
+
+

Move cursor to start of current line

+

moveCursorToNextWord

+
(method) Element:moveCursorToNextWord()
+
+

Move cursor to start of next word

+

moveCursorToPreviousWord

+
(method) Element:moveCursorToPreviousWord()
+
+

Move cursor to start of previous word

+

moveCursorToStart

+
(method) Element:moveCursorToStart()
+
+

Move cursor to start of text

+

multiline

+
boolean
+
+

Whether the element supports multiple lines (default: false)

+

new

+
function Element.new(props: ElementProps, deps: table)
+  -> Element
+
+

@param deps — Required dependency table (provided by FlexLove)

+

objectFit

+
("contain"|"cover"|"fill"|"none"|"scale-down")?
+
+

Image fit mode (default: “fill”)

+

objectPosition

+
string?
+
+

Image position like “center center”, “top left”, “50% 50%” (default: “center center”)

+

onBlur

+
fun(element: Element)?
+
+

Callback function when element loses focus

+

onEnter

+
fun(element: Element)?
+
+

Callback function when Enter key is pressed

+

onEvent

+
fun(element: Element, event: InputEvent)?
+
+

Callback function for interaction events

+

onFocus

+
fun(element: Element)?
+
+

Callback function when element receives focus

+

onTextChange

+
fun(element: Element, text: string)?
+
+

Callback function when text changes

+

onTextInput

+
fun(element: Element, text: string)?
+
+

Callback function for text input

+

opacity

+
number
+
+

overflow

+
string?
+
+

Overflow behavior for both axes

+

overflowX

+
string?
+
+

Overflow behavior for horizontal axis

+

overflowY

+
string?
+
+

Overflow behavior for vertical axis

+

padding

+
{ top: number, right: number, bottom: number, left: number }?
+
+

Padding around children (default: {top=0, right=0, bottom=0, left=0})

+

parent

+
Element?
+
+

Parent element (nil if top-level)

+

passwordMode

+
boolean
+
+

Whether to display text as password (default: false)

+

placeholder

+
string?
+
+

Placeholder text when empty (default: nil)

+

positioning

+
Positioning
+
+

Layout positioning mode (default: RELATIVE)

+

prevGameSize

+
{ width: number, height: number }
+
+

Previous game size for resize calculations

+

recalculateUnits

+
(method) Element:recalculateUnits(newViewportWidth: number, newViewportHeight: number)
+
+

removeChild

+
(method) Element:removeChild(child: Element)
+
+

Remove a specific child from this element

+

replaceText

+
(method) Element:replaceText(startPos: number, endPos: number, newText: string)
+
+

Replace text in range

+

@param startPos — Start position (inclusive)

+

@param endPos — End position (inclusive)

+

@param newText — Replacement text

+

resize

+
(method) Element:resize(newGameWidth: number, newGameHeight: number)
+
+

Resize element and its children based on game window size change

+ +
number?
+
+

Offset from right edge (CSS-style positioning)

+

rowGap

+
(string|number)?
+
+

Gap between grid rows

+

scaleCorners

+
number?
+
+

Scale multiplier for 9-patch corners/edges. E.g., 2 = 2x size (overrides theme setting)

+

scalingAlgorithm

+
("bilinear"|"nearest")?
+
+

Scaling algorithm for 9-patch corners: “nearest” (sharp/pixelated) or “bilinear” (smooth) (overrides theme setting)

+

scrollBy

+
(method) Element:scrollBy(dx?: number, dy?: number)
+
+

Scroll by delta amount (delegates to ScrollManager)

+

@param dx — X delta (nil for no change)

+

@param dy — Y delta (nil for no change)

+

scrollSpeed

+
number?
+
+

Scroll speed multiplier

+

scrollToBottom

+
(method) Element:scrollToBottom()
+
+

Scroll to bottom

+

scrollToLeft

+
(method) Element:scrollToLeft()
+
+

Scroll to left

+

scrollToRight

+
(method) Element:scrollToRight()
+
+

Scroll to right

+

scrollToTop

+
(method) Element:scrollToTop()
+
+

Scroll to top

+

scrollable

+
boolean
+
+

Whether text is scrollable (default: false for single-line, true for multi-line)

+

scrollbarColor

+
Color?
+
+

Scrollbar thumb color

+

scrollbarPadding

+
number?
+
+

Scrollbar padding from edges

+

scrollbarRadius

+
number?
+
+

Scrollbar corner radius

+

scrollbarTrackColor

+
Color?
+
+

Scrollbar track color

+

scrollbarWidth

+
number?
+
+

Scrollbar width in pixels

+

selectAll

+
(method) Element:selectAll()
+
+

Select all text

+

selectOnFocus

+
boolean
+
+

Whether to select all text on focus (default: false)

+

selectionColor

+
Color?
+
+

Selection background color (default: nil, uses theme or default)

+

setCursorPosition

+
(method) Element:setCursorPosition(position: number)
+
+

Set cursor position

+

@param position — Character index (0-based)

+

setScrollPosition

+
(method) Element:setScrollPosition(x?: number, y?: number)
+
+

Set scroll position with bounds clamping (delegates to ScrollManager)

+

@param x — X scroll position (nil to keep current)

+

@param y — Y scroll position (nil to keep current)

+

setSelection

+
(method) Element:setSelection(startPos: number, endPos: number)
+
+

Set selection range

+

@param startPos — Start position (inclusive)

+

@param endPos — End position (inclusive)

+

setText

+
(method) Element:setText(text: string)
+
+

Set text buffer and mark dirty

+

show

+
(method) Element:show()
+
+

same as calling updateOpacity(1)

+

text

+
string?
+
+

Text content to display in the element

+

textAlign

+
TextAlign
+
+

Alignment of the text content

+

textColor

+
Color
+
+

Color of the text content

+

textOverflow

+
"clip"|"ellipsis"|"scroll"
+
+

Text overflow behavior (default: “clip”)

+

textSize

+
number?
+
+

Resolved font size for text content in pixels

+

textWrap

+
boolean|"char"|"word"
+
+

Text wrapping mode (default: false for single-line, “word” for multi-line)

+

textinput

+
(method) Element:textinput(text: string)
+
+

Handle text input (character input)

+

@param text — Character(s) to insert

+

theme

+
string?
+
+

Theme component to use for rendering

+

themeComponent

+
string?
+
+

top

+
number?
+
+

Offset from top edge (CSS-style positioning)

+

transform

+
TransformProps
+
+

Transform properties for animations and styling

+

transition

+
TransitionProps
+
+

Transition settings for animations

+

units

+
table
+
+

Original unit specifications for responsive behavior

+

update

+
(method) Element:update(dt: number)
+
+

Update element (propagate to children)

+

updateOpacity

+
(method) Element:updateOpacity(newOpacity: number)
+
+

updateText

+
(method) Element:updateText(newText: string, autoresize?: boolean)
+
+

@param autoresize — default: false

+

userdata

+
table?
+
+

width

+
string|number
+
+

Width of the element

+

x

+
string|number
+
+

X coordinate of the element

+

y

+
string|number
+
+

Y coordinate of the element

+

z

+
number
+
+

Z-index for layering (default: 0)

+
+
+

⚠️ Internal Properties

+
+

Warning: The following properties are internal implementation details and should not be accessed directly. They are prefixed with _ to indicate they are private. Accessing these properties may break in future versions without notice.

+
+
+

__index

+
Element
+
+

_blurInstance

+
table?
+
+

Internal: cached blur effect instance

+

_borderBoxHeight

+
number?
+
+

Internal: cached border-box height

+

_borderBoxWidth

+
number?
+
+

Internal: cached border-box width

+

_calculateScrollbarDimensions

+
(method) Element:_calculateScrollbarDimensions()
+  -> table
+
+

Calculate scrollbar dimensions and positions (delegates to ScrollManager)

+

@return — {vertical: {visible, trackHeight, thumbHeight, thumbY}, horizontal: {visible, trackWidth, thumbWidth, thumbX}}

+

_contentHeight

+
number?
+
+

Internal: total content height

+

_contentWidth

+
number?
+
+

Internal: total content width

+

_cursorBlinkPauseTimer

+
number?
+
+

Internal: timer for how long cursor blink has been paused

+

_cursorBlinkPaused

+
boolean?
+
+

Internal: whether cursor blink is paused (e.g., while typing)

+

_cursorBlinkTimer

+
number?
+
+

Internal: cursor blink timer

+

_cursorColumn

+
number?
+
+

Internal: cursor column within line

+

_cursorLine

+
number?
+
+

Internal: cursor line number (1-based)

+

_cursorPosition

+
number?
+
+

Internal: cursor character position (0-based)

+

_cursorVisible

+
boolean?
+
+

Internal: cursor visibility state

+

_detectOverflow

+
(method) Element:_detectOverflow()
+
+

Detect if content overflows container bounds (delegates to ScrollManager)

+

_eventHandler

+
EventHandler
+
+

Event handler instance for input processing

+

_explicitlyAbsolute

+
boolean?
+
+

_focused

+
boolean?
+
+

Internal: focus state

+

_getFont

+
(method) Element:_getFont()
+  -> love.Font
+
+

_getScrollbarAtPosition

+
(method) Element:_getScrollbarAtPosition(mouseX: number, mouseY: number)
+  -> table|nil
+
+

Get scrollbar at mouse position (delegates to ScrollManager)

+

@return — {component: “vertical”|“horizontal”, region: “thumb”|“track”}

+

_handleScrollbarDrag

+
(method) Element:_handleScrollbarDrag(mouseX: number, mouseY: number)
+  -> boolean
+
+

Handle scrollbar drag (delegates to ScrollManager)

+

@return — True if event was consumed

+

_handleScrollbarPress

+
(method) Element:_handleScrollbarPress(mouseX: number, mouseY: number, button: number)
+  -> boolean
+
+

Handle scrollbar mouse press

+

@return — True if event was consumed

+

_handleScrollbarRelease

+
(method) Element:_handleScrollbarRelease(button: number)
+  -> boolean
+
+

Handle scrollbar release (delegates to ScrollManager)

+

@return — True if event was consumed

+

_handleTextClick

+
(method) Element:_handleTextClick(mouseX: number, mouseY: number, clickCount: number)
+
+

Handle mouse click on text (set cursor position or start selection)

+

@param mouseX — Mouse X coordinate

+

@param mouseY — Mouse Y coordinate

+

@param clickCount — Number of clicks (1=single, 2=double, 3=triple)

+

_handleTextDrag

+
(method) Element:_handleTextDrag(mouseX: number, mouseY: number)
+
+

Handle mouse drag for text selection

+

@param mouseX — Mouse X coordinate

+

@param mouseY — Mouse Y coordinate

+

_handleWheelScroll

+
(method) Element:_handleWheelScroll(x: number, y: number)
+  -> boolean
+
+

Handle mouse wheel scrolling (delegates to ScrollManager)

+

@param x — Horizontal scroll amount

+

@param y — Vertical scroll amount

+

@return — True if scroll was handled

+

_hoveredScrollbar

+
table?
+
+

Internal: currently hovered scrollbar info

+

_layoutEngine

+
LayoutEngine
+
+

Internal: LayoutEngine instance for layout calculations

+

_lines

+
table?
+
+

Internal: split lines for multi-line text

+

_loadedImage

+
(love.Image)?
+
+

Internal: cached loaded image

+

_maxScrollX

+
number?
+
+

Internal: maximum horizontal scroll

+

_maxScrollY

+
number?
+
+

Internal: maximum vertical scroll

+

_mouseDownPosition

+
number?
+
+

Internal: mouse down position for drag tracking

+

_originalPositioning

+
Positioning?
+
+

Original positioning value set by user

+

_overflowX

+
boolean?
+
+

Internal: whether content overflows horizontally

+

_overflowY

+
boolean?
+
+

Internal: whether content overflows vertically

+

_pressed

+
table?
+
+

Internal: button press state tracking

+

_renderer

+
Renderer
+
+

Internal: Renderer instance for visual rendering

+

_scrollManager

+
ScrollManager?
+
+

Internal: ScrollManager instance for scroll handling

+

_scrollX

+
number?
+
+

Internal: horizontal scroll position

+

_scrollY

+
number?
+
+

Internal: vertical scroll position

+

_scrollbarDragOffset

+
number?
+
+

Internal: scrollbar drag offset

+

_scrollbarDragging

+
boolean?
+
+

Internal: scrollbar dragging state

+

_scrollbarHoveredHorizontal

+
boolean?
+
+

Internal: horizontal scrollbar hover state

+

_scrollbarHoveredVertical

+
boolean?
+
+

Internal: vertical scrollbar hover state

+

_scrollbarPressHandled

+
boolean?
+
+

Internal: scrollbar press handled flag

+

_selectionAnchor

+
number?
+
+

Internal: selection anchor point

+

_selectionEnd

+
number?
+
+

Internal: selection end position

+

_selectionStart

+
number?
+
+

Internal: selection start position

+

_stateId

+
string?
+
+

State manager ID for this element

+

_syncScrollManagerState

+
(method) Element:_syncScrollManagerState()
+
+

Sync ScrollManager state to Element properties for backward compatibility +This ensures Renderer and StateManager can access scroll state from Element

+

_textBuffer

+
string?
+
+

Internal: text buffer for editable elements

+

_textDirty

+
boolean?
+
+

Internal: flag to recalculate lines/wrapping

+

_textDragOccurred

+
boolean?
+
+

Internal: whether text drag occurred

+

_textEditor

+
TextEditor?
+
+

Internal: TextEditor instance for editable elements

+

_themeManager

+
ThemeManager
+
+

Internal: theme manager instance

+

_themeState

+
string?
+
+

Current theme state (normal, hover, pressed, active, disabled)

+

_wrapLine

+
(method) Element:_wrapLine(line: string, maxWidth: number)
+  -> table
+
+

Wrap a single line of text

+

@param line — Line to wrap

+

@param maxWidth — Maximum width in pixels

+

@return — Array of wrapped line parts

+

_wrappedLines

+
table?
+
+

Internal: wrapped line data

+

ElementProps

+

active

+
boolean?
+
+

Whether the element is active/focused (for inputs, default: false)

+

alignContent

+
AlignContent?
+
+

Alignment of lines in multi-line flex containers (default: STRETCH)

+

alignItems

+
AlignItems?
+
+

Alignment of items along cross axis (default: STRETCH)

+

alignSelf

+
AlignSelf?
+
+

Alignment of the item itself along cross axis (default: AUTO)

+

autoGrow

+
boolean?
+
+

Whether element auto-grows with text (default: false for single-line, true for multi-line)

+

autoScaleText

+
boolean?
+
+

Whether text should auto-scale with window size (default: true)

+

backdropBlur

+
{ intensity: number, quality: number }?
+
+

Blur content behind the element (intensity: 0-100, quality: 1-10, default: nil)

+

backgroundColor

+
Color?
+
+

Background color (default: transparent)

+

border

+
Border?
+
+

Border configuration for the element

+

borderColor

+
Color?
+
+

Color of the border (default: black)

+

bottom

+
(string|number)?
+
+

Offset from bottom edge (CSS-style positioning)

+

columnGap

+
(string|number)?
+
+

Gap between grid columns (default: 0)

+

contentAutoSizingMultiplier

+
{ width: number?, height: number? }?
+
+

Multiplier for auto-sized content dimensions (default: sourced from theme or {1, 1})

+

contentBlur

+
{ intensity: number, quality: number }?
+
+

Blur the element’s content including children (intensity: 0-100, quality: 1-10, default: nil)

+

cornerRadius

+
(number|{ topLeft: number?, topRight: number?, bottomLeft: number?, bottomRight: number? })?
+
+

Corner radius: number (all corners) or table for individual corners (default: 0)

+

cursorBlinkRate

+
number?
+
+

Cursor blink rate in seconds (default: 0.5)

+

cursorColor

+
Color?
+
+

Cursor color (default: nil, uses textColor)

+

disableHighlight

+
boolean?
+
+

Whether to disable the pressed state highlight overlay (default: false, or true when using themeComponent)

+

disabled

+
boolean?
+
+

Whether the element is disabled (default: false)

+

editable

+
boolean?
+
+

Whether the element is editable (default: false)

+

flexDirection

+
FlexDirection?
+
+

Direction of flex layout: “horizontal”|“vertical” (default: HORIZONTAL)

+

flexWrap

+
FlexWrap?
+
+

Whether children wrap to multiple lines: “nowrap”|“wrap”|“wrap-reverse” (default: NOWRAP)

+

fontFamily

+
string?
+
+

Font family name from theme or path to font file (default: theme default or system default, inherits from parent)

+

gap

+
(string|number)?
+
+

Space between children elements (default: 0)

+

gridColumns

+
number?
+
+

Number of columns in the grid (default: 1)

+

gridRows

+
number?
+
+

Number of rows in the grid (default: 1)

+

height

+
(string|number)?
+
+

Height of the element (default: calculated automatically)

+

hideScrollbars

+
(boolean|{ vertical: boolean, horizontal: boolean })?
+
+

Hide scrollbars (boolean for both, or table for individual control, default: false)

+

id

+
string?
+
+

Unique identifier for the element (auto-generated in immediate mode if not provided)

+

image

+
(love.Image)?
+
+

Image object to display

+

imageOpacity

+
number?
+
+

Image opacity 0-1 (default: 1, combines with element opacity)

+

imagePath

+
string?
+
+

Path to image file (auto-loads via ImageCache)

+

inputType

+
("email"|"number"|"text"|"url")?
+
+

Input type for validation (default: “text”)

+

justifyContent

+
JustifyContent?
+
+

Alignment of items along main axis (default: FLEX_START)

+

justifySelf

+
JustifySelf?
+
+

Alignment of the item itself along main axis (default: AUTO)

+

left

+
(string|number)?
+
+

Offset from left edge (CSS-style positioning)

+

margin

+
{ top: (string|number)?, right: (string|number)?, bottom: (string|number)?, left: (string|number)?, horizontal: (string|number)?, vertical: (string|number)? }?
+
+

Margin around element (default: {top=0, right=0, bottom=0, left=0})

+

maxLength

+
number?
+
+

Maximum text length in characters (default: nil)

+

maxLines

+
number?
+
+

Maximum number of lines (default: nil)

+

maxTextSize

+
number?
+
+

Maximum text size in pixels for auto-scaling

+

minTextSize

+
number?
+
+

Minimum text size in pixels for auto-scaling

+

multiline

+
boolean?
+
+

Whether the element supports multiple lines (default: false)

+

objectFit

+
("contain"|"cover"|"fill"|"none"|"scale-down")?
+
+

Image fit mode (default: “fill”)

+

objectPosition

+
string?
+
+

Image position like “center center”, “top left”, “50% 50%” (default: “center center”)

+

onBlur

+
fun(element: Element, event: InputEvent)?
+
+

Callback when element loses focus

+

onEnter

+
fun(element: Element)?
+
+

Callback when Enter key is pressed

+

onEvent

+
fun(element: Element, event: InputEvent)?
+
+

Callback function for interaction events

+

onFocus

+
fun(element: Element, event: InputEvent)?
+
+

Callback when element receives focus

+

onTextChange

+
fun(element: Element, text: string)?
+
+

Callback when text content changes

+

onTextInput

+
fun(element: Element, text: string)?
+
+

Callback when text is input

+

opacity

+
number?
+
+

Element opacity 0-1 (default: 1)

+

overflow

+
("auto"|"hidden"|"scroll"|"visible")?
+
+

Overflow behavior (default: “hidden”)

+

overflowX

+
("auto"|"hidden"|"scroll"|"visible")?
+
+

X-axis overflow (overrides overflow)

+

overflowY

+
("auto"|"hidden"|"scroll"|"visible")?
+
+

Y-axis overflow (overrides overflow)

+

padding

+
{ top: (string|number)?, right: (string|number)?, bottom: (string|number)?, left: (string|number)?, horizontal: (string|number)?, vertical: (string|number)? }?
+
+

Padding around children (default: {top=0, right=0, bottom=0, left=0})

+

parent

+
Element?
+
+

Parent element for hierarchical structure

+

passwordMode

+
boolean?
+
+

Whether to display text as password (default: false, disables multiline)

+

placeholder

+
string?
+
+

Placeholder text when empty (default: nil)

+

positioning

+
Positioning?
+
+

Layout positioning mode: “absolute”|“relative”|“flex”|“grid” (default: RELATIVE)

+

right

+
(string|number)?
+
+

Offset from right edge (CSS-style positioning)

+

rowGap

+
(string|number)?
+
+

Gap between grid rows (default: 0)

+

scaleCorners

+
number?
+
+

Scale multiplier for 9-patch corners/edges. E.g., 2 = 2x size (overrides theme setting)

+

scalingAlgorithm

+
("bilinear"|"nearest")?
+
+

Scaling algorithm for 9-patch corners: “nearest” (sharp/pixelated) or “bilinear” (smooth) (overrides theme setting)

+

scrollSpeed

+
number?
+
+

Pixels per wheel notch (default: 20)

+

scrollable

+
boolean?
+
+

Whether text is scrollable (default: false for single-line, true for multi-line)

+

scrollbarColor

+
Color?
+
+

Scrollbar thumb color (default: Color.new(0.5, 0.5, 0.5, 0.8))

+

scrollbarPadding

+
number?
+
+

Padding between scrollbar and edge (default: 2)

+

scrollbarRadius

+
number?
+
+

Corner radius for scrollbar (default: 6)

+

scrollbarTrackColor

+
Color?
+
+

Scrollbar track color (default: Color.new(0.2, 0.2, 0.2, 0.5))

+

scrollbarWidth

+
number?
+
+

Width of scrollbar track in pixels (default: 12)

+

selectOnFocus

+
boolean?
+
+

Whether to select all text on focus (default: false)

+

selectionColor

+
Color?
+
+

Selection background color (default: nil, uses theme or default)

+

text

+
string?
+
+

Text content to display (default: nil)

+

textAlign

+
TextAlign?
+
+

Alignment of the text content (default: START)

+

textColor

+
Color?
+
+

Color of the text content (default: black or theme text color)

+

textOverflow

+
("clip"|"ellipsis"|"scroll")?
+
+

Text overflow behavior (default: “clip”)

+

textSize

+
(string|number)?
+
+

Font size: number (px), string with units (“2vh”, “10%”), or preset (“xxs”|“xs”|“sm”|“md”|“lg”|“xl”|“xxl”|“3xl”|“4xl”) (default: “md” or 12px)

+

textWrap

+
(boolean|"char"|"word")?
+
+

Text wrapping mode (default: false for single-line, “word” for multi-line)

+

theme

+
string?
+
+

Theme name to use (e.g., “space”, “metal”). Defaults to theme from flexlove.init()

+

themeComponent

+
string?
+
+

Theme component to use (e.g., “panel”, “button”, “input”). If nil, no theme is applied

+

top

+
(string|number)?
+
+

Offset from top edge (CSS-style positioning)

+

transform

+
TransformProps?
+
+

Transform properties for animations and styling

+

transition

+
TransitionProps?
+
+

Transition settings for animations

+

userdata

+
table?
+
+

User-defined data storage for custom properties

+

width

+
(string|number)?
+
+

Width of the element (default: calculated automatically)

+

x

+
(string|number)?
+
+

X coordinate of the element (default: 0)

+

y

+
(string|number)?
+
+

Y coordinate of the element (default: 0)

+

z

+
number?
+
+

Z-index for layering (default: 0)

+
+
+

⚠️ Internal Properties

+
+

Warning: The following properties are internal implementation details and should not be accessed directly. They are prefixed with _ to indicate they are private. Accessing these properties may break in future versions without notice.

+
+
+

_scrollX

+
number?
+
+

Internal: scroll X position (restored in immediate mode)

+

_scrollY

+
number?
+
+

Internal: scroll Y position (restored in immediate mode)

+

EventHandler

+

getState

+
(method) EventHandler:getState()
+  -> State: table
+
+

Get state for persistence (for immediate mode)

+

@return State — data

+

initialize

+
(method) EventHandler:initialize(element: Element)
+
+

Initialize EventHandler with parent element reference

+

@param element — The parent element

+

isAnyButtonPressed

+
(method) EventHandler:isAnyButtonPressed()
+  -> True: boolean
+
+

Check if any mouse button is pressed

+

@return True — if any button is pressed

+

isButtonPressed

+
(method) EventHandler:isButtonPressed(button: number)
+  -> True: boolean
+
+

Check if a specific button is pressed

+

@param button — Mouse button (1=left, 2=right, 3=middle)

+

@return True — if button is pressed

+

new

+
function EventHandler.new(config: table, deps: table)
+  -> EventHandler
+
+

Create a new EventHandler instance

+

@param config — Configuration options

+

@param deps — Dependencies {InputEvent, Context, utils}

+

onEvent

+
fun(element: Element, event: InputEvent)?
+
+

processMouseEvents

+
(method) EventHandler:processMouseEvents(mx: number, my: number, isHovering: boolean, isActiveElement: boolean)
+
+

Process mouse button events in the update cycle

+

@param mx — Mouse X position

+

@param my — Mouse Y position

+

@param isHovering — Whether mouse is over element

+

@param isActiveElement — Whether this is the top element at mouse position

+

processTouchEvents

+
(method) EventHandler:processTouchEvents()
+
+

Process touch events in the update cycle

+

resetScrollbarPressFlag

+
(method) EventHandler:resetScrollbarPressFlag()
+
+

Reset scrollbar press flag (called each frame)

+

setState

+
(method) EventHandler:setState(state: table)
+
+

Restore state from persistence (for immediate mode)

+

@param state — State data

+
+
+

⚠️ Internal Properties

+
+

Warning: The following properties are internal implementation details and should not be accessed directly. They are prefixed with _ to indicate they are private. Accessing these properties may break in future versions without notice.

+
+
+

_Context

+
table
+
+

_InputEvent

+
table
+
+

__index

+
EventHandler
+
+

_clickCount

+
number
+
+

_dragStartX

+
table<number, number>
+
+

_dragStartY

+
table<number, number>
+
+

_element

+
Element?
+
+

_handleMouseDrag

+
(method) EventHandler:_handleMouseDrag(mx: number, my: number, button: number, isHovering: boolean)
+
+

Handle mouse drag (while button is pressed and mouse moves)

+

@param mx — Mouse X position

+

@param my — Mouse Y position

+

@param button — Mouse button

+

@param isHovering — Whether mouse is over element

+

_handleMousePress

+
(method) EventHandler:_handleMousePress(mx: number, my: number, button: number)
+
+

Handle mouse button press

+

@param mx — Mouse X position

+

@param my — Mouse Y position

+

@param button — Mouse button (1=left, 2=right, 3=middle)

+

_handleMouseRelease

+
(method) EventHandler:_handleMouseRelease(mx: number, my: number, button: number)
+
+

Handle mouse button release

+

@param mx — Mouse X position

+

@param my — Mouse Y position

+

@param button — Mouse button

+

_hovered

+
boolean
+
+

_lastClickButton

+
number?
+
+

_lastClickTime

+
number?
+
+

_lastMouseX

+
table<number, number>
+
+

_lastMouseY

+
table<number, number>
+
+

_pressed

+
table<number, boolean>
+
+

_scrollbarPressHandled

+
boolean
+
+

_touchPressed

+
table<number, boolean>
+
+

_utils

+
table
+
+

FlexLove

+

Animation

+
Animation
+
+

Color

+
Color
+
+

Utility class for color handling

+

Theme

+
Theme
+
+

baseScale

+
table
+
+

beginFrame

+
function FlexLove.beginFrame()
+
+

Begin a new immediate mode frame

+

clearAllStates

+
function FlexLove.clearAllStates()
+
+

Clear all immediate mode states

+

clearState

+
function FlexLove.clearState(id: string)
+
+

Clear state for a specific element ID

+

defaultTheme

+
(string|ThemeDefinition)?
+
+

destroy

+
function FlexLove.destroy()
+
+

draw

+
function FlexLove.draw(gameDrawFunc: function|nil, postDrawFunc: function|nil)
+
+

endFrame

+
function FlexLove.endFrame()
+
+

enums

+
unknown
+
+

getElementAtPosition

+
function FlexLove.getElementAtPosition(x: number, y: number)
+  -> Element?
+
+

Find the topmost element at given coordinates

+

getMode

+
function FlexLove.getMode()
+  -> "immediate"|"retained"
+
+
return #1:
+    | "immediate"
+    | "retained"
+
+

getStateCount

+
function FlexLove.getStateCount()
+  -> number
+
+

getStateStats

+
function FlexLove.getStateStats()
+  -> table
+
+

Get state statistics (for debugging)

+

init

+
function FlexLove.init(config: { baseScale: { width: number?, height: number? }?, theme: (string|ThemeDefinition)?, immediateMode: boolean?, stateRetentionFrames: number?, maxStateEntries: number?, autoFrameManagement: boolean? })
+
+

keypressed

+
function FlexLove.keypressed(key: string, scancode: string, isrepeat: boolean)
+
+

new

+
function FlexLove.new(props: ElementProps)
+  -> Element
+
+

resize

+
function FlexLove.resize()
+
+

scaleFactors

+
table
+
+

setMode

+
function FlexLove.setMode(mode: "immediate"|"retained")
+
+
mode:
+    | "immediate"
+    | "retained"
+
+

textinput

+
function FlexLove.textinput(text: string)
+
+

topElements

+
table
+
+

update

+
function FlexLove.update(dt: any)
+
+

wheelmoved

+
function FlexLove.wheelmoved(dx: any, dy: any)
+
+
+
+

⚠️ Internal Properties

+
+

Warning: The following properties are internal implementation details and should not be accessed directly. They are prefixed with _ to indicate they are private. Accessing these properties may break in future versions without notice.

+
+
+

_DESCRIPTION

+
string
+
+

_LICENSE

+
string
+
+

_URL

+
string
+
+

_VERSION

+
string
+
+

Add version and metadata

+

_activeEventElement

+
Element?
+
+

_autoBeganFrame

+
boolean
+
+

_autoFrameManagement

+
boolean
+
+

_backdropCanvas

+
nil
+
+

A Canvas is used for off-screen rendering. Think of it as an invisible screen that you can draw to, but that will not be visible until you draw it to the actual visible screen. It is also known as “render to texture”.

+

By drawing things that do not change position often (such as background items) to the Canvas, and then drawing the entire Canvas instead of each item, you can reduce the number of draw operations performed each frame.

+

In versions prior to love.graphics.isSupported(“canvas”) could be used to check for support at runtime.

+

Open in Browser

+

_cachedViewport

+
table
+
+

_canvasDimensions

+
table
+
+

_currentFrameElements

+
table
+
+

_focusedElement

+
nil
+
+

_frameNumber

+
integer
+
+

_frameStarted

+
boolean
+
+

_gameCanvas

+
nil
+
+

A Canvas is used for off-screen rendering. Think of it as an invisible screen that you can draw to, but that will not be visible until you draw it to the actual visible screen. It is also known as “render to texture”.

+

By drawing things that do not change position often (such as background items) to the Canvas, and then drawing the entire Canvas instead of each item, you can reduce the number of draw operations performed each frame.

+

In versions prior to love.graphics.isSupported(“canvas”) could be used to check for support at runtime.

+

Open in Browser

+

_immediateMode

+
boolean
+
+

_immediateModeState

+
unknown
+
+

FontFamily

+

path

+
string
+
+

Path to the font file (relative to FlexLove or absolute)

+
+
+

⚠️ Internal Properties

+
+

Warning: The following properties are internal implementation details and should not be accessed directly. They are prefixed with _ to indicate they are private. Accessing these properties may break in future versions without notice.

+
+
+

_loadedFont

+
(love.Font)?
+
+

Internal: cached loaded font

+

InputEvent

+

button

+
number
+
+

Mouse button: 1 (left), 2 (right), 3 (middle)

+

clickCount

+
number
+
+

Number of clicks (for double/triple click detection)

+

dx

+
number?
+
+

Delta X from drag start (only for drag events)

+

dy

+
number?
+
+

Delta Y from drag start (only for drag events)

+

modifiers

+
{ shift: boolean, ctrl: boolean, alt: boolean, super: boolean }
+
+

new

+
function InputEvent.new(props: InputEventProps)
+  -> InputEvent
+
+

Create a new input event

+

timestamp

+
number
+
+

Time when event occurred

+

type

+
"click"|"drag"|"middleclick"|"press"|"release"...(+1)
+
+

x

+
number
+
+

Mouse X position

+

y

+
number
+
+

Mouse Y position

+
+
+

⚠️ Internal Properties

+
+

Warning: The following properties are internal implementation details and should not be accessed directly. They are prefixed with _ to indicate they are private. Accessing these properties may break in future versions without notice.

+
+
+

__index

+
InputEvent
+
+

TextEditor

+

allowNewlines

+
boolean
+
+

allowTabs

+
boolean
+
+

autoGrow

+
boolean
+
+

blur

+
(method) TextEditor:blur()
+
+

Remove focus from this element

+

clearSelection

+
(method) TextEditor:clearSelection()
+
+

Clear selection

+

cursorBlinkRate

+
number
+
+

cursorColor

+
Color?
+
+

Utility class for color handling

+

customSanitizer

+
function?
+
+

deleteSelection

+
(method) TextEditor:deleteSelection()
+  -> boolean
+
+

Delete selected text

+

@return — True if text was deleted

+

deleteText

+
(method) TextEditor:deleteText(startPos: number, endPos: number)
+
+

Delete text in range

+

@param startPos — Start position (inclusive)

+

@param endPos — End position (inclusive)

+

editable

+
boolean
+
+

focus

+
(method) TextEditor:focus()
+
+

Focus this element for keyboard input

+

getCursorPosition

+
(method) TextEditor:getCursorPosition()
+  -> number
+
+

Get cursor position

+

@return — Character index (0-based)

+

getSelectedText

+
(method) TextEditor:getSelectedText()
+  -> string?
+
+

Get selected text

+

@return — Selected text or nil if no selection

+

getSelection

+
(method) TextEditor:getSelection()
+  -> number?
+  2. number?
+
+

Get selection range

+

@return — Start and end positions, or nil if no selection

+

getText

+
(method) TextEditor:getText()
+  -> string
+
+

Get current text buffer

+

handleKeyPress

+
(method) TextEditor:handleKeyPress(key: string, scancode: string, isrepeat: boolean)
+
+

Handle key press (special keys)

+

@param key — Key name

+

@param scancode — Scancode

+

@param isrepeat — Whether this is a key repeat

+

handleTextClick

+
(method) TextEditor:handleTextClick(mouseX: number, mouseY: number, clickCount: number)
+
+

Handle mouse click on text

+

@param clickCount — 1=single, 2=double, 3=triple

+

handleTextDrag

+
(method) TextEditor:handleTextDrag(mouseX: number, mouseY: number)
+
+

Handle mouse drag for text selection

+

handleTextInput

+
(method) TextEditor:handleTextInput(text: string)
+
+

Handle text input (character insertion)

+

hasSelection

+
(method) TextEditor:hasSelection()
+  -> boolean
+
+

Check if there is an active selection

+

initialize

+
(method) TextEditor:initialize(element: table)
+
+

Initialize TextEditor with parent element reference

+

@param element — The parent Element instance

+

inputType

+
"email"|"number"|"text"|"url"
+
+

insertText

+
(method) TextEditor:insertText(text: string, position?: number, skipSanitization?: boolean)
+
+

Insert text at position

+

@param text — Text to insert

+

@param position — Position to insert at (default: cursor position)

+

@param skipSanitization — Skip sanitization (for internal use)

+

isFocused

+
(method) TextEditor:isFocused()
+  -> boolean
+
+

Check if this element is focused

+

maxLength

+
number?
+
+

maxLines

+
number?
+
+

mouseToTextPosition

+
(method) TextEditor:mouseToTextPosition(mouseX: number, mouseY: number)
+  -> number
+
+

Convert mouse coordinates to cursor position in text

+

@param mouseX — Mouse X coordinate (absolute)

+

@param mouseY — Mouse Y coordinate (absolute)

+

@return — Cursor position (character index)

+

moveCursorBy

+
(method) TextEditor:moveCursorBy(delta: number)
+
+

Move cursor by delta characters

+

@param delta — Number of characters to move (positive or negative)

+

moveCursorToEnd

+
(method) TextEditor:moveCursorToEnd()
+
+

Move cursor to end of text

+

moveCursorToLineEnd

+
(method) TextEditor:moveCursorToLineEnd()
+
+

Move cursor to end of current line

+

moveCursorToLineStart

+
(method) TextEditor:moveCursorToLineStart()
+
+

Move cursor to start of current line

+

moveCursorToNextWord

+
(method) TextEditor:moveCursorToNextWord()
+
+

Move cursor to start of next word

+

moveCursorToPreviousWord

+
(method) TextEditor:moveCursorToPreviousWord()
+
+

Move cursor to start of previous word

+

moveCursorToStart

+
(method) TextEditor:moveCursorToStart()
+
+

Move cursor to start of text

+

multiline

+
boolean
+
+

new

+
function TextEditor.new(config: TextEditorConfig, deps: table)
+  -> TextEditor: table
+
+

Create a new TextEditor instance

+

@param deps — Dependencies {Context, StateManager, Color, utils}

+

@return TextEditor — instance

+

onBlur

+
fun(element: Element)?
+
+

onEnter

+
fun(element: Element)?
+
+

onFocus

+
fun(element: Element)?
+
+

onSanitize

+
fun(element: Element, original: string, sanitized: string)?
+
+

onTextChange

+
fun(element: Element, text: string)?
+
+

onTextInput

+
fun(element: Element, text: string)?
+
+

passwordMode

+
boolean
+
+

placeholder

+
string?
+
+

replaceText

+
(method) TextEditor:replaceText(startPos: number, endPos: number, newText: string)
+
+

Replace text in range

+

@param startPos — Start position (inclusive)

+

@param endPos — End position (inclusive)

+

@param newText — Replacement text

+

sanitize

+
boolean
+
+

scrollable

+
boolean
+
+

selectAll

+
(method) TextEditor:selectAll()
+
+

Select all text

+

selectOnFocus

+
boolean
+
+

selectionColor

+
Color?
+
+

Utility class for color handling

+

setCursorPosition

+
(method) TextEditor:setCursorPosition(position: number)
+
+

Set cursor position

+

@param position — Character index (0-based)

+

setSelection

+
(method) TextEditor:setSelection(startPos: number, endPos: number)
+
+

Set selection range

+

@param startPos — Start position (inclusive)

+

@param endPos — End position (inclusive)

+

setText

+
(method) TextEditor:setText(text: string, skipSanitization?: boolean)
+
+

Set text buffer and mark dirty

+

@param skipSanitization — Skip sanitization (for trusted input)

+

textOverflow

+
"clip"|"ellipsis"|"scroll"
+
+

textWrap

+
boolean|"char"|"word"
+
+

update

+
(method) TextEditor:update(dt: number)
+
+

Update cursor blink animation

+

@param dt — Delta time

+

updateAutoGrowHeight

+
(method) TextEditor:updateAutoGrowHeight()
+
+

Update element height based on text content (for autoGrow)

+
+
+

⚠️ Internal Properties

+
+

Warning: The following properties are internal implementation details and should not be accessed directly. They are prefixed with _ to indicate they are private. Accessing these properties may break in future versions without notice.

+
+
+

_Color

+
table
+
+

_Context

+
table
+
+

_FONT_CACHE

+
table
+
+

_StateManager

+
table
+
+

__index

+
TextEditor
+
+

_calculateWrapping

+
(method) TextEditor:_calculateWrapping()
+
+

Calculate text wrapping

+

_cursorBlinkPauseTimer

+
number
+
+

_cursorBlinkPaused

+
boolean
+
+

_cursorBlinkTimer

+
number
+
+

_cursorColumn

+
number
+
+

_cursorLine

+
number
+
+

_cursorPosition

+
number
+
+

_cursorVisible

+
boolean
+
+

_element

+
Element?
+
+

_focused

+
boolean
+
+

_getCursorScreenPosition

+
(method) TextEditor:_getCursorScreenPosition()
+  -> number
+  2. number
+
+

Get cursor screen position for rendering (handles multiline text)

+

@return — Cursor X and Y position relative to content area

+

_getFont

+
(method) TextEditor:_getFont()
+  -> (love.Font)?
+
+

Get font for text rendering

+

_getModifiers

+
function
+
+

_getSelectionRects

+
(method) TextEditor:_getSelectionRects(selStart: number, selEnd: number)
+  -> table
+
+

Get selection rectangles for rendering

+

@param selStart — Selection start position

+

@param selEnd — Selection end position

+

@return — Array of rectangles {x, y, width, height}

+

_lines

+
table?
+
+

_markTextDirty

+
(method) TextEditor:_markTextDirty()
+
+

Mark text as dirty (needs recalculation)

+

_mouseDownPosition

+
number
+
+ +
(method) TextEditor:_resetCursorBlink(pauseBlink: boolean|nil)
+
+

Reset cursor blink (show cursor immediately)

+

@param pauseBlink — Whether to pause blinking (for typing)

+

_sanitizeText

+
(method) TextEditor:_sanitizeText(text: string)
+  -> string
+
+

Internal: Sanitize text input

+

@param text — Text to sanitize

+

@return — Sanitized text

+

_saveState

+
(method) TextEditor:_saveState()
+
+

Save state to StateManager (for immediate mode)

+

_selectWordAtPosition

+
(method) TextEditor:_selectWordAtPosition(position: number)
+
+

Select word at given position

+

_selectionAnchor

+
number?
+
+

_selectionEnd

+
number?
+
+

_selectionStart

+
number?
+
+

_splitLines

+
(method) TextEditor:_splitLines()
+
+

Split text into lines (for multi-line text)

+

_textBuffer

+
string
+
+

_textDirty

+
boolean
+
+

_textDragOccurred

+
boolean?
+
+

_textScrollX

+
number
+
+

_updateTextIfDirty

+
(method) TextEditor:_updateTextIfDirty()
+
+

Update text if dirty (recalculate lines and wrapping)

+

_updateTextScroll

+
(method) TextEditor:_updateTextScroll()
+
+

Update text scroll offset to keep cursor visible

+

_utils

+
table
+
+

_validateCursorPosition

+
(method) TextEditor:_validateCursorPosition()
+
+

Validate cursor position (ensure it’s within text bounds)

+

_wrapLine

+
(method) TextEditor:_wrapLine(line: string, maxWidth: number)
+  -> table
+
+

Wrap a single line of text

+

@param line — Line to wrap

+

@param maxWidth — Maximum width in pixels

+

@return — Array of wrapped line parts

+

_wrappedLines

+
table?
+
+

TextEditorConfig

+

allowNewlines

+
boolean?
+
+

Whether to allow newline characters (default: true in multiline)

+

allowTabs

+
boolean?
+
+

Whether to allow tab characters (default: true)

+

autoGrow

+
boolean
+
+

Whether element auto-grows with text

+

cursorBlinkRate

+
number
+
+

Cursor blink rate in seconds

+

cursorColor

+
Color?
+
+

Cursor color

+

customSanitizer

+
function?
+
+

Custom sanitization function

+

editable

+
boolean
+
+

Whether text is editable

+

inputType

+
"email"|"number"|"text"|"url"
+
+

Input validation type

+

maxLength

+
number?
+
+

Maximum text length in characters

+

maxLines

+
number?
+
+

Maximum number of lines

+

multiline

+
boolean
+
+

Whether multi-line is supported

+

passwordMode

+
boolean
+
+

Whether to mask text

+

placeholder

+
string?
+
+

Placeholder text when empty

+

sanitize

+
boolean?
+
+

Whether to sanitize text input (default: true)

+

scrollable

+
boolean
+
+

Whether text is scrollable

+

selectOnFocus

+
boolean
+
+

Whether to select all text on focus

+

selectionColor

+
Color?
+
+

Selection background color

+

textOverflow

+
"clip"|"ellipsis"|"scroll"
+
+

Text overflow behavior

+

textWrap

+
boolean|"char"|"word"
+
+

Text wrapping mode

+
+

Theme

+

Manager

+
ThemeManager
+
+

Export both Theme and ThemeManager

+

atlas

+
(love.Image)?
+
+

Optional: global atlas

+

atlasData

+
(love.ImageData)?
+
+

Raw (decoded) image data.

+

You can’t draw ImageData directly to screen. See Image for that.

+

Open in Browser

+

colors

+
table<string, Color>
+
+

components

+
table<string, ThemeComponent>
+
+

contentAutoSizingMultiplier

+
{ width: number?, height: number? }?
+
+

Optional: default multiplier for auto-sized content dimensions

+

fonts

+
table<string, string>
+
+

Font family definitions

+

get

+
function Theme.get(themeName: string)
+  -> Theme|nil
+
+

Get a theme by name

+

@param themeName — Name of the theme

+

@return — Returns theme or nil if not found

+

getActive

+
function Theme.getActive()
+  -> Theme?
+
+

Get the active theme

+

getAllColors

+
function Theme.getAllColors()
+  -> table<string, Color>|nil
+
+

Get all colors from the active theme

+

@return — Table of all colors, or nil if no theme active

+

getColor

+
function Theme.getColor(colorName: string)
+  -> Color?
+
+

Get a color from the active theme

+

@param colorName — Name of the color (e.g., “primary”, “secondary”)

+

@return — Returns Color instance or nil if not found

+

getColorNames

+
function Theme.getColorNames()
+  -> table<string>|nil
+
+

Get all available color names from the active theme

+

@return — Array of color names, or nil if no theme active

+

getColorOrDefault

+
function Theme.getColorOrDefault(colorName: string, fallback: Color|nil)
+  -> Color
+
+

Get a color with a fallback if not found

+

@param colorName — Name of the color to retrieve

+

@param fallback — Fallback color if not found (default: white)

+

@return — The color or fallback

+

getComponent

+
function Theme.getComponent(componentName: string, state?: string)
+  -> ThemeComponent?
+
+

Get a component from the active theme

+

@param componentName — Name of the component (e.g., “button”, “panel”)

+

@param state — Optional state (e.g., “hover”, “pressed”, “disabled”)

+

@return — Returns component or nil if not found

+

getFont

+
function Theme.getFont(fontName: string)
+  -> string?
+
+

Get a font from the active theme

+

@param fontName — Name of the font family (e.g., “default”, “heading”)

+

@return — Returns font path or nil if not found

+

getRegisteredThemes

+
function Theme.getRegisteredThemes()
+  -> table<string>
+
+

Get all registered theme names

+

@return — Array of theme names

+

hasActive

+
function Theme.hasActive()
+  -> boolean
+
+

Check if a theme is currently active

+

@return — Returns true if a theme is active

+

load

+
function Theme.load(path: string)
+  -> Theme
+
+

Load a theme from a Lua file

+

@param path — Path to theme definition file (e.g., “space” or “mytheme”)

+

name

+
string
+
+

new

+
function Theme.new(definition: any)
+  -> Theme
+
+

sanitizeTheme

+
function Theme.sanitizeTheme(theme?: table)
+  -> sanitized: table
+
+

Sanitize a theme definition by removing invalid values and providing defaults

+

@param theme — The theme to sanitize

+

@return sanitized — The sanitized theme

+

setActive

+
function Theme.setActive(themeOrName: string|Theme)
+
+

validateTheme

+
function Theme.validateTheme(theme?: table, options?: table)
+  -> valid: boolean
+  2. errors: table
+
+

Validate a theme definition for structural correctness (non-aggressive)

+

@param theme — The theme to validate

+

@param options — Optional validation options {strict: boolean}

+

@return valid,errors — List of validation errors

+
+
+

⚠️ Internal Properties

+
+

Warning: The following properties are internal implementation details and should not be accessed directly. They are prefixed with _ to indicate they are private. Accessing these properties may break in future versions without notice.

+
+
+

__index

+
Theme
+
+

ThemeComponent

+

atlas

+
(string|love.Image)?
+
+

Optional: component-specific atlas (overrides theme atlas). Files ending in .9.png are auto-parsed

+

contentAutoSizingMultiplier

+
{ width: number?, height: number? }?
+
+

Optional: multiplier for auto-sized content dimensions

+

insets

+
{ left: number, top: number, right: number, bottom: number }?
+
+

Optional: 9-patch insets (auto-extracted from .9.png files or manually defined)

+

regions

+
{ topLeft: ThemeRegion, topCenter: ThemeRegion, topRight: ThemeRegion, middleLeft: ThemeRegion, middleCenter: ThemeRegion, middleRight: ThemeRegion, bottomLeft: ThemeRegion, bottomCenter: ThemeRegion, bottomRight: ThemeRegion }
+
+

scaleCorners

+
number?
+
+

Optional: scale multiplier for non-stretched regions (corners/edges). E.g., 2 = 2x size. Default: nil (no scaling)

+

scalingAlgorithm

+
("bilinear"|"nearest")?
+
+

Optional: scaling algorithm for non-stretched regions. Default: “bilinear”

+

states

+
table<string, ThemeComponent>?
+
+

stretch

+
{ horizontal: table<integer, string>, vertical: table<integer, string> }
+
+
+
+

⚠️ Internal Properties

+
+

Warning: The following properties are internal implementation details and should not be accessed directly. They are prefixed with _ to indicate they are private. Accessing these properties may break in future versions without notice.

+
+
+

_loadedAtlas

+
(string|love.Image)?
+
+

Internal: cached loaded atlas image

+

_loadedAtlasData

+
(love.ImageData)?
+
+

Internal: cached loaded atlas ImageData for pixel access

+

_ninePatchData

+
{ insets: table, contentPadding: table, stretchX: table, stretchY: table }?
+
+

Internal: parsed 9-patch data with stretch regions and content padding

+

_scaledRegionCache

+
table<string, love.Image>?
+
+

Internal: cache for scaled corner/edge images

+

ThemeDefinition

+

atlas

+
(string|love.Image)?
+
+

Optional: global atlas (can be overridden per component)

+

colors

+
table<string, Color>?
+
+

components

+
table<string, ThemeComponent>
+
+

contentAutoSizingMultiplier

+
{ width: number?, height: number? }?
+
+

Optional: default multiplier for auto-sized content dimensions

+

fonts

+
table<string, string>?
+
+

Optional: font family definitions (name -> path)

+

name

+
string
+
+
+

TransformProps

+

rotate

+
number?
+
+

scale

+
{ x: number, y: number }?
+
+

skew

+
{ x: number, y: number }?
+
+

translate

+
{ x: number, y: number }?
+
+
+

TransitionProps

+

duration

+
number?
+
+

easing

+
string?
+
+
+ +
+
+ + ↑ Top + + + + \ No newline at end of file diff --git a/generate_docs.sh b/generate_docs.sh deleted file mode 100755 index 7a90c02..0000000 --- a/generate_docs.sh +++ /dev/null @@ -1,40 +0,0 @@ -#!/bin/bash - -# FlexLöve Documentation Generator -# This script generates HTML documentation from LuaLS annotations - -echo "Generating FlexLöve documentation..." - -# Get the directory where this script is located -SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" -cd "$SCRIPT_DIR" - -# Check if lua-language-server is installed -if ! command -v lua-language-server &> /dev/null; then - echo "Error: lua-language-server not found. Please install it first." - echo " macOS: brew install lua-language-server" - echo " Linux: See https://github.com/LuaLS/lua-language-server" - exit 1 -fi - -# Create docs directory if it doesn't exist -mkdir -p docs - -# Generate documentation using lua-language-server -echo "Running lua-language-server documentation export..." -lua-language-server \ - --doc="$SCRIPT_DIR" \ - --doc_out_path="$SCRIPT_DIR/docs" - -if [ $? -eq 0 ]; then - echo "✓ Documentation generated successfully!" - echo " - docs/doc.md (Markdown format)" - echo " - docs/doc.json (JSON format)" - echo " - docs/index.html (GitHub Pages)" - echo "" - echo "To view locally, open: file://$SCRIPT_DIR/docs/index.html" - echo "To publish, commit the docs/ directory and enable GitHub Pages." -else - echo "✗ Documentation generation failed" - exit 1 -fi diff --git a/scripts/archive-docs.sh b/scripts/archive-docs.sh new file mode 100755 index 0000000..90146c9 --- /dev/null +++ b/scripts/archive-docs.sh @@ -0,0 +1,58 @@ +#!/bin/bash + +set -e + +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +echo -e "${BLUE}FlexLöve Documentation Archival${NC}" +echo "" + +VERSION=$(grep -m 1 "_VERSION" FlexLove.lua | sed -E 's/.*"([^"]+)".*/\1/') +if [ -z "$VERSION" ]; then + echo -e "${RED}Error: Could not extract version from FlexLove.lua${NC}" + exit 1 +fi + +echo -e "${GREEN}Version detected: ${VERSION}${NC}" + +if [ ! -f "docs/api.html" ]; then + echo -e "${RED}Error: docs/api.html not found${NC}" + echo "Please run ./scripts/generate_docs.sh first" + exit 1 +fi + +VERSION_DIR="docs/versions/v${VERSION}" +if [ -d "$VERSION_DIR" ]; then + echo -e "${YELLOW}Warning: $VERSION_DIR already exists${NC}" + read -p "Overwrite? (y/n) " -n 1 -r + echo + if [[ ! $REPLY =~ ^[Yy]$ ]]; then + echo -e "${RED}Aborted${NC}" + exit 1 + fi +else + echo -e "${YELLOW}Creating version directory...${NC}" + mkdir -p "$VERSION_DIR" +fi + +echo -e "${YELLOW}Archiving documentation...${NC}" +cp docs/api.html "$VERSION_DIR/api.html" + +if [ ! -f "$VERSION_DIR/api.html" ]; then + echo -e "${RED}Error: Failed to copy documentation${NC}" + exit 1 +fi + +FILE_SIZE=$(du -h "$VERSION_DIR/api.html" | cut -f1) + +echo "" +echo -e "${GREEN}✓ Documentation archived successfully!${NC}" +echo "" +echo -e " ${BLUE}Version:${NC} v${VERSION}" +echo -e " ${BLUE}Location:${NC} $VERSION_DIR/api.html" +echo -e " ${BLUE}Size:${NC} $FILE_SIZE" +echo "" diff --git a/scripts/create-release.sh b/scripts/create-release.sh new file mode 100755 index 0000000..51d6c2f --- /dev/null +++ b/scripts/create-release.sh @@ -0,0 +1,99 @@ +#!/bin/bash + +set -e + +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +echo -e "${BLUE}FlexLöve Release Builder${NC}" +echo "" + +VERSION=$(grep -m 1 "_VERSION" FlexLove.lua | sed -E 's/.*"([^"]+)".*/\1/') +if [ -z "$VERSION" ]; then + echo -e "${RED}Error: Could not extract version from FlexLove.lua${NC}" + exit 1 +fi + +echo -e "${GREEN}Version detected: ${VERSION}${NC}" + +# Create releases directory if it doesn't exist +RELEASE_DIR="releases" +if [ ! -d "$RELEASE_DIR" ]; then + echo -e "${YELLOW}Creating releases directory...${NC}" + mkdir -p "$RELEASE_DIR" +fi + +OUTPUT_FILE="${RELEASE_DIR}/flexlove-v${VERSION}.zip" + +CHECKSUM_FILE="${OUTPUT_FILE}.sha256" +if [ -f "$OUTPUT_FILE" ] || [ -f "$CHECKSUM_FILE" ]; then + echo -e "${YELLOW}Warning: Release files already exist - overwriting${NC}" + [ -f "$OUTPUT_FILE" ] && echo " - $OUTPUT_FILE" && rm "$OUTPUT_FILE" + [ -f "$CHECKSUM_FILE" ] && echo " - $CHECKSUM_FILE" && rm "$CHECKSUM_FILE" +fi + +TEMP_DIR=$(mktemp -d) +BUILD_DIR="${TEMP_DIR}/flexlove" + +echo -e "${YELLOW}Creating release package...${NC}" + +mkdir -p "$BUILD_DIR" + +echo " → Copying FlexLove.lua" +cp FlexLove.lua "$BUILD_DIR/" + +echo " → Copying modules/" +cp -r modules "$BUILD_DIR/" + +echo " → Copying LICENSE" +cp LICENSE "$BUILD_DIR/" + +echo " → Creating README.md" +cp README.md "$BUILD_DIR/" + +echo -e "${YELLOW}Creating zip archive...${NC}" + +ABS_OUTPUT_FILE="$(cd "$(dirname "$OUTPUT_FILE")" && pwd)/$(basename "$OUTPUT_FILE")" + +cd "$TEMP_DIR" +zip -r -q "flexlove-v${VERSION}.zip" flexlove/ + +mv "flexlove-v${VERSION}.zip" "$ABS_OUTPUT_FILE" +cd - > /dev/null + +echo -e "${YELLOW}Generating SHA256 checksum...${NC}" +CHECKSUM_FILE="${OUTPUT_FILE}.sha256" +cd "$RELEASE_DIR" +shasum -a 256 "flexlove-v${VERSION}.zip" > "flexlove-v${VERSION}.zip.sha256" +cd - > /dev/null + +CHECKSUM=$(cat "$CHECKSUM_FILE" | cut -d ' ' -f 1) + +echo -e "${YELLOW}Cleaning up...${NC}" +rm -rf "$TEMP_DIR" + +FILE_SIZE=$(du -h "$OUTPUT_FILE" | cut -f1) + +echo "" +echo -e "${GREEN}✓ Release created successfully!${NC}" +echo "" +echo -e " ${BLUE}File:${NC} $OUTPUT_FILE" +echo -e " ${BLUE}Size:${NC} $FILE_SIZE" +echo -e " ${BLUE}Version:${NC} $VERSION" +echo -e " ${BLUE}SHA256:${NC} $CHECKSUM" +echo "" +echo -e "Contents:" +echo " - FlexLove.lua" +echo " - modules/ (27 files)" +echo " - LICENSE" +echo " - README.txt" +echo "" +echo -e "Files created:" +echo " - $OUTPUT_FILE" +echo " - $CHECKSUM_FILE" +echo "" +echo -e "${YELLOW}Verify checksum:${NC}" +echo " cd releases && shasum -a 256 -c flexlove-v${VERSION}.zip.sha256" diff --git a/scripts/generate_docs.sh b/scripts/generate_docs.sh new file mode 100755 index 0000000..d33d17c --- /dev/null +++ b/scripts/generate_docs.sh @@ -0,0 +1,72 @@ +#!/bin/bash + +YELLOW='\033[1;33m' +GREEN='\033[0;32m' +NC='\033[0m' + +echo "Generating FlexLöve documentation..." + +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +PROJECT_ROOT="$( cd "$SCRIPT_DIR/.." && pwd )" +cd "$PROJECT_ROOT" + +if [ -f "docs/api.html" ]; then + echo -e "${YELLOW}Checking for previous documentation version...${NC}" + OLD_VERSION=$(grep -o 'FlexLöve v[0-9.]*' docs/api.html | head -1 | sed 's/FlexLöve v//') + CURRENT_VERSION=$(grep -m 1 "_VERSION" FlexLove.lua | sed -E 's/.*"([^"]+)".*/\1/') + + if [ -n "$OLD_VERSION" ] && [ "$OLD_VERSION" != "$CURRENT_VERSION" ]; then + echo -e "${YELLOW}Found previous version v${OLD_VERSION}, archiving before generating new docs...${NC}" + mkdir -p "docs/versions/v${OLD_VERSION}" + cp docs/api.html "docs/versions/v${OLD_VERSION}/api.html" + echo -e "${GREEN}✓ Archived previous documentation to docs/versions/v${OLD_VERSION}/${NC}" + elif [ -n "$OLD_VERSION" ] && [ "$OLD_VERSION" = "$CURRENT_VERSION" ]; then + echo -e "${YELLOW}Same version (v${OLD_VERSION}), will overwrite current documentation${NC}" + fi +fi + +if ! command -v lua-language-server &> /dev/null; then + echo "Error: lua-language-server not found. Please install it first." + echo " macOS: brew install lua-language-server" + echo " Linux: See https://github.com/LuaLS/lua-language-server" + exit 1 +fi + +mkdir -p docs + +echo "Running lua-language-server documentation export..." +lua-language-server \ + --doc="$PROJECT_ROOT" \ + --doc_out_path="$PROJECT_ROOT/docs" + +if [ $? -eq 0 ]; then + echo "✓ Markdown documentation generated" + + echo "Building beautiful HTML documentation..." + cd "$PROJECT_ROOT/docs" + + if [ ! -d "node_modules" ]; then + echo "Installing Node.js dependencies..." + npm install --silent + fi + + npm run build --silent + + if [ $? -eq 0 ]; then + echo -e "${GREEN}✓ HTML documentation built successfully!${NC}" + echo "" + echo "Generated files:" + echo " - docs/api.html (Beautiful, searchable API reference)" + echo " - docs/index.html (Landing page)" + echo " - docs/doc.md (Raw markdown)" + if [ -n "$OLD_VERSION" ] && [ "$OLD_VERSION" != "$CURRENT_VERSION" ]; then + echo " - docs/versions/v${OLD_VERSION}/api.html (Previous version archived)" + fi + else + echo "✗ HTML build failed, but markdown docs are available" + exit 1 + fi +else + echo "✗ Documentation generation failed" + exit 1 +fi diff --git a/scripts/make-tag.sh b/scripts/make-tag.sh new file mode 100755 index 0000000..767127f --- /dev/null +++ b/scripts/make-tag.sh @@ -0,0 +1,177 @@ +#!/bin/bash + +set -e + +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +CYAN='\033[0;36m' +NC='\033[0m' # No Color + +echo -e "${BLUE}═══════════════════════════════════════${NC}" +echo -e "${BLUE} FlexLöve Version Bump & Tag Tool ${NC}" +echo -e "${BLUE}═══════════════════════════════════════${NC}" +echo "" + +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +PROJECT_ROOT="$( cd "$SCRIPT_DIR/.." && pwd )" +cd "$PROJECT_ROOT" + +if [ ! -d .git ] && [ ! -f .git ]; then + echo -e "${RED}Error: Not in a git repository${NC}" + exit 1 +fi + +if ! git diff-index --quiet HEAD --; then + echo -e "${YELLOW}Warning: You have uncommitted changes${NC}" + git status --short + echo "" + read -p "Continue anyway? (y/n) " -n 1 -r + echo + if [[ ! $REPLY =~ ^[Yy]$ ]]; then + echo -e "${RED}Aborted${NC}" + exit 1 + fi + echo "" +fi + +CURRENT_VERSION=$(grep -m 1 "_VERSION" FlexLove.lua | sed -E 's/.*"([^"]+)".*/\1/') +if [ -z "$CURRENT_VERSION" ]; then + echo -e "${RED}Error: Could not extract version from FlexLove.lua${NC}" + exit 1 +fi + +echo -e "${CYAN}Current version:${NC} ${GREEN}v${CURRENT_VERSION}${NC}" +echo "" + +IFS='.' read -r MAJOR MINOR PATCH <<< "$CURRENT_VERSION" + +MAJOR=$(echo "$MAJOR" | sed 's/[^0-9].*//') +MINOR=$(echo "$MINOR" | sed 's/[^0-9].*//') +PATCH=$(echo "$PATCH" | sed 's/[^0-9].*//') + +echo -e "${CYAN}Select version bump type:${NC}" +echo " 1) Major (breaking changes) ${MAJOR}.${MINOR}.${PATCH} → $((MAJOR+1)).0.0" +echo " 2) Minor (new features) ${MAJOR}.${MINOR}.${PATCH} → ${MAJOR}.$((MINOR+1)).0" +echo " 3) Patch (bug fixes) ${MAJOR}.${MINOR}.${PATCH} → ${MAJOR}.${MINOR}.$((PATCH+1))" +echo " 4) Custom version" +echo " 5) Cancel" +echo "" +read -p "Enter choice (1-5): " -n 1 -r CHOICE +echo "" +echo "" + +case $CHOICE in + 1) + NEW_MAJOR=$((MAJOR+1)) + NEW_MINOR=0 + NEW_PATCH=0 + BUMP_TYPE="major" + ;; + 2) + NEW_MAJOR=$MAJOR + NEW_MINOR=$((MINOR+1)) + NEW_PATCH=0 + BUMP_TYPE="minor" + ;; + 3) + NEW_MAJOR=$MAJOR + NEW_MINOR=$MINOR + NEW_PATCH=$((PATCH+1)) + BUMP_TYPE="patch" + ;; + 4) + read -p "Enter custom version (e.g., 1.0.0-beta): " CUSTOM_VERSION + NEW_VERSION="$CUSTOM_VERSION" + BUMP_TYPE="custom" + ;; + 5) + echo -e "${RED}Cancelled${NC}" + exit 0 + ;; + *) + echo -e "${RED}Invalid choice${NC}" + exit 1 + ;; +esac + +# Construct new version (unless custom) +if [ "$BUMP_TYPE" != "custom" ]; then + NEW_VERSION="${NEW_MAJOR}.${NEW_MINOR}.${NEW_PATCH}" +fi + +echo -e "${CYAN}New version:${NC} ${GREEN}v${NEW_VERSION}${NC}" +echo "" +echo -e "${YELLOW}This will:${NC}" +echo " 1. Update FlexLove.lua → flexlove._VERSION = \"${NEW_VERSION}\"" +echo " 2. Update README.md → first line version" +echo " 3. Stage changes for commit" +echo " 4. Create git tag v${NEW_VERSION}" +echo "" +read -p "Proceed? (y/n) " -n 1 -r +echo "" +if [[ ! $REPLY =~ ^[Yy]$ ]]; then + echo -e "${RED}Aborted${NC}" + exit 1 +fi +echo "" + +echo -e "${CYAN}[1/4]${NC} Updating FlexLove.lua..." +sed -i.bak "s/flexlove\._VERSION = \".*\"/flexlove._VERSION = \"${NEW_VERSION}\"/" FlexLove.lua +rm -f FlexLove.lua.bak +echo -e "${GREEN}✓ FlexLove.lua updated${NC}" + +echo -e "${CYAN}[2/4]${NC} Updating README.md..." +FIRST_LINE=$(head -1 README.md) +if [[ "$FIRST_LINE" =~ ^#.*FlexLöve.*v[0-9]+\.[0-9]+\.[0-9]+ ]]; then + sed -i.bak -E "1s/v[0-9]+\.[0-9]+\.[0-9]+/v${NEW_VERSION}/" README.md + rm -f README.md.bak + echo -e "${GREEN}✓ README.md updated${NC}" +else + echo -e "${YELLOW}⚠ README.md first line doesn't match expected format, skipping${NC}" + echo -e "${YELLOW}Expected: # FlexLöve v0.0.0${NC}" + echo -e "${YELLOW}Found: $FIRST_LINE${NC}" +fi + +echo -e "${CYAN}[3/4]${NC} Staging changes..." +git add FlexLove.lua README.md +echo -e "${GREEN}✓ Changes staged${NC}" + +echo "" +echo -e "${CYAN}Changes to be committed:${NC}" +git diff --cached --stat +echo "" + +echo -e "${YELLOW}Ready to commit and create tag${NC}" +echo -e "${CYAN}Commit message:${NC} v${NEW_VERSION} release" +echo -e "${CYAN}Tag:${NC} v${NEW_VERSION}" +echo "" +read -p "Commit changes and create tag? (y/n) " -n 1 -r +echo "" +if [[ ! $REPLY =~ ^[Yy]$ ]]; then + echo -e "${YELLOW}Changes staged but not committed${NC}" + echo "You can:" + echo " - Review changes: git diff --cached" + echo " - Commit manually: git commit -m 'v${NEW_VERSION} release'" + echo " - Unstage: git restore --staged FlexLove.lua README.md" + exit 0 +fi + +# Commit changes +echo "" +echo -e "${CYAN}[4/4]${NC} Committing and tagging..." +git commit -m "v${NEW_VERSION} release" +git tag -a "v${NEW_VERSION}" -m "Release version ${NEW_VERSION}" + +echo "" +echo -e "${GREEN}═══════════════════════════════════════${NC}" +echo -e "${GREEN}✓ Version bump complete!${NC}" +echo -e "${GREEN}═══════════════════════════════════════${NC}" +echo "" +echo -e "${CYAN}Version:${NC} ${CURRENT_VERSION} → ${GREEN}${NEW_VERSION}${NC}" +echo -e "${CYAN}Tag created:${NC} ${GREEN}v${NEW_VERSION}${NC}" +echo "" +echo -e "${BLUE}Release will be available at:${NC}" +echo " https://github.com/$(git remote get-url origin | sed 's/.*github.com[:/]\(.*\)\.git/\1/')/releases/tag/v${NEW_VERSION}" +echo ""