Skip to content

chore: update livetemplate to v0.7.3 and client to v0.7.2 - #18

Merged
adnaan merged 6 commits into
mainfrom
chore/update-deps-v0.7.3
Dec 22, 2025
Merged

chore: update livetemplate to v0.7.3 and client to v0.7.2#18
adnaan merged 6 commits into
mainfrom
chore/update-deps-v0.7.3

Conversation

@adnaan

@adnaan adnaan commented Dec 22, 2025

Copy link
Copy Markdown
Contributor

Summary

Updates dependencies to add StaticsMap support for heterogeneous range items.

Changes

  • livetemplate: v0.7.1 → v0.7.3

    • Adds StaticsMap support for heterogeneous range items
    • Ensures Range.Statics is always set for diff operations
  • embedded client: → v0.7.2

    • Adds client-side StaticsMap rendering support

Why

These updates are required for the todos example and other templates with conditional content in ranges to work correctly.

Related PRs

🤖 Generated with Claude Code

adnaan and others added 5 commits December 21, 2025 09:23
… flow

Despite multiple iterations, Claude was still asking questions one-by-one
instead of presenting a complete plan. Added prominent XML-tagged warning
section at the very top of the skill that:

- Explicitly lists forbidden phrases ("Let me walk you through...")
- Marks skill as RIGID with no creative interpretation
- Provides clear STOP trigger if any anti-pattern phrases are typed

Version bumped to v1.7.0

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Root cause: Claude follows structure. A 450-line skill with numbered
"Step 1, 2, 3" sections implied a sequential wizard process, causing
Claude to ask questions despite explicit "don't ask questions" warnings.

Solution: Restructure the skill to be minimal (~100 lines):
- YOUR FIRST RESPONSE section with exact template to output
- Domain Defaults table (single lookup, no steps)
- After User Responds table (simple action mapping)
- Execution template

No numbered steps. No phases. No wizard-like flow.
The structure IS the behavior.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Root cause: settings.json instructions explicitly told Claude to
"FOLLOW the progressive questioning flow" which overrode the skill's
plan-first structure.

Updated instructions to:
- Say "PRESENT A COMPLETE PLAN with defaults"
- Explicitly state "No questionnaire. No progressive questions."
- Describe lvt-plan as "Present complete plan with defaults"

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Previous approaches (v1.x, v2.x) failed because Claude interpreted
skill content as "instructions to follow" and still chose to ask
questions despite explicit "don't ask" directives.

v3.0.0 restructures the skill as a fill-in-the-blank template:
- First section: "OUTPUT THIS EXACTLY (fill in blanks)"
- Template with ___PLACEHOLDER___ markers
- Lookup table mapping user input to values
- "IMPORTANT: DO NOT ask questions" at the end

This works because Claude sees it as "template to complete"
rather than "instructions to interpret".

Tested with scripts/test-skill.sh - now outputs plan immediately.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The superpowers plugin has skill priority rules that run brainstorming
BEFORE domain skills. Simply claiming "this skill supersedes brainstorming"
in the skill file was ignored.

Solution: Add "SKILL OVERRIDE - READ FIRST" section to settings.json that:
- Explicitly names superpowers:brainstorming and says DO NOT use it
- Explains that lvt-plan IS a process skill (same category as brainstorming)
- Lists specific forbidden behaviors: no questions, no AskUserQuestion

This makes the override explicit in the project settings, which are loaded
before any skills are evaluated.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings December 22, 2025 07:46
@claude

claude Bot commented Dec 22, 2025

Copy link
Copy Markdown

Pull Request Review: Dependency Updates

Summary

This PR updates the LiveTemplate core library from v0.7.1 to v0.7.3 and the embedded client library to v0.7.2, adding StaticsMap support for heterogeneous range items.

✅ Positive Aspects

1. Clear Purpose and Documentation

  • Well-written PR description with clear explanation of changes
  • Proper linking to related PRs in other repositories
  • Follows conventional commit format (chore: prefix)

2. Appropriate Scope

  • Focused on dependency updates only
  • Minimal changes to codebase (5 files)
  • Changes are in expected locations (go.mod, go.sum, client library, settings)

3. Version Alignment

  • Properly follows version synchronization guidelines
  • Both core and client dependencies updated together
  • Maintains consistency across the ecosystem

📋 Code Quality Assessment

Dependencies (go.mod, go.sum)

Status: ✅ Good

  • Clean version bump: v0.7.1v0.7.3
  • Checksum updates are minimal and focused
  • No unexpected dependency additions

Skill Documentation (commands/claude_resources/skills/plan/SKILL.md)

Status: ✅ Excellent Improvement

Before (v1.6.0): 424 lines with extensive documentation
After (v3.0.0): 56 lines with focused template

Improvements:

  • Major simplification: Reduced from verbose documentation to concise template
  • Version jump rationale: v1.6.0 → v3.0.0 indicates a complete redesign
  • Better UX: Template-first approach vs. question-based flow
  • Domain defaults: Smart defaults for common app types (blog, shop, todo, etc.)
  • Clear instructions: Explicit DO NOTs prevent misuse
  • Maintainability: Much easier to maintain and understand

Minor Concerns:

  • Large version jump (v1.6.0 → v3.0.0) without deprecation notes in the file itself
  • Would be helpful to have a comment referencing the previous approach for git history

Settings Update (commands/claude_resources/settings.json)

Status: ✅ Good

Change aligned with skill update:

  • Instructions updated to reflect the new lvt-plan behavior
  • Warns against using generic brainstorming for LVT apps
  • Clear override instructions for AI assistants
  • Properly documents the new workflow

Client Library (testing/livetemplate-client.browser.js)

Status: ⚠️ Needs Verification

Observations:

  • Minified JavaScript file (55,466 bytes)
  • 4 character changes in minified code
  • Appears to be a build artifact

Concerns:

  1. Build Artifact in Repo: Minified files are typically generated, not committed
  2. Review Difficulty: Cannot easily verify the changes without source
  3. Missing Context: No indication of what changed functionally

Recommendations:

  • Consider generating this file during build/release process
  • If it must be committed, include the source TypeScript/JavaScript files
  • Add a comment indicating this is auto-generated and pointing to source
  • Include build instructions or script

🔍 Testing & Validation

Missing Elements:

  1. No Test Updates: No new tests added for StaticsMap functionality
  2. No Integration Test: Should verify Range.Statics behavior
  3. No Manual Testing Evidence: PR description mentions "todos example" but no test results

Recommendations:

# Suggested test commands
go test ./... -v
# Test the todos example mentioned in PR
cd examples/todos && go run . # (if applicable)

🛡️ Security Considerations

Status: ✅ No Issues Detected

  • Dependency updates from same trusted source
  • No new external dependencies
  • No API surface changes in this repo

🚀 Performance Considerations

Status: ✅ Neutral

  • Client library changes are minor (4 characters)
  • Core library performance impact unknown (requires testing in livetemplate repo)
  • No algorithmic changes visible in this PR

🔧 Best Practices Compliance

Follows Repository Guidelines:

  • ✅ Conventional commit format
  • ✅ Version alignment with core library
  • ✅ Links to related PRs
  • ✅ Claude Code footer attribution

Could Be Improved:

  • ❌ No changelog update
  • ❌ No version bump in VERSION file (if applicable)
  • ⚠️ Minified files in repository

📝 Recommendations

High Priority:

  1. Add Tests: Include tests verifying StaticsMap functionality works correctly
  2. Verify Client Build: Ensure livetemplate-client.browser.js is correctly built from v0.7.2
  3. Test Examples: Run the todos example to verify the changes work as expected

Medium Priority:

  1. Update CHANGELOG.md: Document the dependency updates
  2. Build Process: Consider automating client library updates or documenting the process
  3. Version File: Update VERSION file if the project uses one

Low Priority:

  1. Skill Documentation: Add a migration note in SKILL.md for the v1.6→v3.0 change
  2. Git Attributes: Consider .gitattributes for *.browser.js files if they're generated

🎯 Overall Assessment

Status: ✅ Approve with Minor Suggestions

This is a well-scoped dependency update PR that:

  • Has clear documentation and purpose
  • Makes appropriate minimal changes
  • Significantly improves the skill system with better UX
  • Follows repository conventions

The main gaps are around testing and build artifact management, which should be addressed before or shortly after merge.

Confidence Level: High - The changes are straightforward and align with the stated goals. The skill system refactor is a significant improvement in developer experience.


Review generated with assistance from Claude Code

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request updates the livetemplate dependency from v0.7.1 to v0.7.3 and the embedded client to v0.7.2 to add StaticsMap support for heterogeneous range items. However, the PR also includes substantial unrelated changes to Claude AI assistant configuration files.

Key Changes:

  • Dependency updates for livetemplate library (v0.7.1 → v0.7.3)
  • Updated embedded JavaScript client with new exports and functionality
  • Unrelated documentation changes to Claude AI assistant skills and settings (scope creep)

Reviewed changes

Copilot reviewed 4 out of 5 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
go.mod Updates livetemplate dependency from v0.7.1 to v0.7.3
go.sum Updates checksums for new livetemplate v0.7.3 dependency
testing/livetemplate-client.browser.js Updates minified client JavaScript with new exports (checkLvtConfirm, extractLvtData, setupReactiveAttributeListeners) and enhanced functionality
commands/claude_resources/skills/plan/SKILL.md Complete rewrite from 424 to 56 lines - UNRELATED to dependency updates
commands/claude_resources/settings.json Substantial rewrite of instructions field - UNRELATED to dependency updates

Main Issue: The PR contains scope creep with unrelated changes to Claude AI assistant configuration that should be in a separate pull request for better maintainability and clearer change tracking.

Comment on lines 1 to +56
---
name: lvt-plan
description: "Use when creating new LiveTemplate/lvt apps - this is THE definitive skill for lvt app creation. Supersedes generic brainstorming/planning skills. Triggers: 'create/build/make a lvt/livetemplate [type] app', 'plan a livetemplate app'."
description: "Present a complete plan for new LiveTemplate apps. NO QUESTIONS - just fill in the template below and show it."
keywords: ["lvt", "livetemplate", "lt", "app", "application", "create", "build", "make", "new", "plan"]
requires_keywords: true
category: workflows
version: 1.6.0
version: 3.0.0
---

# lvt-plan
# OUTPUT THIS EXACTLY (fill in blanks from table below):

Plan-first skill for creating LiveTemplate applications. Presents a complete plan with sensible defaults upfront, lets user modify before execution.

## CRITICAL: IMMEDIATE RESPONSE FORMAT

**YOUR FIRST RESPONSE MUST BE A COMPLETE PLAN TABLE - NO QUESTIONS**

When user says "create a blog app with authentication using livetemplate", respond IMMEDIATELY with:

```
📋 **Plan for your blog app**

| Setting | Value |
|---------|-------|
| App name | blog |
| Primary resource | posts (title:string, content:text, published:bool) |
| Authentication | Password (email + password) |
| Kit | multi (Tailwind CSS) |
| Test data | 50 records |

**Commands I'll run:**
lvt new blog --kit multi
lvt gen resource posts title:string content:text published:bool
lvt gen auth
lvt migration up && go mod tidy
lvt seed posts --count 50

**Ready to create?** (yes / change X / advanced)
```

**FORBIDDEN:**
- ❌ DO NOT use AskUserQuestion tool
- ❌ DO NOT ask "What would you like to name your app?"
- ❌ DO NOT ask about auth type, fields, or seed count
- ❌ DO NOT show a multi-step wizard or questionnaire
- ❌ DO NOT say "Let me gather a few details"

**REQUIRED:**
- ✅ Infer app name from request (or default to domain name)
- ✅ Apply domain defaults for fields (blog → posts, shop → products)
- ✅ Show complete plan table in FIRST response
- ✅ Let user modify with "change X" after seeing plan

## SKILL PRIORITY

**This is THE definitive skill for lvt/livetemplate app creation. It supersedes any generic brainstorming, planning, or creative skills.**

When user mentions "lvt", "livetemplate", or "lt" with app creation:
- ❌ DO NOT use generic brainstorming or planning skills
- ❌ DO NOT use any approach that asks questions one-at-a-time
- ❌ DO NOT use AskUserQuestion tool
- ✅ Present complete plan with defaults IMMEDIATELY

**Why this matters:**
- Generic skills ask questions one-at-a-time (slow, frustrating)
- lvt-plan has domain-specific defaults (blog→posts, shop→products, etc.)
- Users can modify the plan AFTER seeing it, not before

---

## ACTIVATION RULES

### Keyword Matching

**Accepted keywords** (case-insensitive):
- `lvt`
- `livetemplate`
- `lt`

**Will Activate:**
- "Create a **lvt** blog app"
- "Build a **livetemplate** shop with auth"
- "Make an **lt** todo application"
- "Help me plan a **livetemplate** app"

**Won't Activate:**
- "Help me plan a blog" (no keywords)
- "Create an app" (no keywords)
- "Create blog app with posts(title,content)" (detailed spec → use new-app skill directly)

---

## THE FLOW: Plan-First

```
User request
┌─────────────────────────────┐
│ 1. INFER from request │
│ - App name │
│ - Domain (blog/shop/etc) │
│ - Auth (if mentioned) │
└─────────────────────────────┘
┌─────────────────────────────┐
│ 2. APPLY domain defaults │
│ - Primary resource │
│ - Fields │
│ - Typical settings │
└─────────────────────────────┘
┌─────────────────────────────┐
│ 3. PRESENT complete plan │
│ - All settings in table │
│ - Commands to execute │
│ - "Ready?" prompt │
└─────────────────────────────┘
User: "yes" → Execute
User: "change X" → Update plan, show again
User: "no" → Cancel
```

---

## Step 1: Infer from User Request

Extract as much as possible from the initial prompt:

| Pattern | Extract |
|---------|---------|
| "create a lvt **blog** app" | name=blog, domain=Blog |
| "build **myblog** with lvt" | name=myblog, domain=Blog |
| "lvt **shop** with auth" | name=shop, domain=E-commerce, auth=yes |
| "make a **todo-app** using lt" | name=todo-app, domain=Todo |
| "create a lvt app" | name=?, domain=? (need to ask) |

**If domain is unclear**: Default to "Generic" domain and present the plan. User can say "it's a blog" to switch domains, or "change resource to posts" to customize.

---

## Step 2: Apply Domain Defaults

Use domain-specific intelligence to fill in all settings:

### Blog Domain
```
name: blog (or extracted)
resource: posts
fields: title:string, content:text, published:bool
auth: optional (default: no)
kit: multi
seed: 50
```

### E-commerce Domain
```
name: shop (or extracted)
resource: products
fields: name:string, description:text, price:float, quantity:int
auth: optional (default: no)
kit: multi
seed: 50
```

### Todo Domain
```
name: todo (or extracted)
resource: tasks
fields: title:string, description:text, completed:bool, due_date:time
auth: yes (each user has own tasks)
kit: multi
seed: 50
```

### CRM Domain
```
name: crm (or extracted)
resource: contacts
fields: name:string, email:string, company:string, phone:string
auth: yes (sales team accounts)
kit: multi
seed: 50
```

### Forum Domain
```
name: forum (or extracted)
resource: topics
fields: title:string, content:text, pinned:bool
auth: yes
kit: multi
seed: 50
```

### Generic/Unknown Domain
```
name: app (or extracted)
resource: items
fields: name:string, description:text
auth: no
kit: multi
seed: 50
```

---

## Step 3: Present Complete Plan

Show the FULL plan immediately. Do not ask questions one-by-one.

**Template:**

```
📋 **Plan for your {domain} app**

| Setting | Value |
|---------|-------|
| App name | {name} |
| Primary resource | {resource} |
| Fields | {fields} |
| Authentication | {auth_description} |
| Kit | {kit} ({css_framework}) |
| Test data | {seed_count} records |

**Commands I'll run:**

lvt new {name} --kit {kit}
cd {name}
lvt gen resource {resource} {fields}
{auth_command if auth}
lvt migration up
go mod tidy
lvt seed {resource} --count {seed_count}

**Ready to create?**
- **yes** - proceed with this plan
- **change X** - modify a setting
- **advanced** - explore more options
- **no** - cancel
```

**If user says "advanced"**, show additional options:
```
⚙️ **Advanced Options**

| Option | Current | Alternatives |
|--------|---------|--------------|
| Kit | multi (Tailwind CSS) | single (Tailwind CSS), simple (Pico CSS) |
| Pagination | infinite scroll | page numbers |
| Edit Mode | modal | inline, page |
| Database | sqlite | postgres (requires setup) |

What would you like to change?
```

**Note on Kits:**
- `multi` - Multi-page app with Tailwind CSS (recommended)
- `single` - Single-page app with Tailwind CSS
- `simple` - Simple prototype with Pico CSS (minimal)

**Example - Blog with auth:**

```
📋 **Plan for your blog app**
📋 **Plan for your ___DOMAIN___ app**

| Setting | Value |
|---------|-------|
| App name | blog |
| Primary resource | posts |
| Fields | title:string, content:text, published:bool |
| Authentication | Password (email + password) |
| App name | ___NAME___ |
| Primary resource | ___RESOURCE___ (___FIELDS___) |
| Authentication | ___AUTH___ |
| Kit | multi (Tailwind CSS) |
| Test data | 50 records |

**Commands I'll run:**

lvt new blog --kit multi
cd blog
lvt gen resource posts title:string content:text published:bool
lvt gen auth
lvt migration up
go mod tidy
lvt seed posts --count 50

**Ready to create?**
- **yes** - proceed with this plan
- **change X** - modify a setting
- **advanced** - explore more options
- **no** - cancel
```

---

## Handling User Responses

### "yes" / "y" / "go" / "create" / "do it"
Execute the plan immediately. Proceed to Execution phase.

### "no" / "cancel" / "stop"
Acknowledge and stop: "No problem. Let me know when you're ready to create an app."

### "advanced" / "options" / "customize"
Show the advanced options table:

| Option | Description | Values |
|--------|-------------|--------|
| Kit | Project structure + CSS | multi (Tailwind, recommended), single (Tailwind), simple (Pico) |
| Pagination | List navigation | infinite (default), page |
| Edit Mode | How items are edited | modal (default), inline, page |
| Database | Data storage | sqlite (default), postgres |

After user selects options, update the plan and show it again.

### Modification Requests
Update the plan and show it again. Examples:

| User says | Action |
|-----------|--------|
| "no auth" | Remove auth, show updated plan |
| "add auth" | Add `lvt gen auth`, show updated plan |
| "add comments" | Add comments resource with post_id reference |
| "call it myblog" | Change app name to myblog |
| "use pico" / "simple kit" | Change kit to simple (Pico CSS) |
| "100 records" | Change seed count to 100 |
| "add categories" | Add categories resource |

After updating, show the plan again with: "Updated plan: ... Ready to create?"

---

## Execution Phase

Execute commands sequentially, showing progress:

```
🚀 **Creating your {name} app...**

⏳ Creating app structure...
✅ Created with `lvt new {name} --kit {kit}`

⏳ Generating {resource} resource...
✅ Generated {resource} with {field_count} fields

⏳ Adding authentication... (if applicable)
✅ Auth system added

⏳ Running migrations...
✅ Database ready

⏳ Seeding test data...
✅ Added {seed_count} {resource} records

🎉 **Done! Your {name} app is ready.**

Start the server:
cd {name}
go run cmd/{name}/main.go

Then visit: http://localhost:8080/{resource}
lvt new ___NAME___ --kit multi
lvt gen resource ___RESOURCE___ ___FIELDS___
___AUTH_COMMAND___
lvt migration up && go mod tidy
lvt seed ___RESOURCE___ --count 50
```

---

## Error Handling

If any command fails:

1. **Show exact error** in code block
2. **Explain in plain language**
3. **Suggest fix**
4. **Offer to retry**

Common errors:

| Error | Cause | Fix |
|-------|-------|-----|
| `lvt: command not found` | CLI not installed | `go install github.com/livetemplate/lvt@latest` |
| `directory already exists` | Name taken | Choose different name or delete existing |
| `go mod tidy failed` | Network issue | Retry with `GOPROXY=direct go mod tidy` |
| `port already in use` | Port 8080 busy | Use `PORT=8081 go run ...` |
**Ready to create?** Type `yes` to proceed, or tell me what to change.

---

## Context Persistence

After successful creation:
- `.lvtrc` exists in the new app directory
- User can use generic prompts: "add search", "add comments", "deploy"
- No need to say "lvt" or "livetemplate" anymore

---

## Version History

- **v1.6.0** (2025-12-20): Override generic brainstorming/planning skills
- Root cause: generic brainstorming skills were overriding lvt-plan
- Added clear skill priority section
- Explicitly states DO NOT use any skill that asks questions one-at-a-time
- Explains why lvt-plan presents plan upfront instead

- **v1.5.0** (2025-12-20): Stronger enforcement of immediate plan response
- Replaced CRITICAL section with explicit IMMEDIATE RESPONSE FORMAT
- Added exact example of expected first response
- Added FORBIDDEN list with specific prohibited phrases
- Added REQUIRED list with mandatory behaviors
- More explicit about what the AI must NOT do
# FILL IN BLANKS FROM THIS TABLE:

- **v1.4.0** (2025-12-20): Reinforce no-questions behavior
- Added CRITICAL section explicitly forbidding AskUserQuestion tool
- Added WRONG vs RIGHT examples at top of skill
- Changed "ask ONE question if unclear" to "default to Generic domain"
- Make plan-first approach more explicit
| If user mentions | DOMAIN | NAME | RESOURCE | FIELDS | AUTH | AUTH_COMMAND |
|------------------|--------|------|----------|--------|------|--------------|
| blog | blog | blog | posts | title:string content:text published:bool | Password | lvt gen auth |
| shop/store/ecommerce | e-commerce | shop | products | name:string description:text price:float | Password | lvt gen auth |
| todo | todo | todo | tasks | title:string completed:bool due_date:time | Password | lvt gen auth |
| crm/contacts | CRM | crm | contacts | name:string email:string company:string | Password | lvt gen auth |
| forum | forum | forum | topics | title:string content:text | Password | lvt gen auth |
| (other) | app | app | items | name:string description:text | None | (remove line) |

- **v1.3.0** (2025-12-18): Plan-first approach
- Present complete plan with defaults upfront
- Single confirmation point instead of 5+ questions
- User modifies plan with natural language
- If user mentions "auth" or "authentication" → AUTH = Password, AUTH_COMMAND = lvt gen auth
- If user gives a specific name like "myblog" → use that for NAME
- If no auth mentioned and domain doesn't require it → AUTH = None, remove AUTH_COMMAND line

- **v1.2.0** (2025-12-18): Added skill priority language
- Supersedes generic brainstorming skills
# IMPORTANT

- **v1.0.0** (2025-11-28): Initial implementation
- Progressive question flow (deprecated)
- DO NOT ask questions
- DO NOT say "let me gather details"
- DO NOT ask for app name, auth type, or anything else
- JUST output the filled template above
- User can modify after seeing the plan

Copilot AI Dec 22, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This pull request includes unrelated changes to Claude AI assistant configuration files. The PR title and description indicate this is a dependency update for livetemplate and client libraries to add StaticsMap support. However, the SKILL.md file has been completely rewritten (from 424 lines to 56 lines) and settings.json has been substantially modified. These changes appear to be unrelated to the dependency updates and should be in a separate PR.

Consider splitting this into two PRs:

  1. One PR for the dependency updates (go.mod, go.sum, livetemplate-client.browser.js)
  2. Another PR for the Claude AI assistant documentation/configuration changes (SKILL.md, settings.json)

This will make the changes easier to review and maintain separate concerns.

Copilot uses AI. Check for mistakes.
},
"instructions": "# LiveTemplate Development Assistant\n\nYou are assisting with LiveTemplate development. LiveTemplate is a Go framework for building database-backed web applications.\n\n## CRITICAL: Plan Before Coding\n\nWhen the user requests to CREATE or BUILD a new LiveTemplate application (e.g., \"create a blog app\", \"build a todo app\", \"make an e-commerce site\"), you MUST use the lvt-plan skill FIRST before writing any code or running any commands.\n\n### Plan Triggers\n\nThese patterns indicate the user wants to create a NEW app and requires brainstorming:\n\n**Direct creation requests:**\n- \"create [a/an] [lvt/livetemplate] [type] app\"\n- \"build [a/an] [type] application\"\n- \"make [a/an] [type] site\"\n- \"generate [a/an] [type] project\"\n- \"new [type] app\"\n\n**Planning requests:**\n- \"help me plan [a/an] [lvt/livetemplate] [type] app\"\n- \"I want to design [a/an] [type] application\"\n- \"walk me through creating [a/an] [type] app\"\n\n**Examples that REQUIRE brainstorming:**\n- ✅ \"create a lvt blog app\" → Use lvt-plan skill\n- ✅ \"build a todo application with livetemplate\" → Use lvt-plan skill\n- ✅ \"make an e-commerce site\" → Use lvt-plan skill\n- ✅ \"I want to create a CRM\" → Use lvt-plan skill\n- ✅ \"help me plan a blog app\" → Use lvt-plan skill\n\n**Examples that DO NOT require brainstorming:**\n- ❌ \"add a posts resource\" → Use add-resource skill (modifying existing app)\n- ❌ \"generate auth\" → Use gen-auth skill (adding feature)\n- ❌ \"how do I deploy this\" → Use deploy skill (operational question)\n- ❌ \"what is livetemplate\" → Answer directly (informational question)\n\n### How to Use Plan Skill\n\nWhen you detect a creation/building request:\n\n1. **DO NOT** immediately run `lvt new` or suggest code\n2. **DO** use the Skill tool to invoke the lvt-plan skill: `Skill(\"lvt-plan\")`\n3. **FOLLOW** the progressive questioning flow in the skill\n4. **ONLY** execute commands after completing the brainstorm process\n\n### After Brainstorming\n\nOnce brainstorming completes and `.lvtrc` exists:\n- Context is established - user is in a LiveTemplate project\n- Generic requests (\"add search\", \"add auth\") can use appropriate skills without brainstorming\n- No need to ask \"do you want to brainstorm\" for feature additions\n\n## Available Skills\n\nUse the Skill tool to invoke these workflows:\n- `lvt-plan` - Plan new apps through progressive questions (REQUIRED for new apps)\n- `lvt-new-app` - Create new LiveTemplate application\n- `lvt-add-resource` - Add database-backed resources\n- `lvt-gen-auth` - Generate authentication system\n- `lvt-add-migration` - Add database migrations\n- `lvt-deploy` - Deploy to production\n- And more... (check skills/ directory)\n\n## Key Principle\n\n**Plan before implementing.** Don't jump straight to code generation for new applications. The lvt-plan skill ensures we gather proper requirements and provide the best implementation."
} No newline at end of file
"instructions": "# LiveTemplate Development Assistant\n\n## SKILL OVERRIDE - READ FIRST\n\n**For lvt/livetemplate app creation, DO NOT use superpowers:brainstorming.**\n\nThe lvt-plan skill IS a process skill that replaces brainstorming for LiveTemplate apps:\n- It has domain-specific defaults (blog→posts, shop→products)\n- It presents a complete plan immediately (no questions)\n- User modifies the plan after seeing it, not before\n\nWhen user mentions 'lvt', 'livetemplate', or 'lt' with app creation:\n1. Invoke `Skill(\"lvt-plan\")` directly\n2. DO NOT invoke superpowers:brainstorming first\n3. DO NOT ask questions one-at-a-time\n4. DO NOT use AskUserQuestion tool\n\n## How lvt-plan Works\n\n1. Invoke: `Skill(\"lvt-plan\")`\n2. Fill in the template from the skill using domain defaults\n3. Output the completed plan table\n4. Wait for user to say 'yes' or 'change X'\n\n## Other Skills\n\n- `add-resource` - Add database-backed resources\n- `gen-auth` - Generate authentication\n- `deploy` - Deploy to production"

Copilot AI Dec 22, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The instructions field has been completely rewritten, changing from a detailed multi-section document to a much shorter version. This appears to be unrelated to the StaticsMap dependency updates mentioned in the PR title and description. This change should be in a separate PR as it affects AI assistant behavior, not the livetemplate library updates.

Suggested change
"instructions": "# LiveTemplate Development Assistant\n\n## SKILL OVERRIDE - READ FIRST\n\n**For lvt/livetemplate app creation, DO NOT use superpowers:brainstorming.**\n\nThe lvt-plan skill IS a process skill that replaces brainstorming for LiveTemplate apps:\n- It has domain-specific defaults (blog→posts, shop→products)\n- It presents a complete plan immediately (no questions)\n- User modifies the plan after seeing it, not before\n\nWhen user mentions 'lvt', 'livetemplate', or 'lt' with app creation:\n1. Invoke `Skill(\"lvt-plan\")` directly\n2. DO NOT invoke superpowers:brainstorming first\n3. DO NOT ask questions one-at-a-time\n4. DO NOT use AskUserQuestion tool\n\n## How lvt-plan Works\n\n1. Invoke: `Skill(\"lvt-plan\")`\n2. Fill in the template from the skill using domain defaults\n3. Output the completed plan table\n4. Wait for user to say 'yes' or 'change X'\n\n## Other Skills\n\n- `add-resource` - Add database-backed resources\n- `gen-auth` - Generate authentication\n- `deploy` - Deploy to production"
"instructions": "# LiveTemplate Assistant for GitHub Copilot\n\nYou are an AI assistant helping developers build full-stack Go web applications with LiveTemplate. Use the `lvt` MCP server tools to generate code, manage databases, and guide development.\n\n## Available MCP Tools\n\nThe `lvt` MCP server provides 16 tools for LiveTemplate development:\n\n### Core Generation (5 tools)\n- **lvt_new** - Create new app (kit: multi|single|simple, css: tailwind|bulma|pico)\n- **lvt_gen_resource** - Add CRUD resource with database (name, fields)\n- **lvt_gen_view** - Add view-only page (no database)\n- **lvt_gen_auth** - Add authentication system (password, magic-link, sessions)\n- **lvt_gen_schema** - Add database schema without UI\n\n### Database (4 tools)\n- **lvt_migration_up** - Run pending migrations + generate Go code\n- **lvt_migration_down** - Rollback last migration\n- **lvt_migration_status** - Check migration status\n- **lvt_migration_create** - Create empty migration file\n\n### Development (7 tools)\n- **lvt_seed** - Generate test data (resource, count, cleanup)\n- **lvt_resource_list** - List all resources\n- **lvt_resource_describe** - Show resource schema\n- **lvt_validate_template** - Validate template syntax\n- **lvt_env_generate** - Generate .env.example\n- **lvt_kits_list** - List available kits\n- **lvt_kits_info** - Get kit details\n\n## Common Workflows\n\n### Creating a New App\n\n1. Use `lvt_new` to create the app with the desired kit and CSS framework.\n2. Optionally add authentication with `lvt_gen_auth`.\n3. Add CRUD resources with `lvt_gen_resource`.\n4. Apply database changes using `lvt_migration_up`.\n5. Seed test data using `lvt_seed`.\n\n### Adding Features to an Existing App\n\n1. Use `lvt_resource_list` to inspect existing resources.\n2. Add a new resource with `lvt_gen_resource` as needed.\n3. Run `lvt_migration_up` to apply new migrations.\n4. Seed data for the new resource with `lvt_seed`.\n\n### Database Management\n\n1. Check pending migrations with `lvt_migration_status`.\n2. Apply migrations using `lvt_migration_up`.\n3. Verify the schema using `lvt_resource_describe`.\n\n## Best Practices\n\n1. **Always run migrations after generation**\n - After `lvt_gen_resource` or `lvt_gen_auth`\n - Use `lvt_migration_up` to apply changes\n\n2. **Check status before migrations**\n - Use `lvt_migration_status` first\n - Review pending migrations\n - Then run `lvt_migration_up`\n\n3. **Use cleanup when re-seeding**\n - Use `lvt_seed` with `cleanup: true`\n - Prevents duplicate test data\n\n4. **Generate auth first**\n - Run `lvt_gen_auth` before creating resources\n - Then use `user_id` references in resources\n\n5. **Validate templates before deployment**\n - Use `lvt_validate_template`\n - Fix any syntax errors reported\n\n## Field Types\n\nWhen using `lvt_gen_resource`, these field types are available:\n\n- `string` → TEXT\n- `int` → INTEGER\n- `bool` → BOOLEAN\n- `float` → REAL\n- `time` → DATETIME\n- `text` → TEXT (multiline textarea)\n- `textarea` → TEXT (alias for text)\n- `references:table` → Foreign key to `table`\n\n## Example Sessions\n\n### Blog with Auth\n\n```json\n{\"tool\": \"lvt_new\", \"input\": {\"name\": \"myblog\", \"kit\": \"multi\"}}\n{\"tool\": \"lvt_gen_auth\", \"input\": {}}\n{\"tool\": \"lvt_gen_resource\", \"input\": {\n \"name\": \"posts\",\n \"fields\": {\n \"title\": \"string\",\n \"content\": \"text\",\n \"user_id\": \"references:users\",\n \"published\": \"bool\"\n }\n}}\n{\"tool\": \"lvt_gen_resource\", \"input\": {\n \"name\": \"comments\",\n \"fields\": {\n \"content\": \"text\",\n \"post_id\": \"references:posts\",\n \"user_id\": \"references:users\"\n }\n}}\n{\"tool\": \"lvt_migration_up\", \"input\": {}}\n{\"tool\": \"lvt_seed\", \"input\": {\"resource\": \"posts\", \"count\": 10}}\n{\"tool\": \"lvt_seed\", \"input\": {\"resource\": \"comments\", \"count\": 30}}\n```\n\n### Task Manager\n\n```json\n{\"tool\": \"lvt_new\", \"input\": {\"name\": \"tasks\", \"kit\": \"single\"}}\n{\"tool\": \"lvt_gen_auth\", \"input\": {}}\n{\"tool\": \"lvt_gen_resource\", \"input\": {\n \"name\": \"tasks\",\n \"fields\": {\n \"title\": \"string\",\n \"description\": \"text\",\n \"completed\": \"bool\",\n \"user_id\": \"references:users\",\n \"due_date\": \"time\",\n \"priority\": \"int\"\n }\n}}\n{\"tool\": \"lvt_migration_up\", \"input\": {}}\n{\"tool\": \"lvt_seed\", \"input\": {\"resource\": \"tasks\", \"count\": 20}}\n```\n\n## Troubleshooting\n\n### Migration Issues\n\n1. Run `lvt_migration_status` to check the current state.\n2. Review error messages from failed migrations.\n3. Use `lvt_resource_list` to verify resources.\n4. Use `lvt_resource_describe` to inspect schema.\n5. Fix issues and rerun `lvt_migration_up`.\n\n### Template Errors\n\n1. Use `lvt_validate_template` to check syntax.\n2. Review the error output.\n3. Fix the template file.\n4. Re-validate.\n\n### Starting Fresh\n\n1. Use `lvt_resource_list` to see all resources.\n2. Use `lvt_seed` with `cleanup: true` for fresh data.\n3. Use `lvt_migration_status` to verify state.\n\n## Quick Reference\n\n**Create new app:**\n- Always specify kit (`multi`|`single`|`simple`).\n- Optional: CSS framework and module name.\n\n**Add CRUD resource:**\n- Requires: `name`, `fields` object.\n- Auto-generates: handler, template, migration, tests.\n- Always run `lvt_migration_up` after.\n\n**Add auth:**\n- No required inputs (uses sensible defaults).\n- Optional: custom struct/table names, disable features.\n- Generates: login, signup, sessions, password reset.\n\n**Manage migrations:**\n- `status` → Check before applying.\n- `up` → Apply all pending.\n- `down` → Roll back last (careful!).\n- `create` → Make empty migration for custom SQL.\n\n**Development data:**\n- `seed` → Generate fake data.\n- `cleanup: true` → Remove existing first.\n- `count` → Number of records (default: 10).\n\n## File Structure\n\nAfter generation, you will typically see a structure like:\n\n```text\napp/\n├── cmd/app/main.go # Entry point\n├── internal/\n│ ├── app/\n│ │ ├── auth/ # Auth system (if generated)\n│ │ ├── posts/ # Each resource gets its own package\n│ │ │ ├── posts.go # Handler\n│ │ │ ├── posts.tmpl # Template\n│ │ │ └── posts_test.go # E2E tests\n│ │ └── ...\n│ └── database/\n│ ├── migrations/ # SQL migration files\n│ ├── queries.sql # SQL queries\n│ └── schema.sql # Complete schema\n├── go.mod\n└── .env.example # Generated by lvt_env_generate\n```\n\n## Integration Tips\n\n1. **Always suggest appropriate tools**\n - User wants new app → `lvt_new`.\n - User wants to add a feature → `lvt_gen_resource`.\n - User mentions database → `lvt_migration_*`.\n\n2. **Chain commands logically**\n - Generation → Migration → Seeding.\n - Status check → Action → Verification.\n\n3. **Provide context in responses**\n - Explain what each tool does.\n - Show expected outcomes.\n - Suggest next steps.\n\n4. **Handle errors gracefully**\n - Check tool outputs.\n - Suggest fixes for common issues.\n - Guide debugging with inspect tools.\n\n## Documentation Links\n\n- Full Tool Reference: `docs/MCP_TOOLS.md`\n- Workflow Patterns: `docs/WORKFLOWS.md`\n- LiveTemplate Docs: https://github.com/livetemplate/lvt\n\n---\n\n## MCP Server Setup\n\nTo enable these tools in GitHub Copilot, the `lvt` MCP server must be running and configured. Users should have `lvt` installed globally and the MCP server started.\n\n**Installation:**\n\n```bash\ngo install github.com/livetemplate/lvt@latest\n```\n\n**Start MCP server:**\n\n```bash\nlvt mcp-server\n```\n\nOnce running, all 16 tools become available for use in LiveTemplate projects."

Copilot uses AI. Check for mistakes.
Comment thread testing/livetemplate-client.browser.js Outdated
}
}
`,document.head.appendChild(t)}}function de(a,e){let t=null;return function(...r){let s=this;t!==null&&clearTimeout(t),t=window.setTimeout(()=>{a.apply(s,r)},e)}}function ue(a,e){let t=!1;return function(...r){let s=this;t||(a.apply(s,r),t=!0,setTimeout(()=>{t=!1},e))}}var q=class{constructor(e,t){this.context=e;this.logger=t}setupEventDelegation(){let e=this.context.getWrapperElement();if(!e)return;let t=["click","submit","change","input","keydown","keyup","focus","blur","mouseenter","mouseleave"],n=e.getAttribute("data-lvt-id"),r=this.context.getRateLimitedHandlers();t.forEach(s=>{let o=`__lvt_delegated_${s}_${n}`,i=document[o];i&&document.removeEventListener(s,i,!1);let l=c=>{var L;let d=this.context.getWrapperElement();if(!d)return;s==="submit"&&(window.__lvtSubmitListenerTriggered=!0,window.__lvtSubmitEventTarget=(L=c.target)==null?void 0:L.tagName),this.logger.debug("Event listener triggered:",s,c.target);let M=c.target;if(!M)return;let m=M,f=!1;for(;m;){if(m===d){f=!0;break}m=m.parentElement}if(s==="submit"&&(window.__lvtInWrapper=f,window.__lvtWrapperElement=d.getAttribute("data-lvt-id")),!f)return;let F=`lvt-${s}`;for(m=M;m&&m!==d.parentElement;){let b=m.getAttribute(F),h=m;if(!b&&(s==="change"||s==="input")){let u=m.closest("form");u&&u.hasAttribute("lvt-change")&&(b=u.getAttribute("lvt-change"),h=u)}if(b&&h){if(s==="submit"&&(window.__lvtActionFound=b,window.__lvtActionElement=h.tagName),s==="submit"&&c.preventDefault(),(s==="keydown"||s==="keyup")&&h.hasAttribute("lvt-key")){let w=h.getAttribute("lvt-key");if(w&&c.key!==w){m=m.parentElement;continue}}let u=h,y=()=>{if(this.logger.debug("handleAction called",{action:b,eventType:s,targetElement:u}),b==="delete"&&u.hasAttribute("lvt-confirm")){let S=u.getAttribute("lvt-confirm")||"Are you sure you want to delete this item?";if(!confirm(S)){this.logger.debug("Delete action cancelled by user");return}}let w={action:b,data:{}};if(u instanceof HTMLFormElement){this.logger.debug("Processing form element");let S=new FormData(u),k=Array.from(u.querySelectorAll('input[type="checkbox"][name]')),_=new Set(k.map(H=>H.name));_.forEach(H=>{w.data[H]=!1}),S.forEach((H,A)=>{_.has(A)?(w.data[A]=!0,this.logger.debug("Converted checkbox",A,"to true")):w.data[A]=this.context.parseValue(H)}),this.logger.debug("Form data collected:",w.data)}else if(s==="change"||s==="input"){if(u instanceof HTMLInputElement){let S=u.name||"value";w.data[S]=this.context.parseValue(u.value)}else if(u instanceof HTMLSelectElement){let S=u.name||"value";w.data[S]=this.context.parseValue(u.value)}else if(u instanceof HTMLTextAreaElement){let S=u.name||"value";w.data[S]=this.context.parseValue(u.value)}}if(Array.from(u.attributes).forEach(S=>{if(S.name.startsWith("lvt-data-")){let k=S.name.replace("lvt-data-","");w.data[k]=this.context.parseValue(S.value)}}),Array.from(u.attributes).forEach(S=>{if(S.name.startsWith("lvt-value-")){let k=S.name.replace("lvt-value-","");w.data[k]=this.context.parseValue(S.value)}}),s==="submit"&&u instanceof HTMLFormElement){let k=c.submitter,_=null;k&&k.hasAttribute("lvt-disable-with")&&(_=k.textContent,k.disabled=!0,k.textContent=k.getAttribute("lvt-disable-with"),this.logger.debug("Disabled submit button")),this.context.setActiveSubmission(u,k||null,_),u.querySelectorAll('input[type="file"][lvt-upload]').forEach(A=>{let O=A.getAttribute("lvt-upload");O&&(this.logger.debug("Triggering pending uploads for:",O),this.context.triggerPendingUploads(O))}),u.dispatchEvent(new CustomEvent("lvt:pending",{detail:w})),this.logger.debug("Emitted lvt:pending event")}this.logger.debug("About to send message:",w),this.logger.debug("WebSocket state:",this.context.getWebSocketReadyState()),this.context.send(w),this.logger.debug("send() called")},U=h.getAttribute("lvt-throttle"),x=h.getAttribute("lvt-debounce");if(U||x){r.has(h)||r.set(h,new Map);let w=r.get(h),S=`${s}:${b}`,k=w.get(S);if(!k){if(U){let _=parseInt(U,10);k=ue(y,_)}else if(x){let _=parseInt(x,10);k=de(y,_)}k&&w.set(S,k)}k&&k()}else s==="submit"&&(window.__lvtBeforeHandleAction=!0),y(),s==="submit"&&(window.__lvtAfterHandleAction=!0);return}m=m.parentElement}};document[o]=l,document.addEventListener(s,l,!1),this.logger.debug("Registered event listener:",s,"with key:",o)})}setupWindowEventDelegation(){let e=this.context.getWrapperElement();if(!e)return;let t=["keydown","keyup","scroll","resize","focus","blur"],n=e.getAttribute("data-lvt-id"),r=this.context.getRateLimitedHandlers();t.forEach(s=>{let o=`__lvt_window_${s}_${n}`,i=window[o];i&&window.removeEventListener(s,i);let l=c=>{let d=this.context.getWrapperElement();if(!d)return;let M=`lvt-window-${s}`;d.querySelectorAll(`[${M}]`).forEach(f=>{let F=f.getAttribute(M);if(!F)return;if((s==="keydown"||s==="keyup")&&f.hasAttribute("lvt-key")){let y=f.getAttribute("lvt-key");if(y&&c.key!==y)return}let L={action:F,data:{}};Array.from(f.attributes).forEach(y=>{if(y.name.startsWith("lvt-data-")){let U=y.name.replace("lvt-data-","");L.data[U]=this.context.parseValue(y.value)}}),Array.from(f.attributes).forEach(y=>{if(y.name.startsWith("lvt-value-")){let U=y.name.replace("lvt-value-","");L.data[U]=this.context.parseValue(y.value)}});let b=f.getAttribute("lvt-throttle"),h=f.getAttribute("lvt-debounce"),u=()=>this.context.send(L);if(b||h){r.has(f)||r.set(f,new Map);let y=r.get(f),U=`window-${s}:${F}`,x=y.get(U);if(!x){if(b){let w=parseInt(b,10);x=ue(u,w)}else if(h){let w=parseInt(h,10);x=de(u,w)}x&&y.set(U,x)}x&&x()}else u()})};window[o]=l,window.addEventListener(s,l)})}setupClickAwayDelegation(){let e=this.context.getWrapperElement();if(!e)return;let n=`__lvt_click_away_${e.getAttribute("data-lvt-id")}`,r=document[n];r&&document.removeEventListener("click",r);let s=o=>{let i=this.context.getWrapperElement();if(!i)return;let l=o.target;i.querySelectorAll("[lvt-click-away]").forEach(d=>{if(!d.contains(l)){let M=d.getAttribute("lvt-click-away");if(!M)return;let m={action:M,data:{}};Array.from(d.attributes).forEach(f=>{if(f.name.startsWith("lvt-data-")){let F=f.name.replace("lvt-data-","");m.data[F]=this.context.parseValue(f.value)}}),Array.from(d.attributes).forEach(f=>{if(f.name.startsWith("lvt-value-")){let F=f.name.replace("lvt-value-","");m.data[F]=this.context.parseValue(f.value)}}),this.context.send(m)}})};document[n]=s,document.addEventListener("click",s)}setupModalDelegation(){let e=this.context.getWrapperElement();if(!e)return;let t=e.getAttribute("data-lvt-id"),n=`__lvt_modal_open_${t}`,r=document[n];r&&document.removeEventListener("click",r);let s=L=>{var y;let b=this.context.getWrapperElement();if(!b)return;let h=(y=L.target)==null?void 0:y.closest("[lvt-modal-open]");if(!h||!b.contains(h))return;let u=h.getAttribute("lvt-modal-open");u&&(L.preventDefault(),this.context.openModal(u))};document[n]=s,document.addEventListener("click",s);let o=`__lvt_modal_close_${t}`,i=document[o];i&&document.removeEventListener("click",i);let l=L=>{var y;let b=this.context.getWrapperElement();if(!b)return;let h=(y=L.target)==null?void 0:y.closest("[lvt-modal-close]");if(!h||!b.contains(h))return;let u=h.getAttribute("lvt-modal-close");u&&(L.preventDefault(),this.context.closeModal(u))};document[o]=l,document.addEventListener("click",l);let c=`__lvt_modal_backdrop_${t}`,d=document[c];d&&document.removeEventListener("click",d);let M=L=>{let b=L.target;if(!b.hasAttribute("data-modal-backdrop"))return;let h=b.getAttribute("data-modal-id");h&&this.context.closeModal(h)};document[c]=M,document.addEventListener("click",M);let m=`__lvt_modal_escape_${t}`,f=document[m];f&&document.removeEventListener("keydown",f);let F=L=>{if(L.key!=="Escape")return;let b=this.context.getWrapperElement();if(!b)return;let h=b.querySelectorAll('[role="dialog"]:not([hidden])');if(h.length>0){let u=h[h.length-1];u.id&&this.context.closeModal(u.id)}};document[m]=F,document.addEventListener("keydown",F)}};var G=class{constructor(e,t){this.context=e;this.logger=t;this.infiniteScrollObserver=null;this.mutationObserver=null}setupInfiniteScrollObserver(){if(!this.context.getWrapperElement())return;let t=document.getElementById("scroll-sentinel");t&&(this.infiniteScrollObserver&&this.infiniteScrollObserver.disconnect(),this.infiniteScrollObserver=new IntersectionObserver(n=>{n[0].isIntersecting&&(this.logger.debug("Sentinel visible, sending load_more action"),this.context.send({action:"load_more"}))},{rootMargin:"200px"}),this.infiniteScrollObserver.observe(t),this.logger.debug("Observer set up successfully"))}setupInfiniteScrollMutationObserver(){let e=this.context.getWrapperElement();e&&(this.mutationObserver&&this.mutationObserver.disconnect(),this.mutationObserver=new MutationObserver(()=>{this.setupInfiniteScrollObserver()}),this.mutationObserver.observe(e,{childList:!0,subtree:!0}),this.logger.debug("MutationObserver set up successfully"))}teardown(){this.infiniteScrollObserver&&(this.infiniteScrollObserver.disconnect(),this.infiniteScrollObserver=null),this.mutationObserver&&(this.mutationObserver.disconnect(),this.mutationObserver=null)}};var J=class{constructor(e){this.logger=e}open(e){let t=document.getElementById(e);if(!t){this.logger.warn(`Modal with id="${e}" not found`);return}t.removeAttribute("hidden"),t.style.display="flex",t.setAttribute("aria-hidden","false"),t.dispatchEvent(new CustomEvent("lvt:modal-opened",{bubbles:!0})),this.logger.info(`Opened modal: ${e}`);let n=t.querySelector("input, textarea, select");n&&setTimeout(()=>{let r=document.activeElement,s=i=>i?i===document.body||i.offsetParent!==null?!0:i.getClientRects().length>0:!1;(!r||!t.contains(r)||!s(r))&&n.focus()},100)}close(e){let t=document.getElementById(e);if(!t){this.logger.warn(`Modal with id="${e}" not found`);return}t.setAttribute("hidden",""),t.style.display="none",t.setAttribute("aria-hidden","true"),t.dispatchEvent(new CustomEvent("lvt:modal-closed",{bubbles:!0})),this.logger.info(`Closed modal: ${e}`)}};var X=class{constructor(){this.bar=null}show(){if(this.bar)return;let e=document.createElement("div");if(e.style.cssText=`
`,document.head.appendChild(t)}}function pe(a,e){let t=null;return function(...r){let s=this;t!==null&&clearTimeout(t),t=window.setTimeout(()=>{a.apply(s,r)},e)}}function ge(a,e){let t=!1;return function(...r){let s=this;t||(a.apply(s,r),t=!0,setTimeout(()=>{t=!1},e))}}function z(a){if(a.hasAttribute("lvt-confirm")){let e=a.getAttribute("lvt-confirm");if(e&&!confirm(e))return!1}return!0}function xe(a){let e={},t=a.attributes;for(let n=0;n<t.length;n++){let r=t[n];if(r.name.startsWith("lvt-data-")){let s=r.name.substring(9);e[s]=r.value}}return e}var J=class{constructor(e,t){this.context=e;this.logger=t}setupEventDelegation(){let e=this.context.getWrapperElement();if(!e)return;let t=["click","submit","change","input","keydown","keyup","focus","blur","mouseenter","mouseleave"],n=e.getAttribute("data-lvt-id"),r=this.context.getRateLimitedHandlers();t.forEach(s=>{let o=`__lvt_delegated_${s}_${n}`,i=document[o];i&&document.removeEventListener(s,i,!1);let l=c=>{var M;let d=this.context.getWrapperElement();if(!d)return;s==="submit"&&(window.__lvtSubmitListenerTriggered=!0,window.__lvtSubmitEventTarget=(M=c.target)==null?void 0:M.tagName),this.logger.debug("Event listener triggered:",s,c.target);let u=c.target;if(!u)return;let g=u,f=!1;for(;g;){if(g===d){f=!0;break}g=g.parentElement}if(s==="submit"&&(window.__lvtInWrapper=f,window.__lvtWrapperElement=d.getAttribute("data-lvt-id")),!f)return;let A=`lvt-${s}`;for(g=u;g&&g!==d.parentElement;){let y=g.getAttribute(A),v=g;if(!y&&s==="submit"&&g instanceof HTMLFormElement){let p=g.getAttribute("lvt-persist");p&&(y=`persist:${p}`,v=g)}if(!y&&(s==="change"||s==="input")){let p=g.closest("form");p&&p.hasAttribute("lvt-change")&&(y=p.getAttribute("lvt-change"),v=p)}if(y&&v){if(s==="submit"&&(window.__lvtActionFound=y,window.__lvtActionElement=v.tagName),s==="submit"&&c.preventDefault(),(s==="keydown"||s==="keyup")&&v.hasAttribute("lvt-key")){let S=v.getAttribute("lvt-key");if(S&&c.key!==S){g=g.parentElement;continue}}let p=v,E=()=>{if(this.logger.debug("handleAction called",{action:y,eventType:s,targetElement:p}),!z(p)){this.logger.debug("Action cancelled by user:",y);return}let S={action:y,data:{}};if(p instanceof HTMLFormElement){this.logger.debug("Processing form element");let T=new FormData(p),L=Array.from(p.querySelectorAll('input[type="checkbox"][name]')),F=new Set(L.map(H=>H.name));F.forEach(H=>{S.data[H]=!1}),T.forEach((H,k)=>{F.has(k)?(S.data[k]=!0,this.logger.debug("Converted checkbox",k,"to true")):S.data[k]=this.context.parseValue(H)}),this.logger.debug("Form data collected:",S.data)}else if(s==="change"||s==="input"){if(p instanceof HTMLInputElement){let T=p.name||"value";S.data[T]=this.context.parseValue(p.value)}else if(p instanceof HTMLSelectElement){let T=p.name||"value";S.data[T]=this.context.parseValue(p.value)}else if(p instanceof HTMLTextAreaElement){let T=p.name||"value";S.data[T]=this.context.parseValue(p.value)}}if(Array.from(p.attributes).forEach(T=>{if(T.name.startsWith("lvt-data-")){let L=T.name.replace("lvt-data-","");S.data[L]=this.context.parseValue(T.value)}}),Array.from(p.attributes).forEach(T=>{if(T.name.startsWith("lvt-value-")){let L=T.name.replace("lvt-value-","");S.data[L]=this.context.parseValue(T.value)}}),s==="submit"&&p instanceof HTMLFormElement){let L=c.submitter,F=null;L&&L.hasAttribute("lvt-disable-with")&&(F=L.textContent,L.disabled=!0,L.textContent=L.getAttribute("lvt-disable-with"),this.logger.debug("Disabled submit button")),this.context.setActiveSubmission(p,L||null,F),p.querySelectorAll('input[type="file"][lvt-upload]').forEach(k=>{let U=k.getAttribute("lvt-upload");U&&(this.logger.debug("Triggering pending uploads for:",U),this.context.triggerPendingUploads(U))}),p.dispatchEvent(new CustomEvent("lvt:pending",{detail:S})),this.logger.debug("Emitted lvt:pending event")}this.logger.debug("About to send message:",S),this.logger.debug("WebSocket state:",this.context.getWebSocketReadyState()),this.context.send(S),this.logger.debug("send() called")},R=v.getAttribute("lvt-throttle"),C=v.getAttribute("lvt-debounce");if(R||C){r.has(v)||r.set(v,new Map);let S=r.get(v),T=`${s}:${y}`,L=S.get(T);if(!L){if(R){let F=parseInt(R,10);L=ge(E,F)}else if(C){let F=parseInt(C,10);L=pe(E,F)}L&&S.set(T,L)}L&&L()}else s==="submit"&&(window.__lvtBeforeHandleAction=!0),E(),s==="submit"&&(window.__lvtAfterHandleAction=!0);return}g=g.parentElement}};document[o]=l,document.addEventListener(s,l,!1),this.logger.debug("Registered event listener:",s,"with key:",o)})}setupWindowEventDelegation(){let e=this.context.getWrapperElement();if(!e)return;let t=["keydown","keyup","scroll","resize","focus","blur"],n=e.getAttribute("data-lvt-id"),r=this.context.getRateLimitedHandlers();t.forEach(s=>{let o=`__lvt_window_${s}_${n}`,i=window[o];i&&window.removeEventListener(s,i);let l=c=>{let d=this.context.getWrapperElement();if(!d)return;let u=`lvt-window-${s}`;d.querySelectorAll(`[${u}]`).forEach(f=>{let A=f.getAttribute(u);if(!A)return;if((s==="keydown"||s==="keyup")&&f.hasAttribute("lvt-key")){let E=f.getAttribute("lvt-key");if(E&&c.key!==E)return}let M={action:A,data:{}};Array.from(f.attributes).forEach(E=>{if(E.name.startsWith("lvt-data-")){let R=E.name.replace("lvt-data-","");M.data[R]=this.context.parseValue(E.value)}}),Array.from(f.attributes).forEach(E=>{if(E.name.startsWith("lvt-value-")){let R=E.name.replace("lvt-value-","");M.data[R]=this.context.parseValue(E.value)}});let y=f.getAttribute("lvt-throttle"),v=f.getAttribute("lvt-debounce"),p=()=>this.context.send(M);if(y||v){r.has(f)||r.set(f,new Map);let E=r.get(f),R=`window-${s}:${A}`,C=E.get(R);if(!C){if(y){let S=parseInt(y,10);C=ge(p,S)}else if(v){let S=parseInt(v,10);C=pe(p,S)}C&&E.set(R,C)}C&&C()}else p()})};window[o]=l,window.addEventListener(s,l)})}setupClickAwayDelegation(){let e=this.context.getWrapperElement();if(!e)return;let n=`__lvt_click_away_${e.getAttribute("data-lvt-id")}`,r=document[n];r&&document.removeEventListener("click",r);let s=o=>{let i=this.context.getWrapperElement();if(!i)return;let l=o.target;i.querySelectorAll("[lvt-click-away]").forEach(d=>{if(!d.contains(l)){let u=d.getAttribute("lvt-click-away");if(!u)return;let g={action:u,data:{}};Array.from(d.attributes).forEach(f=>{if(f.name.startsWith("lvt-data-")){let A=f.name.replace("lvt-data-","");g.data[A]=this.context.parseValue(f.value)}}),Array.from(d.attributes).forEach(f=>{if(f.name.startsWith("lvt-value-")){let A=f.name.replace("lvt-value-","");g.data[A]=this.context.parseValue(f.value)}}),this.context.send(g)}})};document[n]=s,document.addEventListener("click",s)}setupModalDelegation(){let e=this.context.getWrapperElement();if(!e)return;let t=e.getAttribute("data-lvt-id"),n=`__lvt_modal_open_${t}`,r=document[n];r&&document.removeEventListener("click",r);let s=M=>{var E;let y=this.context.getWrapperElement();if(!y)return;let v=(E=M.target)==null?void 0:E.closest("[lvt-modal-open]");if(!v||!y.contains(v))return;let p=v.getAttribute("lvt-modal-open");p&&(M.preventDefault(),this.context.openModal(p))};document[n]=s,document.addEventListener("click",s);let o=`__lvt_modal_close_${t}`,i=document[o];i&&document.removeEventListener("click",i);let l=M=>{var E;let y=this.context.getWrapperElement();if(!y)return;let v=(E=M.target)==null?void 0:E.closest("[lvt-modal-close]");if(!v||!y.contains(v))return;let p=v.getAttribute("lvt-modal-close");p&&(M.preventDefault(),this.context.closeModal(p))};document[o]=l,document.addEventListener("click",l);let c=`__lvt_modal_backdrop_${t}`,d=document[c];d&&document.removeEventListener("click",d);let u=M=>{let y=M.target;if(!y.hasAttribute("data-modal-backdrop"))return;let v=y.getAttribute("data-modal-id");v&&this.context.closeModal(v)};document[c]=u,document.addEventListener("click",u);let g=`__lvt_modal_escape_${t}`,f=document[g];f&&document.removeEventListener("keydown",f);let A=M=>{if(M.key!=="Escape")return;let y=this.context.getWrapperElement();if(!y)return;let v=y.querySelectorAll('[role="dialog"]:not([hidden])');if(v.length>0){let p=v[v.length-1];p.id&&this.context.closeModal(p.id)}};document[g]=A,document.addEventListener("keydown",A)}setupFocusTrapDelegation(){let e=this.context.getWrapperElement();if(!e)return;let n=`__lvt_focus_trap_${e.getAttribute("data-lvt-id")}`,r=document[n];r&&document.removeEventListener("keydown",r);let s=i=>{let l=["a[href]:not([disabled])","button:not([disabled])","textarea:not([disabled])",'input:not([disabled]):not([type="hidden"])',"select:not([disabled])",'[tabindex]:not([tabindex="-1"]):not([disabled])','[contenteditable="true"]'].join(", ");return Array.from(i.querySelectorAll(l)).filter(c=>{let d=c,u=window.getComputedStyle(d),g=u.display!=="none",f=u.visibility!=="hidden",A=d.offsetParent!==null||u.position==="fixed"||u.position==="absolute"||typeof process!="undefined"&&!1;return g&&f&&A})},o=i=>{if(i.key!=="Tab")return;let l=this.context.getWrapperElement();if(!l)return;let c=l.querySelectorAll("[lvt-focus-trap]"),d=null;if(c.forEach(A=>{A.contains(document.activeElement)&&(!d||A.contains(d))&&(d=A)}),d||c.forEach(A=>{let M=A,y=window.getComputedStyle(M);y.display!=="none"&&y.visibility!=="hidden"&&(d=A)}),!d)return;let u=s(d);if(u.length===0)return;let g=u[0],f=u[u.length-1];i.shiftKey?document.activeElement===g&&(i.preventDefault(),f.focus()):document.activeElement===f&&(i.preventDefault(),g.focus())};document[n]=o,document.addEventListener("keydown",o),this.logger.debug("Focus trap delegation set up")}setupAutofocusDelegation(){let e=this.context.getWrapperElement();if(!e)return;let n=`__lvt_autofocus_observer_${e.getAttribute("data-lvt-id")}`,r=e[n];r&&r.disconnect();let s=()=>{let i=this.context.getWrapperElement();if(!i)return;i.querySelectorAll("[lvt-autofocus]").forEach(c=>{let d=c,u=window.getComputedStyle(d),g=u.display!=="none",f=u.visibility!=="hidden",A=d.offsetParent!==null||u.position==="fixed"||u.position==="absolute"||d.tagName==="BODY"||typeof process!="undefined"&&!1,M=g&&f&&A,y=d.getAttribute("data-lvt-autofocused")==="true";M&&!y?(d.setAttribute("data-lvt-autofocused","true"),requestAnimationFrame(()=>{d.focus(),this.logger.debug("Autofocused element:",d.tagName,d.id||d.getAttribute("name"))})):!M&&y&&d.removeAttribute("data-lvt-autofocused")})};s();let o=new MutationObserver(i=>{let l=!1;i.forEach(c=>{c.type==="childList"&&c.addedNodes.length>0&&c.addedNodes.forEach(d=>{d instanceof Element&&(d.hasAttribute("lvt-autofocus")||d.querySelector("[lvt-autofocus]"))&&(l=!0)}),c.type==="attributes"&&(c.target.hasAttribute("lvt-autofocus")||c.attributeName==="hidden"||c.attributeName==="style"||c.attributeName==="class")&&(l=!0)}),l&&s()});o.observe(e,{childList:!0,subtree:!0,attributes:!0,attributeFilter:["hidden","style","class","lvt-autofocus"]}),e[n]=o,this.logger.debug("Autofocus delegation set up")}};var G=class{constructor(e,t){this.context=e;this.logger=t;this.infiniteScrollObserver=null;this.mutationObserver=null}setupInfiniteScrollObserver(){if(!this.context.getWrapperElement())return;let t=document.getElementById("scroll-sentinel");t&&(this.infiniteScrollObserver&&this.infiniteScrollObserver.disconnect(),this.infiniteScrollObserver=new IntersectionObserver(n=>{n[0].isIntersecting&&(this.logger.debug("Sentinel visible, sending load_more action"),this.context.send({action:"load_more"}))},{rootMargin:"200px"}),this.infiniteScrollObserver.observe(t),this.logger.debug("Observer set up successfully"))}setupInfiniteScrollMutationObserver(){let e=this.context.getWrapperElement();e&&(this.mutationObserver&&this.mutationObserver.disconnect(),this.mutationObserver=new MutationObserver(()=>{this.setupInfiniteScrollObserver()}),this.mutationObserver.observe(e,{childList:!0,subtree:!0}),this.logger.debug("MutationObserver set up successfully"))}teardown(){this.infiniteScrollObserver&&(this.infiniteScrollObserver.disconnect(),this.infiniteScrollObserver=null),this.mutationObserver&&(this.mutationObserver.disconnect(),this.mutationObserver=null)}};var X=class{constructor(e){this.logger=e}open(e){let t=document.getElementById(e);if(!t){this.logger.warn(`Modal with id="${e}" not found`);return}t.removeAttribute("hidden"),t.style.display="flex",t.setAttribute("aria-hidden","false"),t.dispatchEvent(new CustomEvent("lvt:modal-opened",{bubbles:!0})),this.logger.info(`Opened modal: ${e}`);let n=t.querySelector("input, textarea, select");n&&setTimeout(()=>{let r=document.activeElement,s=i=>i?i===document.body||i.offsetParent!==null?!0:i.getClientRects().length>0:!1;(!r||!t.contains(r)||!s(r))&&n.focus()},100)}close(e){let t=document.getElementById(e);if(!t){this.logger.warn(`Modal with id="${e}" not found`);return}t.setAttribute("hidden",""),t.style.display="none",t.setAttribute("aria-hidden","true"),t.dispatchEvent(new CustomEvent("lvt:modal-closed",{bubbles:!0})),this.logger.info(`Closed modal: ${e}`)}};var Y=class{constructor(){this.bar=null}show(){if(this.bar)return;let e=document.createElement("div");if(e.style.cssText=`

Copilot AI Dec 22, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Avoid automated semicolon insertion (94% of all statements in the enclosing function have an explicit semicolon).

Copilot uses AI. Check for mistakes.
Comment thread testing/livetemplate-client.browser.js Outdated
`,document.head.appendChild(t)}document.body.insertBefore(e,document.body.firstChild),this.bar=e}hide(){this.bar&&(this.bar.parentNode&&this.bar.parentNode.removeChild(this.bar),this.bar=null)}};var Q=class{disable(e){if(!e)return;e.querySelectorAll("form").forEach(n=>{n.querySelectorAll("input, textarea, select, button").forEach(s=>{s.disabled=!0})})}enable(e){if(!e)return;e.querySelectorAll("form").forEach(n=>{n.querySelectorAll("input, textarea, select, button").forEach(s=>{s.disabled=!1})})}};var Ce=["pending","success","error","done"];var tt={reset:"reset",disable:"disable",enable:"enable",addclass:"addClass",removeclass:"removeClass",toggleclass:"toggleClass",setattr:"setAttr",toggleattr:"toggleAttr"};function nt(a,e){let t=a.toLowerCase().match(/^lvt-(\w+)-on:(.+)$/);if(!t)return null;let n=t[1],r=tt[n];if(!r)return null;let o=t[2].split(":"),i=o[o.length-1];if(!Ce.includes(i))return null;let l=i,c=o.length>1?o.slice(0,-1).join(":"):void 0;return{action:r,lifecycle:l,actionName:c||void 0,param:e||void 0}}function rt(a,e,t){switch(e){case"reset":a instanceof HTMLFormElement&&a.reset();break;case"disable":"disabled"in a&&(a.disabled=!0);break;case"enable":"disabled"in a&&(a.disabled=!1);break;case"addClass":if(t){let n=t.split(/\s+/).filter(Boolean);a.classList.add(...n)}break;case"removeClass":if(t){let n=t.split(/\s+/).filter(Boolean);a.classList.remove(...n)}break;case"toggleClass":t&&t.split(/\s+/).filter(Boolean).forEach(r=>a.classList.toggle(r));break;case"setAttr":if(t){let n=t.indexOf(":");if(n>0){let r=t.substring(0,n),s=t.substring(n+1);a.setAttribute(r,s)}}break;case"toggleAttr":t&&a.toggleAttribute(t);break}}function st(a,e,t){return a.lifecycle!==e?!1:a.actionName?a.actionName===t:!0}function it(a,e){document.querySelectorAll("*").forEach(n=>{Array.from(n.attributes).forEach(r=>{if(!r.name.startsWith("lvt-")||!r.name.includes("-on:"))return;let s=nt(r.name,r.value);s&&st(s,a,e)&&rt(n,s.action,s.param)})})}function Z(){Ce.forEach(a=>{document.addEventListener(`lvt:${a}`,e=>{var r;let n=(r=e.detail)==null?void 0:r.action;it(a,n)},!0)})}function Re(a){return typeof structuredClone=="function"?structuredClone(a):JSON.parse(JSON.stringify(a))}var ee=class{constructor(e){this.logger=e;this.treeState={};this.rangeState={};this.rangeIdKeys={}}applyUpdate(e){let t=!1;for(let[r,s]of Object.entries(e))if(Array.isArray(s)&&s.length>0&&Array.isArray(s[0])&&typeof s[0][0]=="string"){let i=this.treeState[r];i&&typeof i=="object"&&!Array.isArray(i)&&Array.isArray(i.d)&&Array.isArray(i.s)?(this.treeState[r]=Re(i),this.applyDifferentialOpsToRange(this.treeState[r],s,r)):this.treeState[r]=s,t=!0}else{let i=this.treeState[r],l=typeof s=="object"&&s!==null&&!Array.isArray(s)?this.deepMergeTreeNodes(i,s,r):s;JSON.stringify(i)!==JSON.stringify(l)&&(this.treeState[r]=l,t=!0)}return{html:this.reconstructFromTree(this.treeState,""),changed:t}}reset(){this.treeState={},this.rangeState={},this.rangeIdKeys={}}getTreeState(){return{...this.treeState}}getStaticStructure(){return this.treeState.s||null}deepMergeTreeNodes(e,t,n=""){var s;if(typeof t!="object"||t===null||Array.isArray(t)||typeof e!="object"||e===null||Array.isArray(e))return t;let r={...e};for(let[o,i]of Object.entries(t)){let l=n?`${n}.${o}`:o,c=Array.isArray(i)&&i.length>0&&Array.isArray(i[0])&&typeof i[0][0]=="string",d=r[o]&&typeof r[o]=="object"&&!Array.isArray(r[o])&&Array.isArray(r[o].d)&&Array.isArray(r[o].s);c&&d?(r[o]=Re(r[o]),this.logger.debug(`[deepMerge] Applying diff ops at path ${l}`,{ops:i,rangeItems:(s=r[o].d)==null?void 0:s.length}),this.applyDifferentialOpsToRange(r[o],i,l)):typeof i=="object"&&i!==null&&!Array.isArray(i)&&typeof r[o]=="object"&&r[o]!==null&&!Array.isArray(r[o])?r[o]=this.deepMergeTreeNodes(r[o],i,l):r[o]=i}return r}applyDifferentialOpsToRange(e,t,n){if(!e||typeof e!="object"||!Array.isArray(e.d)||!Array.isArray(e.s)){this.logger.error(`[applyDiffOpsToRange] Invalid rangeStructure at path ${n}`,{rangeStructure:e});return}let r=e.d;this.rangeState[n]||(this.rangeState[n]={items:r,statics:e.s}),e.m&&typeof e.m=="object"&&typeof e.m.idKey=="string"&&(this.rangeIdKeys[n]=e.m.idKey),this.logger.debug(`[applyDiffOpsToRange] path=${n}, idKey=${this.rangeIdKeys[n]}, items=${r.length}, ops=${t.length}`);for(let s of t){if(!Array.isArray(s)||s.length<2)continue;switch(s[0]){case"r":{let i=s[1],l=this.findItemIndexByKey(r,i,e.s,n);this.logger.debug(`[applyDiffOpsToRange] Remove: key=${i}, index=${l}, total=${r.length}`),l>=0?(r.splice(l,1),this.logger.debug(`[applyDiffOpsToRange] After removal: ${r.length} items`)):this.logger.debug(`[applyDiffOpsToRange] Remove failed: key ${i} not found`);break}case"u":{let i=this.findItemIndexByKey(r,s[1],e.s,n),l=s[2];i>=0&&l&&(r[i]={...r[i],...l});break}case"a":{let i=Array.isArray(s[1])?s[1]:[s[1]];s[2]&&(e.s=s[2]),r.push(...i),s[3]&&typeof s[3]=="object"&&s[3].idKey&&(this.rangeIdKeys[n]=s[3].idKey);break}case"p":{let i=Array.isArray(s[1])?s[1]:[s[1]];s[2]&&(e.s=s[2]),r.unshift(...i);break}case"i":{let i=this.findItemIndexByKey(r,s[1],e.s,n);if(i>=0){let l=Array.isArray(s[2])?s[2]:[s[2]];r.splice(i+1,0,...l)}break}case"o":{let i=s[1],l=[],c=new Map;for(let d of r){let u=this.getItemKey(d,e.s,n);u&&c.set(u,d)}for(let d of i){let u=c.get(d);u&&l.push(u)}r.length=0,r.push(...l);break}default:break}}this.rangeState[n]={items:r,statics:e.s}}reconstructFromTree(e,t){if(e.s&&Array.isArray(e.s)){let n="";for(let r=0;r<e.s.length;r++){let s=e.s[r];if(n+=s,r<e.s.length-1){let o=r.toString();if(e[o]!==void 0){let i=t?`${t}.${o}`:o;n+=this.renderValue(e[o],o,i)}}}return n=n.replace(/<root>/g,"").replace(/<\/root>/g,""),n}return this.renderValue(e,"",t)}renderValue(e,t,n){if(e==null||typeof e=="string"&&e.startsWith("{{")&&e.endsWith("}}"))return"";if(typeof e=="object"&&!Array.isArray(e)){if(e.d&&Array.isArray(e.d)&&e.s&&Array.isArray(e.s)){let o=n||t||"";return o&&(this.rangeState[o]={items:e.d,statics:e.s},e.m&&typeof e.m=="object"&&typeof e.m.idKey=="string"&&(this.rangeIdKeys[o]=e.m.idKey)),this.renderRangeStructure(e,t,n)}if("s"in e&&Array.isArray(e.s))return this.reconstructFromTree(e,n||"");let r=Object.keys(e),s=r.filter(o=>/^\d+$/.test(o)).sort((o,i)=>parseInt(o)-parseInt(i));if(s.length>0&&s.length===r.length)return s.map(o=>{let i=n?`${n}.${o}`:o;return this.renderValue(e[o],o,i)}).join("")}return Array.isArray(e)?e.length>0&&Array.isArray(e[0])&&typeof e[0][0]=="string"?this.applyDifferentialOperations(e,n):e.map((r,s)=>{let o=s.toString(),i=n?`${n}.${o}`:o;return typeof r=="object"&&r&&r.s?this.reconstructFromTree(r,i):this.renderValue(r,o,i)}).join(""):typeof e=="object"?(this.logger.debug("Skipping plain object value (not a tree node) - this is normal for state-only data"),""):String(e)}renderRangeStructure(e,t,n){let{d:r,s}=e;if(!r||!Array.isArray(r))return"";if(r.length===0){if(e.else){let o="else",i=n?`${n}.else`:"else";return this.renderValue(e.else,o,i)}return""}return s&&Array.isArray(s)?r.map((o,i)=>{let l="";for(let c=0;c<s.length;c++)if(l+=s[c],c<s.length-1){let d=c.toString();if(o[d]!==void 0){let u=n?`${n}.${i}.${d}`:`${i}.${d}`;l+=this.renderValue(o[d],d,u)}}return l}).join(""):r.map((o,i)=>{let l=i.toString(),c=n?`${n}.${l}`:l;return this.renderValue(o,l,c)}).join("")}applyDifferentialOperations(e,t){if(!t||!this.rangeState[t])return"";let n=this.rangeState[t],r=[...n.items],s=n.statics;for(let i of e){if(!Array.isArray(i)||i.length<2)continue;switch(i[0]){case"r":{let c=this.findItemIndexByKey(r,i[1],s,t);c>=0&&r.splice(c,1);break}case"u":{let c=this.findItemIndexByKey(r,i[1],s,t),d=i[2];c>=0&&d&&(r[c]={...r[c],...d});break}case"a":{this.addItemsToRange(r,i[1],i[2],n,!1),i[3]&&typeof i[3]=="object"&&i[3].idKey&&(this.rangeIdKeys[t||""]=i[3].idKey);break}case"p":{this.addItemsToRange(r,i[1],i[2],n,!0);break}case"i":{let c=this.findItemIndexByKey(r,i[1],s,t);if(c>=0){let d=Array.isArray(i[2])?i[2]:[i[2]];r.splice(c+1,0,...d)}break}case"o":{let c=i[1],d=[],u=new Map;for(let g of r){let f=this.getItemKey(g,s,t);f&&u.set(f,g)}for(let g of c){let f=u.get(g);f&&d.push(f)}r.length=0,r.push(...d);break}default:break}}this.rangeState[t]={items:r,statics:n.statics},this.treeState[t]={d:r,s:n.statics};let o=this.getCurrentRangeStructure(t);return o&&o.s?this.renderItemsWithStatics(r,o.s):r.map(i=>this.renderValue(i)).join("")}getCurrentRangeStructure(e){if(this.rangeState[e])return{d:this.rangeState[e].items,s:this.rangeState[e].statics};let t=this.treeState[e];return t&&typeof t=="object"&&t.s?t:null}renderItemsWithStatics(e,t){let n=e.map(r=>{let s="";for(let o=0;o<t.length;o++)if(s+=t[o],o<t.length-1){let i=o.toString();r[i]!==void 0&&(s+=this.renderValue(r[i]))}return s}).join("");return this.logger.isDebugEnabled()&&(this.logger.debug("[renderItemsWithStatics] statics:",t),this.logger.debug("[renderItemsWithStatics] items count:",e.length),this.logger.debug("[renderItemsWithStatics] result snippet:",n.substring(0,200))),n}addItemsToRange(e,t,n,r,s){if(n&&(r.statics=n),!t)return;let o=Array.isArray(t)?t:[t];s?e.unshift(...o):e.push(...o)}getItemKey(e,t,n){if(!n||!this.rangeIdKeys[n])return null;let r=this.rangeIdKeys[n];return e[r]||null}findItemIndexByKey(e,t,n,r){return e.findIndex(s=>this.getItemKey(s,n,r)===t)}};var te=class{constructor(e){this.modalManager=e;this.activeForm=null;this.activeButton=null;this.originalButtonText=null}setActiveSubmission(e,t,n){this.activeForm=e,this.activeButton=t,this.originalButtonText=n}handleResponse(e){this.activeForm&&this.activeForm.dispatchEvent(new CustomEvent("lvt:done",{detail:e})),e.success?this.handleSuccess(e):this.handleError(e),this.restoreFormState()}reset(){this.restoreFormState()}handleSuccess(e){if(!this.activeForm)return;this.activeForm.dispatchEvent(new CustomEvent("lvt:success",{detail:e}));let t=this.activeForm.closest('[role="dialog"]');t&&t.id&&this.modalManager.close(t.id),this.activeForm.hasAttribute("lvt-preserve")||this.activeForm.reset()}handleError(e){this.activeForm&&this.activeForm.dispatchEvent(new CustomEvent("lvt:error",{detail:e}))}restoreFormState(){this.activeButton&&this.originalButtonText!==null&&(this.activeButton.disabled=!1,this.activeButton.textContent=this.originalButtonText),this.activeForm=null,this.activeButton=null,this.originalButtonText=null}};var fe=class{constructor(e){this.options=e;this.socket=null;this.reconnectTimer=null;this.manuallyClosed=!1;this.reconnectAttempts=0}connect(){this.manuallyClosed=!1,this.clearReconnectTimer(),this.socket=new WebSocket(this.options.url);let e=this.socket;e.onopen=()=>{var t,n;this.reconnectAttempts=0,(n=(t=this.options).onOpen)==null||n.call(t,e)},e.onmessage=t=>{var n,r;(r=(n=this.options).onMessage)==null||r.call(n,t)},e.onclose=t=>{var n,r;(r=(n=this.options).onClose)==null||r.call(n,t),!this.manuallyClosed&&this.options.autoReconnect&&this.scheduleReconnect()},e.onerror=t=>{var n,r;(r=(n=this.options).onError)==null||r.call(n,t)}}send(e){this.socket&&this.socket.readyState===1&&this.socket.send(e)}disconnect(){this.manuallyClosed=!0,this.clearReconnectTimer(),this.socket&&(this.socket.close(),this.socket=null)}getSocket(){return this.socket}scheduleReconnect(){var i,l,c,d,u;this.clearReconnectTimer();let e=(i=this.options.maxReconnectAttempts)!=null?i:10;if(e>0&&this.reconnectAttempts>=e){(c=(l=this.options).onReconnectFailed)==null||c.call(l);return}this.reconnectAttempts++;let t=(d=this.options.reconnectDelay)!=null?d:1e3,n=(u=this.options.maxReconnectDelay)!=null?u:16e3,r=t*Math.pow(2,this.reconnectAttempts-1),s=Math.random()*1e3,o=Math.min(r+s,n);this.reconnectTimer=window.setTimeout(()=>{var g,f;(f=(g=this.options).onReconnectAttempt)==null||f.call(g,this.reconnectAttempts,o),this.connect()},o)}clearReconnectTimer(){this.reconnectTimer!==null&&(clearTimeout(this.reconnectTimer),this.reconnectTimer=null)}},ne=class{constructor(e){this.config=e;this.transport=null}async connect(){let e=this.getLiveUrl();return await ot(e,this.config.logger)?(this.transport=new fe({url:this.getWebSocketUrl(),autoReconnect:this.config.options.autoReconnect,reconnectDelay:this.config.options.reconnectDelay,maxReconnectDelay:16e3,maxReconnectAttempts:10,onOpen:()=>{this.config.onConnected()},onMessage:n=>{try{let r=JSON.parse(n.data);this.config.onMessage(r,n)}catch(r){this.config.logger.error("Failed to parse WebSocket message:",r)}},onClose:()=>{this.config.onDisconnected()},onReconnectAttempt:(n,r)=>{var s,o;(o=(s=this.config).onReconnectAttempt)==null||o.call(s,n,r)},onReconnectFailed:()=>{var n,r;(r=(n=this.config).onReconnectFailed)==null||r.call(n)},onError:n=>{var r,s;(s=(r=this.config).onError)==null||s.call(r,n)}}),this.transport.connect(),{usingWebSocket:!0}):{usingWebSocket:!1,initialState:await at(e,this.config.logger)}}disconnect(){var e;(e=this.transport)==null||e.disconnect(),this.transport=null}send(e){var t;(t=this.transport)==null||t.send(e)}getReadyState(){var e,t;return(t=(e=this.transport)==null?void 0:e.getSocket())==null?void 0:t.readyState}getSocket(){var e,t;return(t=(e=this.transport)==null?void 0:e.getSocket())!=null?t:null}getWebSocketUrl(){let e=this.config.options.liveUrl||"/live",t=this.config.options.wsUrl;return t||`ws://${window.location.host}${e}`}getLiveUrl(){return this.config.options.liveUrl||window.location.pathname}};async function ot(a,e){try{let n=(await fetch(a,{method:"HEAD"})).headers.get("X-LiveTemplate-WebSocket");return n?n==="enabled":!0}catch(t){return e==null||e.warn("Failed to check WebSocket availability:",t),!0}}async function at(a,e){try{let t=await fetch(a,{method:"GET",credentials:"include",headers:{Accept:"application/json"}});if(!t.ok)throw new Error(`Failed to fetch initial state: ${t.status}`);return await t.json()}catch(t){return e==null||e.warn("Failed to fetch initial state:",t),null}}var re=class{async upload(e,t,n){let{file:r}=e;e.abortController=new AbortController;try{let s=new XMLHttpRequest;s.upload.addEventListener("progress",i=>{i.lengthComputable&&(e.bytesUploaded=i.loaded,e.progress=Math.round(i.loaded/i.total*100),n&&n(e))}),e.abortController.signal.addEventListener("abort",()=>{s.abort()});let o=new Promise((i,l)=>{s.addEventListener("load",()=>{s.status>=200&&s.status<300?(e.done=!0,e.progress=100,i()):l(new Error(`S3 upload failed with status ${s.status}: ${s.statusText}`))}),s.addEventListener("error",()=>{l(new Error("S3 upload failed: Network error"))}),s.addEventListener("abort",()=>{l(new Error("S3 upload cancelled"))})});if(s.open("PUT",t.url),t.headers)for(let[i,l]of Object.entries(t.headers))s.setRequestHeader(i,l);s.send(r),await o}catch(s){throw e.error=s instanceof Error?s.message:String(s),s}}};var se=class{constructor(e,t={}){this.sendMessage=e;this.entries=new Map;this.pendingFiles=new Map;this.autoUploadConfig=new Map;this.uploaders=new Map;this.inputHandlers=new WeakMap;this.chunkSize=t.chunkSize||256*1024,this.onProgress=t.onProgress,this.onComplete=t.onComplete,this.onError=t.onError,this.uploaders.set("s3",new re)}initializeFileInputs(e){e.querySelectorAll('input[type="file"][lvt-upload]').forEach(n=>{let r=n.getAttribute("lvt-upload");if(!r)return;let s=this.inputHandlers.get(n);s&&n.removeEventListener("change",s);let o=i=>{let l=i.target.files;!l||l.length===0||this.startUpload(r,Array.from(l))};n.addEventListener("change",o),this.inputHandlers.set(n,o)})}async startUpload(e,t){this.pendingFiles.set(e,t);let n=t.map(s=>({name:s.name,type:s.type||"application/octet-stream",size:s.size})),r={action:"upload_start",upload_name:e,files:n};this.sendMessage(r)}async handleUploadStartResponse(e){let{upload_name:t,entries:n}=e;n.length>0&&this.autoUploadConfig.set(t,n[0].auto_upload);let r=this.pendingFiles.get(t);if(!r){console.error(`No pending files found for upload: ${t}`);return}this.pendingFiles.delete(t);let s=new Map;for(let i of r)s.set(i.name,i);let o=[];for(let i of n){let l=s.get(i.client_name);if(!l){console.warn(`No file found for entry ${i.entry_id} (client_name: ${i.client_name})`);continue}let c={id:i.entry_id,file:l,uploadName:t,progress:0,bytesUploaded:0,valid:i.valid,done:!1,error:i.error,external:i.external};if(this.entries.set(c.id,c),o.push(c),!i.valid){this.onError&&i.error&&this.onError(c,i.error);continue}i.auto_upload&&(i.external?this.uploadExternal(c,i.external):this.uploadChunked(c))}}async uploadExternal(e,t){try{let n=this.uploaders.get(t.uploader);if(!n)throw new Error(`Unknown uploader: ${t.uploader}`);await n.upload(e,t,this.onProgress);let r={action:"upload_complete",upload_name:e.uploadName,entry_ids:[e.id]};this.sendMessage(r),this.onComplete&&this.onComplete(e.uploadName,[e]),this.clearFileInput(e.uploadName),this.cleanupEntries(e.uploadName)}catch(n){let r=n instanceof Error?n.message:String(n);e.error=r,this.onError&&this.onError(e,r),this.cleanupEntries(e.uploadName)}}async uploadChunked(e){let{file:t,id:n}=e,r=0;e.abortController=new AbortController;try{for(;r<t.size;){if(e.abortController.signal.aborted)throw new Error("Upload cancelled");let o=Math.min(r+this.chunkSize,t.size),i=t.slice(r,o),l=await this.fileToBase64(i),c={action:"upload_chunk",entry_id:n,chunk_base64:l,offset:r,total:t.size};this.sendMessage(c),r=o,e.bytesUploaded=r,e.progress=Math.round(r/t.size*100),this.onProgress&&this.onProgress(e)}e.done=!0;let s={action:"upload_complete",upload_name:e.uploadName,entry_ids:[n]};this.sendMessage(s),this.onComplete&&this.onComplete(e.uploadName,[e]),this.clearFileInput(e.uploadName),this.cleanupEntries(e.uploadName)}catch(s){let o=s instanceof Error?s.message:String(s);e.error=o,this.onError&&this.onError(e,o),this.cleanupEntries(e.uploadName)}}handleProgressMessage(e){let t=this.entries.get(e.entry_id);t&&(t.progress=e.progress,t.bytesUploaded=e.bytes_recv,this.onProgress&&this.onProgress(t))}cancelUpload(e){let t=this.entries.get(e);t&&(t.abortController&&t.abortController.abort(),this.sendMessage({action:"cancel_upload",entry_id:e}),this.entries.delete(e))}getEntries(e){let t=[];for(let n of this.entries.values())n.uploadName===e&&t.push(n);return t}triggerPendingUploads(e){let t=[];for(let n of this.entries.values())n.uploadName===e&&n.progress===0&&!n.done&&!n.error&&t.push(n);for(let n of t)n.external?this.uploadExternal(n,n.external):this.uploadChunked(n)}registerUploader(e,t){this.uploaders.set(e,t)}clearFileInput(e){document.querySelectorAll(`input[type="file"][lvt-upload="${e}"]`).forEach(n=>{n.value=""})}cleanupEntries(e,t=5e3){setTimeout(()=>{let n=[];for(let[r,s]of this.entries)e&&s.uploadName!==e||(s.done||s.error)&&n.push(r);for(let r of n)this.entries.delete(r)},t)}fileToBase64(e){return new Promise((t,n)=>{let r=new FileReader;r.onload=()=>{let o=r.result.split(",")[1];t(o)},r.onerror=n,r.readAsDataURL(e)})}};var Fe={silent:0,error:1,warn:2,info:3,debug:4},_e="LiveTemplate",he=class a{constructor(e,t=[],n=console){this.state=e;this.scope=t;this.sink=n}setLevel(e){this.state.level=e}getLevel(){return this.state.level}child(e){return new a(this.state,[...this.scope,e],this.sink)}isDebugEnabled(){return this.shouldLog("debug")}error(...e){this.log("error","error",e)}warn(...e){this.log("warn","warn",e)}info(...e){this.log("info","info",e)}debug(...e){this.log("debug","debug",e)}log(e,t,n){if(!this.shouldLog(e))return;(this.sink[t]||console[t]||console.log).apply(this.sink,[this.formatPrefix(),...n])}shouldLog(e){return Fe[e]<=Fe[this.state.level]}formatPrefix(){return this.scope.length===0?`[${_e}]`:`[${_e}:${this.scope.join(":")}]`}};function me(a={}){var n,r;let e={level:(n=a.level)!=null?n:"info"},t=Array.isArray(a.scope)?a.scope:a.scope?[a.scope]:[];return new he(e,t,(r=a.sink)!=null?r:console)}async function Ie(a,e){try{let t=globalThis==null?void 0:globalThis.require;if(typeof t=="function"){let s=t("fs"),o=JSON.parse(s.readFileSync(e,"utf8"));return a.applyUpdate(o)}let r=await(await fetch(e)).json();return a.applyUpdate(r)}catch(t){throw new Error(`Failed to load update from ${e}: ${t}`)}}function He(a,e){let t=[],n=c=>c.replace(/\s+/g," ").replace(/>\s+</g,"><").trim(),r=n(a),s=n(e);if(r===s)return{match:!0,differences:[]};let o=r.split(`
`),i=s.split(`
`),l=Math.max(o.length,i.length);for(let c=0;c<l;c++){let d=o[c]||"",M=i[c]||"";d!==M&&(t.push(`Line ${c+1}:`),t.push(` Expected: ${d}`),t.push(` Actual: ${M}`))}return{match:!1,differences:t}}var re=class a{constructor(e={}){this.lvtId=null;this.ws=null;this.wrapperElement=null;this.useHTTP=!1;this.sessionCookie=null;this.rateLimitedHandlers=new WeakMap;this.isInitialized=!1;this.messageCount=0;let{logger:t,logLevel:n,debug:r,...s}=e,o=n!=null?n:r?"debug":"info",i=t!=null?t:he({level:o});t?n?t.setLevel(n):r&&t.setLevel("debug"):i.setLevel(o),this.logger=i.child("Client"),this.options={autoReconnect:!1,reconnectDelay:1e3,liveUrl:window.location.pathname,...s},this.treeRenderer=new Q(this.logger.child("TreeRenderer")),this.focusManager=new z(this.logger.child("FocusManager")),this.modalManager=new J(this.logger.child("ModalManager")),this.formLifecycleManager=new Z(this.modalManager),this.loadingIndicator=new X,this.formDisabler=new Y,this.uploadHandler=new ne(l=>this.send(l),{chunkSize:256*1024,onProgress:l=>{this.wrapperElement&&this.wrapperElement.dispatchEvent(new CustomEvent("lvt:upload:progress",{detail:{entry:l}}))},onComplete:(l,c)=>{this.logger.info(`Upload complete: ${l}`,c),this.wrapperElement&&this.wrapperElement.dispatchEvent(new CustomEvent("lvt:upload:complete",{detail:{uploadName:l,entries:c}}))},onError:(l,c)=>{this.logger.error(`Upload error for ${l.id}:`,c),this.wrapperElement&&this.wrapperElement.dispatchEvent(new CustomEvent("lvt:upload:error",{detail:{entry:l,error:c}}))}}),this.eventDelegator=new q({getWrapperElement:()=>this.wrapperElement,getRateLimitedHandlers:()=>this.rateLimitedHandlers,parseValue:l=>this.parseValue(l),send:l=>this.send(l),setActiveSubmission:(l,c,d)=>this.formLifecycleManager.setActiveSubmission(l,c,d),openModal:l=>this.modalManager.open(l),closeModal:l=>this.modalManager.close(l),getWebSocketReadyState:()=>this.webSocketManager.getReadyState(),triggerPendingUploads:l=>this.uploadHandler.triggerPendingUploads(l)},this.logger.child("EventDelegator")),this.observerManager=new G({getWrapperElement:()=>this.wrapperElement,send:l=>this.send(l)},this.logger.child("ObserverManager")),this.webSocketManager=new ee({options:this.options,logger:this.logger.child("Transport"),onConnected:()=>{var l,c,d;this.ws=this.webSocketManager.getSocket(),this.logger.info("WebSocket connected"),(c=(l=this.options).onConnect)==null||c.call(l),(d=this.wrapperElement)==null||d.dispatchEvent(new Event("lvt:connected"))},onDisconnected:()=>{var l,c,d;this.ws=null,this.logger.info("WebSocket disconnected"),(c=(l=this.options).onDisconnect)==null||c.call(l),(d=this.wrapperElement)==null||d.dispatchEvent(new Event("lvt:disconnected"))},onMessage:(l,c)=>{this.handleWebSocketPayload(l,c)},onReconnectAttempt:()=>{this.logger.info("Attempting to reconnect...")},onError:l=>{var c,d;this.logger.error("WebSocket error:",l),(d=(c=this.options).onError)==null||d.call(c,l)}})}static autoInit(){let e=he({scope:"Client:autoInit"}),t=()=>{let n=document.querySelector("[data-lvt-id]");if(n){let r=new a;r.wrapperElement=n,n.getAttribute("data-lvt-loading")==="true"&&(r.loadingIndicator.show(),r.formDisabler.disable(r.wrapperElement)),r.connect().catch(o=>{e.error("Auto-initialization connect failed:",o)}),window.liveTemplateClient=r}};document.readyState==="loading"?document.addEventListener("DOMContentLoaded",t):t()}handleWebSocketPayload(e,t){var r,s;t&&(window.__lastWSMessage=t.data,window.__wsMessages||(window.__wsMessages=[]),window.__wsMessages.push(e));let n=e;if(n.type==="upload_progress"){this.uploadHandler.handleProgressMessage(n);return}if(n.upload_name&&n.entries){let o=n;try{this.handleUploadStartResponse(o)}catch(i){this.logger.error("Error handling upload start response:",i)}return}if(n.upload_name&&n.hasOwnProperty("success")){n.success?this.logger.info(`Upload complete: ${n.upload_name}`):this.logger.error(`Upload failed: ${n.upload_name}`,n.error);return}this.isInitialized||(this.loadingIndicator.hide(),this.formDisabler.enable(this.wrapperElement),this.wrapperElement&&this.wrapperElement.hasAttribute("data-lvt-loading")&&this.wrapperElement.removeAttribute("data-lvt-loading"),this.isInitialized=!0),this.wrapperElement&&(this.updateDOM(this.wrapperElement,e.tree,e.meta),this.messageCount++,window.__wsMessageCount=this.messageCount,this.wrapperElement.dispatchEvent(new CustomEvent("lvt:updated",{detail:{messageCount:this.messageCount,action:(r=e.meta)==null?void 0:r.action,success:(s=e.meta)==null?void 0:s.success}})))}async connect(e="[data-lvt-id]"){var n,r;if(this.wrapperElement=document.querySelector(e),!this.wrapperElement)throw new Error(`LiveTemplate wrapper not found with selector: ${e}`);this.webSocketManager.disconnect();let t=await this.webSocketManager.connect();this.useHTTP=!t.usingWebSocket,this.useHTTP&&(this.ws=null,this.logger.info("WebSocket not available, using HTTP mode"),(r=(n=this.options).onConnect)==null||r.call(n),t.initialState&&this.wrapperElement&&this.handleWebSocketPayload(t.initialState)),this.eventDelegator.setupEventDelegation(),this.eventDelegator.setupWindowEventDelegation(),this.eventDelegator.setupClickAwayDelegation(),this.eventDelegator.setupModalDelegation(),this.focusManager.attach(this.wrapperElement),this.observerManager.setupInfiniteScrollObserver(),this.observerManager.setupInfiniteScrollMutationObserver()}disconnect(){this.webSocketManager.disconnect(),this.ws=null,this.useHTTP=!1,this.observerManager.teardown(),this.formLifecycleManager.reset(),this.loadingIndicator.hide(),this.formDisabler.enable(this.wrapperElement)}isReady(){let e=this.wrapperElement;return!e||e.hasAttribute("data-lvt-loading")?!1:this.useHTTP?!0:this.webSocketManager.getReadyState()===1}send(e){window.__lvtSendCalled=!0,window.__lvtMessageAction=e==null?void 0:e.action;let t=this.webSocketManager.getReadyState();this.logger.isDebugEnabled()&&this.logger.debug("send() invoked",{message:e,useHTTP:this.useHTTP,hasWebSocket:t!==void 0,readyState:t}),this.useHTTP?(this.logger.debug("Using HTTP mode for send"),window.__lvtSendPath="http",this.sendHTTP(e)):t===1?(this.logger.debug("Sending via WebSocket"),window.__lvtSendPath="websocket",window.__lvtWSMessage=JSON.stringify(e),this.webSocketManager.send(JSON.stringify(e)),this.logger.debug("WebSocket send complete"),window.__lvtWSSendComplete=!0):t!==void 0?(this.logger.warn(`WebSocket not ready (state: ${t}), using HTTP fallback`),window.__lvtSendPath="http-fallback",this.sendHTTP(e)):(this.logger.error("No transport available"),window.__lvtSendPath="no-transport")}async sendHTTP(e){try{let t=this.options.liveUrl||"/live",n=await fetch(t,{method:"POST",credentials:"include",headers:{"Content-Type":"application/json",Accept:"application/json"},body:JSON.stringify(e)});if(!n.ok)throw new Error(`HTTP request failed: ${n.status}`);let r=await n.json();this.wrapperElement&&this.updateDOM(this.wrapperElement,r.tree,r.meta)}catch(t){this.logger.error("Failed to send HTTP request:",t)}}parseValue(e){let t=parseFloat(e);return!isNaN(t)&&e.trim()===t.toString()?t:e==="true"?!0:e==="false"?!1:e}applyUpdate(e){return this.treeRenderer.applyUpdate(e)}applyUpdateToHTML(e,t){let n=this.applyUpdate(t);if(!this.lvtId){let c=e.match(/data-lvt-id="([^"]+)"/);c&&(this.lvtId=c[1])}let r=n.html;if(!e.match(/<body>([\s\S]*?)<\/body>/))return e;let l=`<div data-lvt-id="${this.lvtId||"lvt-unknown"}">`+r+"</div>";return e.replace(/<body>[\s\S]*?<\/body>/,`<body>${l}</body>`)}updateDOM(e,t,n){let r=this.applyUpdate(t),s=i=>!i||typeof i!="object"?!1:i.s&&Array.isArray(i.s)?!0:Object.values(i).some(l=>s(l));if(!r.changed&&!s(t))return;let o=document.createElement(e.tagName);this.logger.isDebugEnabled()&&(this.logger.debug("[updateDOM] element.tagName:",e.tagName),this.logger.debug("[updateDOM] result.html (first 500 chars):",r.html.substring(0,500)),this.logger.debug("[updateDOM] Has <table> tag:",r.html.includes("<table>")),this.logger.debug("[updateDOM] Has <tbody> tag:",r.html.includes("<tbody>")),this.logger.debug("[updateDOM] Has <tr> tag:",r.html.includes("<tr"))),o.innerHTML=r.html,this.logger.isDebugEnabled()&&(this.logger.debug("[updateDOM] tempWrapper.innerHTML (first 500 chars):",o.innerHTML.substring(0,500)),this.logger.debug("[updateDOM] tempWrapper has <table>:",o.innerHTML.includes("<table>")),this.logger.debug("[updateDOM] tempWrapper has <tbody>:",o.innerHTML.includes("<tbody>")),this.logger.debug("[updateDOM] tempWrapper has <tr>:",o.innerHTML.includes("<tr"))),Se(e,o,{childrenOnly:!0,getNodeKey:i=>{if(i.nodeType===1)return i.getAttribute("data-key")||i.getAttribute("data-lvt-key")||void 0},onBeforeElUpdated:(i,l)=>{let c=this.focusManager.getLastFocusedElement();return c&&this.focusManager.isTextualInput(i)&&i===c&&(l.value=i.value),i.isEqualNode(l)?!1:(this.executeLifecycleHook(i,"lvt-updated"),!0)},onNodeAdded:i=>{i.nodeType===Node.ELEMENT_NODE&&this.executeLifecycleHook(i,"lvt-mounted")},onBeforeNodeDiscarded:i=>(i.nodeType===Node.ELEMENT_NODE&&this.executeLifecycleHook(i,"lvt-destroyed"),!0)}),this.focusManager.restoreFocusedElement(),Me(e),ke(e),Ae(e),this.uploadHandler.initializeFileInputs(e),n&&this.formLifecycleManager.handleResponse(n)}handleUploadStartResponse(e){this.uploadHandler.handleUploadStartResponse(e)}executeLifecycleHook(e,t){let n=e.getAttribute(t);if(n)try{new Function("element",n).call(e,e)}catch(r){this.logger.error(`Error executing ${t} hook:`,r)}}reset(){this.treeRenderer.reset(),this.focusManager.reset(),this.observerManager.teardown(),this.formLifecycleManager.reset(),this.loadingIndicator.hide(),this.formDisabler.enable(this.wrapperElement),this.lvtId=null}getTreeState(){return this.treeRenderer.getTreeState()}getStaticStructure(){return this.treeRenderer.getStaticStructure()}};typeof window!="undefined"&&re.autoInit();return Ie(Qe);})();
`),l=Math.max(o.length,i.length);for(let c=0;c<l;c++){let d=o[c]||"",u=i[c]||"";d!==u&&(t.push(`Line ${c+1}:`),t.push(` Expected: ${d}`),t.push(` Actual: ${u}`))}return{match:!1,differences:t}}var ie=class a{constructor(e={}){this.lvtId=null;this.ws=null;this.wrapperElement=null;this.useHTTP=!1;this.sessionCookie=null;this.rateLimitedHandlers=new WeakMap;this.isInitialized=!1;this.messageCount=0;let{logger:t,logLevel:n,debug:r,...s}=e,o=n!=null?n:r?"debug":"info",i=t!=null?t:me({level:o});t?n?t.setLevel(n):r&&t.setLevel("debug"):i.setLevel(o),this.logger=i.child("Client"),this.options={autoReconnect:!1,reconnectDelay:1e3,liveUrl:window.location.pathname,...s},this.treeRenderer=new ee(this.logger.child("TreeRenderer")),this.focusManager=new q(this.logger.child("FocusManager")),this.modalManager=new X(this.logger.child("ModalManager")),this.formLifecycleManager=new te(this.modalManager),this.loadingIndicator=new Y,this.formDisabler=new Q,this.uploadHandler=new se(l=>this.send(l),{chunkSize:256*1024,onProgress:l=>{this.wrapperElement&&this.wrapperElement.dispatchEvent(new CustomEvent("lvt:upload:progress",{detail:{entry:l}}))},onComplete:(l,c)=>{this.logger.info(`Upload complete: ${l}`,c),this.wrapperElement&&this.wrapperElement.dispatchEvent(new CustomEvent("lvt:upload:complete",{detail:{uploadName:l,entries:c}}))},onError:(l,c)=>{this.logger.error(`Upload error for ${l.id}:`,c),this.wrapperElement&&this.wrapperElement.dispatchEvent(new CustomEvent("lvt:upload:error",{detail:{entry:l,error:c}}))}}),this.eventDelegator=new J({getWrapperElement:()=>this.wrapperElement,getRateLimitedHandlers:()=>this.rateLimitedHandlers,parseValue:l=>this.parseValue(l),send:l=>this.send(l),setActiveSubmission:(l,c,d)=>this.formLifecycleManager.setActiveSubmission(l,c,d),openModal:l=>this.modalManager.open(l),closeModal:l=>this.modalManager.close(l),getWebSocketReadyState:()=>this.webSocketManager.getReadyState(),triggerPendingUploads:l=>this.uploadHandler.triggerPendingUploads(l)},this.logger.child("EventDelegator")),this.observerManager=new G({getWrapperElement:()=>this.wrapperElement,send:l=>this.send(l)},this.logger.child("ObserverManager")),this.webSocketManager=new ne({options:this.options,logger:this.logger.child("Transport"),onConnected:()=>{var l,c,d;this.ws=this.webSocketManager.getSocket(),this.logger.info("WebSocket connected"),(c=(l=this.options).onConnect)==null||c.call(l),(d=this.wrapperElement)==null||d.dispatchEvent(new Event("lvt:connected"))},onDisconnected:()=>{var l,c,d;this.ws=null,this.logger.info("WebSocket disconnected"),(c=(l=this.options).onDisconnect)==null||c.call(l),(d=this.wrapperElement)==null||d.dispatchEvent(new Event("lvt:disconnected"))},onMessage:(l,c)=>{this.handleWebSocketPayload(l,c)},onReconnectAttempt:()=>{this.logger.info("Attempting to reconnect...")},onError:l=>{var c,d;this.logger.error("WebSocket error:",l),(d=(c=this.options).onError)==null||d.call(c,l)}})}static autoInit(){let e=me({scope:"Client:autoInit"}),t=()=>{let n=document.querySelector("[data-lvt-id]");if(n){let r=new a;r.wrapperElement=n,n.getAttribute("data-lvt-loading")==="true"&&(r.loadingIndicator.show(),r.formDisabler.disable(r.wrapperElement)),r.connect().catch(o=>{e.error("Auto-initialization connect failed:",o)}),window.liveTemplateClient=r}};document.readyState==="loading"?document.addEventListener("DOMContentLoaded",t):t()}handleWebSocketPayload(e,t){var r,s;t&&(window.__lastWSMessage=t.data,window.__wsMessages||(window.__wsMessages=[]),window.__wsMessages.push(e));let n=e;if(n.type==="upload_progress"){this.uploadHandler.handleProgressMessage(n);return}if(n.upload_name&&n.entries){let o=n;try{this.handleUploadStartResponse(o)}catch(i){this.logger.error("Error handling upload start response:",i)}return}if(n.upload_name&&n.hasOwnProperty("success")){n.success?this.logger.info(`Upload complete: ${n.upload_name}`):this.logger.error(`Upload failed: ${n.upload_name}`,n.error);return}this.isInitialized||(this.loadingIndicator.hide(),this.formDisabler.enable(this.wrapperElement),this.wrapperElement&&this.wrapperElement.hasAttribute("data-lvt-loading")&&this.wrapperElement.removeAttribute("data-lvt-loading"),this.isInitialized=!0),this.wrapperElement&&(this.updateDOM(this.wrapperElement,e.tree,e.meta),this.messageCount++,window.__wsMessageCount=this.messageCount,this.wrapperElement.dispatchEvent(new CustomEvent("lvt:updated",{detail:{messageCount:this.messageCount,action:(r=e.meta)==null?void 0:r.action,success:(s=e.meta)==null?void 0:s.success}})))}async connect(e="[data-lvt-id]"){var n,r;if(this.wrapperElement=document.querySelector(e),!this.wrapperElement)throw new Error(`LiveTemplate wrapper not found with selector: ${e}`);this.webSocketManager.disconnect();let t=await this.webSocketManager.connect();this.useHTTP=!t.usingWebSocket,this.useHTTP&&(this.ws=null,this.logger.info("WebSocket not available, using HTTP mode"),(r=(n=this.options).onConnect)==null||r.call(n),t.initialState&&this.wrapperElement&&this.handleWebSocketPayload(t.initialState)),this.eventDelegator.setupEventDelegation(),this.eventDelegator.setupWindowEventDelegation(),this.eventDelegator.setupClickAwayDelegation(),this.eventDelegator.setupModalDelegation(),this.eventDelegator.setupFocusTrapDelegation(),this.eventDelegator.setupAutofocusDelegation(),Z(),this.focusManager.attach(this.wrapperElement),this.observerManager.setupInfiniteScrollObserver(),this.observerManager.setupInfiniteScrollMutationObserver()}disconnect(){this.webSocketManager.disconnect(),this.ws=null,this.useHTTP=!1,this.observerManager.teardown(),this.formLifecycleManager.reset(),this.loadingIndicator.hide(),this.formDisabler.enable(this.wrapperElement)}isReady(){let e=this.wrapperElement;return!e||e.hasAttribute("data-lvt-loading")?!1:this.useHTTP?!0:this.webSocketManager.getReadyState()===1}send(e){window.__lvtSendCalled=!0,window.__lvtMessageAction=e==null?void 0:e.action;let t=this.webSocketManager.getReadyState();this.logger.isDebugEnabled()&&this.logger.debug("send() invoked",{message:e,useHTTP:this.useHTTP,hasWebSocket:t!==void 0,readyState:t}),this.useHTTP?(this.logger.debug("Using HTTP mode for send"),window.__lvtSendPath="http",this.sendHTTP(e)):t===1?(this.logger.debug("Sending via WebSocket"),window.__lvtSendPath="websocket",window.__lvtWSMessage=JSON.stringify(e),this.webSocketManager.send(JSON.stringify(e)),this.logger.debug("WebSocket send complete"),window.__lvtWSSendComplete=!0):t!==void 0?(this.logger.warn(`WebSocket not ready (state: ${t}), using HTTP fallback`),window.__lvtSendPath="http-fallback",this.sendHTTP(e)):(this.logger.error("No transport available"),window.__lvtSendPath="no-transport")}async sendHTTP(e){try{let t=this.options.liveUrl||"/live",n=await fetch(t,{method:"POST",credentials:"include",headers:{"Content-Type":"application/json",Accept:"application/json"},body:JSON.stringify(e)});if(!n.ok)throw new Error(`HTTP request failed: ${n.status}`);let r=await n.json();this.wrapperElement&&this.updateDOM(this.wrapperElement,r.tree,r.meta)}catch(t){this.logger.error("Failed to send HTTP request:",t)}}parseValue(e){let t=parseFloat(e);return!isNaN(t)&&e.trim()===t.toString()?t:e==="true"?!0:e==="false"?!1:e}applyUpdate(e){return this.treeRenderer.applyUpdate(e)}applyUpdateToHTML(e,t){let n=this.applyUpdate(t);if(!this.lvtId){let c=e.match(/data-lvt-id="([^"]+)"/);c&&(this.lvtId=c[1])}let r=n.html;if(!e.match(/<body>([\s\S]*?)<\/body>/))return e;let l=`<div data-lvt-id="${this.lvtId||"lvt-unknown"}">`+r+"</div>";return e.replace(/<body>[\s\S]*?<\/body>/,`<body>${l}</body>`)}updateDOM(e,t,n){let r=this.applyUpdate(t),s=i=>!i||typeof i!="object"?!1:i.s&&Array.isArray(i.s)?!0:Object.values(i).some(l=>s(l));if(!r.changed&&!s(t))return;let o=document.createElement(e.tagName);this.logger.isDebugEnabled()&&(this.logger.debug("[updateDOM] element.tagName:",e.tagName),this.logger.debug("[updateDOM] result.html (first 500 chars):",r.html.substring(0,500)),this.logger.debug("[updateDOM] Has <table> tag:",r.html.includes("<table>")),this.logger.debug("[updateDOM] Has <tbody> tag:",r.html.includes("<tbody>")),this.logger.debug("[updateDOM] Has <tr> tag:",r.html.includes("<tr"))),o.innerHTML=r.html,this.logger.isDebugEnabled()&&(this.logger.debug("[updateDOM] tempWrapper.innerHTML (first 500 chars):",o.innerHTML.substring(0,500)),this.logger.debug("[updateDOM] tempWrapper has <table>:",o.innerHTML.includes("<table>")),this.logger.debug("[updateDOM] tempWrapper has <tbody>:",o.innerHTML.includes("<tbody>")),this.logger.debug("[updateDOM] tempWrapper has <tr>:",o.innerHTML.includes("<tr"))),Me(e,o,{childrenOnly:!0,getNodeKey:i=>{if(i.nodeType===1)return i.getAttribute("data-key")||i.getAttribute("data-lvt-key")||void 0},onBeforeElUpdated:(i,l)=>{let c=this.focusManager.getLastFocusedElement();return c&&this.focusManager.isTextualInput(i)&&i===c&&(l.value=i.value),i.isEqualNode(l)?!1:(this.executeLifecycleHook(i,"lvt-updated"),!0)},onNodeAdded:i=>{i.nodeType===Node.ELEMENT_NODE&&this.executeLifecycleHook(i,"lvt-mounted")},onBeforeNodeDiscarded:i=>(i.nodeType===Node.ELEMENT_NODE&&this.executeLifecycleHook(i,"lvt-destroyed"),!0)}),this.focusManager.restoreFocusedElement(),Te(e),Le(e),ke(e),this.uploadHandler.initializeFileInputs(e),n&&this.formLifecycleManager.handleResponse(n)}handleUploadStartResponse(e){this.uploadHandler.handleUploadStartResponse(e)}executeLifecycleHook(e,t){let n=e.getAttribute(t);if(n)try{new Function("element",n).call(e,e)}catch(r){this.logger.error(`Error executing ${t} hook:`,r)}}reset(){this.treeRenderer.reset(),this.focusManager.reset(),this.observerManager.teardown(),this.formLifecycleManager.reset(),this.loadingIndicator.hide(),this.formDisabler.enable(this.wrapperElement),this.lvtId=null}getTreeState(){return this.treeRenderer.getTreeState()}getStaticStructure(){return this.treeRenderer.getStaticStructure()}};typeof window!="undefined"&&ie.autoInit();return Be(lt);})();

Copilot AI Dec 22, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Avoid automated semicolon insertion (90% of all statements in the enclosing function have an explicit semicolon).

Copilot uses AI. Check for mistakes.
Comment thread testing/livetemplate-client.browser.js Outdated
@@ -1,4 +1,4 @@
"use strict";var LiveTemplateClient=(()=>{var ae=Object.defineProperty;var _e=Object.getOwnPropertyDescriptor;var Ce=Object.getOwnPropertyNames;var Re=Object.prototype.hasOwnProperty;var He=(a,e)=>{for(var t in e)ae(a,t,{get:e[t],enumerable:!0})},Oe=(a,e,t,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of Ce(e))!Re.call(a,r)&&r!==t&&ae(a,r,{get:()=>e[r],enumerable:!(n=_e(e,r))||n.enumerable});return a};var Ie=a=>Oe(ae({},"__esModule",{value:!0}),a);var Qe={};He(Qe,{LiveTemplateClient:()=>re,compareHTML:()=>Fe,loadAndApplyUpdate:()=>xe});var ve=11;function De(a,e){var t=e.attributes,n,r,s,o,i;if(!(e.nodeType===ve||a.nodeType===ve)){for(var l=t.length-1;l>=0;l--)n=t[l],r=n.name,s=n.namespaceURI,o=n.value,s?(r=n.localName||r,i=a.getAttributeNS(s,r),i!==o&&(n.prefix==="xmlns"&&(r=n.name),a.setAttributeNS(s,r,o))):(i=a.getAttribute(r),i!==o&&a.setAttribute(r,o));for(var c=a.attributes,d=c.length-1;d>=0;d--)n=c[d],r=n.name,s=n.namespaceURI,s?(r=n.localName||r,e.hasAttributeNS(s,r)||a.removeAttributeNS(s,r)):e.hasAttribute(r)||a.removeAttribute(r)}}var K,Ne="http://www.w3.org/1999/xhtml",C=typeof document=="undefined"?void 0:document,We=!!C&&"content"in C.createElement("template"),$e=!!C&&C.createRange&&"createContextualFragment"in C.createRange();function Pe(a){var e=C.createElement("template");return e.innerHTML=a,e.content.childNodes[0]}function Be(a){K||(K=C.createRange(),K.selectNode(C.body));var e=K.createContextualFragment(a);return e.childNodes[0]}function Ve(a){var e=C.createElement("body");return e.innerHTML=a,e.childNodes[0]}function Ke(a){return a=a.trim(),We?Pe(a):$e?Be(a):Ve(a)}function j(a,e){var t=a.nodeName,n=e.nodeName,r,s;return t===n?!0:(r=t.charCodeAt(0),s=n.charCodeAt(0),r<=90&&s>=97?t===n.toUpperCase():s<=90&&r>=97?n===t.toUpperCase():!1)}function je(a,e){return!e||e===Ne?C.createElement(a):C.createElementNS(e,a)}function ze(a,e){for(var t=a.firstChild;t;){var n=t.nextSibling;e.appendChild(t),t=n}return e}function le(a,e,t){a[t]!==e[t]&&(a[t]=e[t],a[t]?a.setAttribute(t,""):a.removeAttribute(t))}var be={OPTION:function(a,e){var t=a.parentNode;if(t){var n=t.nodeName.toUpperCase();n==="OPTGROUP"&&(t=t.parentNode,n=t&&t.nodeName.toUpperCase()),n==="SELECT"&&!t.hasAttribute("multiple")&&(a.hasAttribute("selected")&&!e.selected&&(a.setAttribute("selected","selected"),a.removeAttribute("selected")),t.selectedIndex=-1)}le(a,e,"selected")},INPUT:function(a,e){le(a,e,"checked"),le(a,e,"disabled"),a.value!==e.value&&(a.value=e.value),e.hasAttribute("value")||a.removeAttribute("value")},TEXTAREA:function(a,e){var t=e.value;a.value!==t&&(a.value=t);var n=a.firstChild;if(n){var r=n.nodeValue;if(r==t||!t&&r==a.placeholder)return;n.nodeValue=t}},SELECT:function(a,e){if(!e.hasAttribute("multiple")){for(var t=-1,n=0,r=a.firstChild,s,o;r;)if(o=r.nodeName&&r.nodeName.toUpperCase(),o==="OPTGROUP")s=r,r=s.firstChild,r||(r=s.nextSibling,s=null);else{if(o==="OPTION"){if(r.hasAttribute("selected")){t=n;break}n++}r=r.nextSibling,!r&&s&&(r=s.nextSibling,s=null)}a.selectedIndex=t}}},P=1,ye=11,Ee=3,we=8;function N(){}function qe(a){if(a)return a.getAttribute&&a.getAttribute("id")||a.id}function Ge(a){return function(t,n,r){if(r||(r={}),typeof n=="string")if(t.nodeName==="#document"||t.nodeName==="HTML"||t.nodeName==="BODY"){var s=n;n=C.createElement("html"),n.innerHTML=s}else n=Ke(n);else n.nodeType===ye&&(n=n.firstElementChild);var o=r.getNodeKey||qe,i=r.onBeforeNodeAdded||N,l=r.onNodeAdded||N,c=r.onBeforeElUpdated||N,d=r.onElUpdated||N,M=r.onBeforeNodeDiscarded||N,m=r.onNodeDiscarded||N,f=r.onBeforeElChildrenUpdated||N,F=r.skipFromChildren||N,L=r.addChild||function(p,g){return p.appendChild(g)},b=r.childrenOnly===!0,h=Object.create(null),u=[];function y(p){u.push(p)}function U(p,g){if(p.nodeType===P)for(var T=p.firstChild;T;){var v=void 0;g&&(v=o(T))?y(v):(m(T),T.firstChild&&U(T,g)),T=T.nextSibling}}function x(p,g,T){M(p)!==!1&&(g&&g.removeChild(p),m(p),U(p,T))}function w(p){if(p.nodeType===P||p.nodeType===ye)for(var g=p.firstChild;g;){var T=o(g);T&&(h[T]=g),w(g),g=g.nextSibling}}w(t);function S(p){l(p);for(var g=p.firstChild;g;){var T=g.nextSibling,v=o(g);if(v){var E=h[v];E&&j(g,E)?(g.parentNode.replaceChild(E,g),_(E,g)):S(g)}else S(g);g=T}}function k(p,g,T){for(;g;){var v=g.nextSibling;(T=o(g))?y(T):x(g,p,!0),g=v}}function _(p,g,T){var v=o(g);if(v&&delete h[v],!T){var E=c(p,g);if(E===!1||(E instanceof HTMLElement&&(p=E,w(p)),a(p,g),d(p),f(p,g)===!1))return}p.nodeName!=="TEXTAREA"?H(p,g):be.TEXTAREA(p,g)}function H(p,g){var T=F(p,g),v=g.firstChild,E=p.firstChild,W,R,$,B,I;e:for(;v;){for(B=v.nextSibling,W=o(v);!T&&E;){if($=E.nextSibling,v.isSameNode&&v.isSameNode(E)){v=B,E=$;continue e}R=o(E);var V=E.nodeType,D=void 0;if(V===v.nodeType&&(V===P?(W?W!==R&&((I=h[W])?$===I?D=!1:(p.insertBefore(I,E),R?y(R):x(E,p,!0),E=I,R=o(E)):D=!1):R&&(D=!1),D=D!==!1&&j(E,v),D&&_(E,v)):(V===Ee||V==we)&&(D=!0,E.nodeValue!==v.nodeValue&&(E.nodeValue=v.nodeValue))),D){v=B,E=$;continue e}R?y(R):x(E,p,!0),E=$}if(W&&(I=h[W])&&j(I,v))T||L(p,I),_(I,v);else{var oe=i(v);oe!==!1&&(oe&&(v=oe),v.actualize&&(v=v.actualize(p.ownerDocument||C)),L(p,v),S(v))}v=B,E=$}k(p,E,R);var me=be[p.nodeName];me&&me(p,g)}var A=t,O=A.nodeType,fe=n.nodeType;if(!b){if(O===P)fe===P?j(t,n)||(m(t),A=ze(t,je(n.nodeName,n.namespaceURI))):A=n;else if(O===Ee||O===we){if(fe===O)return A.nodeValue!==n.nodeValue&&(A.nodeValue=n.nodeValue),A;A=n}}if(A===n)m(t);else{if(n.isSameNode&&n.isSameNode(A))return;if(_(A,n,b),u)for(var se=0,Ue=u.length;se<Ue;se++){var ie=h[u[se]];ie&&x(ie,ie.parentNode,!1)}}return!b&&A!==t&&t.parentNode&&(A.actualize&&(A=A.actualize(t.ownerDocument||C)),t.parentNode.replaceChild(A,t)),A}}var Je=Ge(De),Se=Je;var ce=["text","textarea","number","email","password","search","tel","url","date","time","datetime-local","color","range"];var z=class{constructor(e){this.logger=e;this.wrapperElement=null;this.focusableElements=[];this.lastFocusedElement=null;this.lastFocusedSelectionStart=null;this.lastFocusedSelectionEnd=null}attach(e){this.wrapperElement=e,e&&(this.updateFocusableElements(),this.setupFocusTracking())}reset(){this.wrapperElement=null,this.focusableElements=[],this.lastFocusedElement=null,this.lastFocusedSelectionStart=null,this.lastFocusedSelectionEnd=null}updateFocusableElements(){if(!this.wrapperElement)return;let n=`${ce.map(r=>r==="textarea"?"textarea:not([disabled])":`input[type="${r}"]:not([disabled])`).join(", ")}, select:not([disabled]), button:not([disabled]), [contenteditable="true"], [tabindex]:not([tabindex="-1"])`;this.focusableElements=Array.from(this.wrapperElement.querySelectorAll(n))}setupFocusTracking(){if(!this.wrapperElement)return;let e=this.wrapperElement.getAttribute("data-lvt-id"),t=`__lvt_focus_tracker_${e}`,n=`__lvt_blur_tracker_${e}`,r=o=>{var l;let i=o.target;!i||!((l=this.wrapperElement)!=null&&l.contains(i))||(this.isTextualInput(i)||i instanceof HTMLSelectElement)&&(this.lastFocusedElement=i,this.logger.debug("[Focus] Tracked focus on:",i.tagName,i.id||i.getAttribute("name")),this.isTextualInput(i)&&(this.lastFocusedSelectionStart=i.selectionStart,this.lastFocusedSelectionEnd=i.selectionEnd))},s=o=>{var l;let i=o.target;!i||!((l=this.wrapperElement)!=null&&l.contains(i))||this.isTextualInput(i)&&i===this.lastFocusedElement&&(this.lastFocusedSelectionStart=i.selectionStart,this.lastFocusedSelectionEnd=i.selectionEnd,this.logger.debug("[Focus] Saved cursor on blur:",this.lastFocusedSelectionStart,"-",this.lastFocusedSelectionEnd))};document[t]&&document.removeEventListener("focus",document[t],!0),document[n]&&document.removeEventListener("blur",document[n],!0),document[t]=r,document[n]=s,document.addEventListener("focus",r,!0),document.addEventListener("blur",s,!0),this.logger.debug("[Focus] Focus tracking set up")}restoreFocusedElement(){var r,s,o;if(this.logger.debug("[Focus] restoreFocusedElement - lastFocusedElement:",(r=this.lastFocusedElement)==null?void 0:r.tagName,((s=this.lastFocusedElement)==null?void 0:s.id)||((o=this.lastFocusedElement)==null?void 0:o.getAttribute("name"))),!this.lastFocusedElement||!this.wrapperElement){this.logger.debug("[Focus] No element to restore");return}let e=this.getElementSelector(this.lastFocusedElement);if(this.logger.debug("[Focus] Selector for last focused:",e),!e){this.logger.debug("[Focus] Could not generate selector");return}let t=null;if(e.startsWith("data-focus-index-")){this.updateFocusableElements();let i=parseInt(e.replace("data-focus-index-",""),10);t=this.focusableElements[i]||null,this.logger.debug("[Focus] Found by index:",i,t==null?void 0:t.tagName)}else t=this.wrapperElement.querySelector(e),this.logger.debug("[Focus] Found by selector:",e,t==null?void 0:t.tagName);if(!t){this.logger.debug("[Focus] Element not found in updated DOM");return}let n=t.matches(":focus");this.logger.debug("[Focus] Already focused:",n),n||(t.focus(),this.logger.debug("[Focus] Restored focus")),this.isTextualInput(t)&&this.lastFocusedSelectionStart!==null&&this.lastFocusedSelectionEnd!==null&&(t.setSelectionRange(this.lastFocusedSelectionStart,this.lastFocusedSelectionEnd),this.logger.debug("[Focus] Restored cursor:",this.lastFocusedSelectionStart,"-",this.lastFocusedSelectionEnd))}isTextualInput(e){return e instanceof HTMLTextAreaElement?!0:e instanceof HTMLInputElement?ce.indexOf(e.type)>=0:!1}getLastFocusedElement(){return this.lastFocusedElement}getElementSelector(e){if(e.id)return`#${e.id}`;if(e.name)return`[name="${e.name}"]`;if(e.getAttribute("data-key"))return`[data-key="${e.getAttribute("data-key")}"]`;let t=this.focusableElements.indexOf(e);return t>=0?`data-focus-index-${t}`:null}};function Me(a){a.querySelectorAll("[lvt-scroll]").forEach(t=>{let n=t,r=n.getAttribute("lvt-scroll"),s=n.getAttribute("lvt-scroll-behavior")||"auto",o=parseInt(n.getAttribute("lvt-scroll-threshold")||"100",10);if(r)switch(r){case"bottom":n.scrollTo({top:n.scrollHeight,behavior:s});break;case"bottom-sticky":{n.scrollHeight-n.scrollTop-n.clientHeight<=o&&n.scrollTo({top:n.scrollHeight,behavior:s});break}case"top":n.scrollTo({top:0,behavior:s});break;case"preserve":break;default:console.warn(`Unknown lvt-scroll mode: ${r}`)}})}function ke(a){a.querySelectorAll("[lvt-highlight]").forEach(t=>{let n=t.getAttribute("lvt-highlight"),r=parseInt(t.getAttribute("lvt-highlight-duration")||"500",10),s=t.getAttribute("lvt-highlight-color")||"#ffc107";if(!n)return;let o=t,i=o.style.backgroundColor,l=o.style.transition;o.style.transition=`background-color ${r}ms ease-out`,o.style.backgroundColor=s,setTimeout(()=>{o.style.backgroundColor=i,setTimeout(()=>{o.style.transition=l},r)},50)})}function Ae(a){if(a.querySelectorAll("[lvt-animate]").forEach(t=>{let n=t.getAttribute("lvt-animate"),r=parseInt(t.getAttribute("lvt-animate-duration")||"300",10);if(!n)return;let s=t;switch(s.style.setProperty("--lvt-animate-duration",`${r}ms`),n){case"fade":s.style.animation="lvt-fade-in var(--lvt-animate-duration) ease-out";break;case"slide":s.style.animation="lvt-slide-in var(--lvt-animate-duration) ease-out";break;case"scale":s.style.animation="lvt-scale-in var(--lvt-animate-duration) ease-out";break;default:console.warn(`Unknown lvt-animate mode: ${n}`)}s.addEventListener("animationend",()=>{s.style.animation=""},{once:!0})}),!document.getElementById("lvt-animate-styles")){let t=document.createElement("style");t.id="lvt-animate-styles",t.textContent=`
"use strict";var LiveTemplateClient=(()=>{var ce=Object.defineProperty;var Oe=Object.getOwnPropertyDescriptor;var Ne=Object.getOwnPropertyNames;var De=Object.prototype.hasOwnProperty;var We=(a,e)=>{for(var t in e)ce(a,t,{get:e[t],enumerable:!0})},$e=(a,e,t,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of Ne(e))!De.call(a,r)&&r!==t&&ce(a,r,{get:()=>e[r],enumerable:!(n=Oe(e,r))||n.enumerable});return a};var Be=a=>$e(ce({},"__esModule",{value:!0}),a);var lt={};We(lt,{LiveTemplateClient:()=>ie,checkLvtConfirm:()=>z,compareHTML:()=>He,extractLvtData:()=>xe,loadAndApplyUpdate:()=>Ie,setupReactiveAttributeListeners:()=>Z});var be=11;function Ke(a,e){var t=e.attributes,n,r,s,o,i;if(!(e.nodeType===be||a.nodeType===be)){for(var l=t.length-1;l>=0;l--)n=t[l],r=n.name,s=n.namespaceURI,o=n.value,s?(r=n.localName||r,i=a.getAttributeNS(s,r),i!==o&&(n.prefix==="xmlns"&&(r=n.name),a.setAttributeNS(s,r,o))):(i=a.getAttribute(r),i!==o&&a.setAttribute(r,o));for(var c=a.attributes,d=c.length-1;d>=0;d--)n=c[d],r=n.name,s=n.namespaceURI,s?(r=n.localName||r,e.hasAttributeNS(s,r)||a.removeAttributeNS(s,r)):e.hasAttribute(r)||a.removeAttribute(r)}}var V,Pe="http://www.w3.org/1999/xhtml",_=typeof document=="undefined"?void 0:document,Ve=!!_&&"content"in _.createElement("template"),je=!!_&&_.createRange&&"createContextualFragment"in _.createRange();function qe(a){var e=_.createElement("template");return e.innerHTML=a,e.content.childNodes[0]}function ze(a){V||(V=_.createRange(),V.selectNode(_.body));var e=V.createContextualFragment(a);return e.childNodes[0]}function Je(a){var e=_.createElement("body");return e.innerHTML=a,e.childNodes[0]}function Ge(a){return a=a.trim(),Ve?qe(a):je?ze(a):Je(a)}function j(a,e){var t=a.nodeName,n=e.nodeName,r,s;return t===n?!0:(r=t.charCodeAt(0),s=n.charCodeAt(0),r<=90&&s>=97?t===n.toUpperCase():s<=90&&r>=97?n===t.toUpperCase():!1)}function Xe(a,e){return!e||e===Pe?_.createElement(a):_.createElementNS(e,a)}function Ye(a,e){for(var t=a.firstChild;t;){var n=t.nextSibling;e.appendChild(t),t=n}return e}function de(a,e,t){a[t]!==e[t]&&(a[t]=e[t],a[t]?a.setAttribute(t,""):a.removeAttribute(t))}var Ee={OPTION:function(a,e){var t=a.parentNode;if(t){var n=t.nodeName.toUpperCase();n==="OPTGROUP"&&(t=t.parentNode,n=t&&t.nodeName.toUpperCase()),n==="SELECT"&&!t.hasAttribute("multiple")&&(a.hasAttribute("selected")&&!e.selected&&(a.setAttribute("selected","selected"),a.removeAttribute("selected")),t.selectedIndex=-1)}de(a,e,"selected")},INPUT:function(a,e){de(a,e,"checked"),de(a,e,"disabled"),a.value!==e.value&&(a.value=e.value),e.hasAttribute("value")||a.removeAttribute("value")},TEXTAREA:function(a,e){var t=e.value;a.value!==t&&(a.value=t);var n=a.firstChild;if(n){var r=n.nodeValue;if(r==t||!t&&r==a.placeholder)return;n.nodeValue=t}},SELECT:function(a,e){if(!e.hasAttribute("multiple")){for(var t=-1,n=0,r=a.firstChild,s,o;r;)if(o=r.nodeName&&r.nodeName.toUpperCase(),o==="OPTGROUP")s=r,r=s.firstChild,r||(r=s.nextSibling,s=null);else{if(o==="OPTION"){if(r.hasAttribute("selected")){t=n;break}n++}r=r.nextSibling,!r&&s&&(r=s.nextSibling,s=null)}a.selectedIndex=t}}},B=1,we=11,Se=3,Ae=8;function D(){}function Qe(a){if(a)return a.getAttribute&&a.getAttribute("id")||a.id}function Ze(a){return function(t,n,r){if(r||(r={}),typeof n=="string")if(t.nodeName==="#document"||t.nodeName==="HTML"||t.nodeName==="BODY"){var s=n;n=_.createElement("html"),n.innerHTML=s}else n=Ge(n);else n.nodeType===we&&(n=n.firstElementChild);var o=r.getNodeKey||Qe,i=r.onBeforeNodeAdded||D,l=r.onNodeAdded||D,c=r.onBeforeElUpdated||D,d=r.onElUpdated||D,u=r.onBeforeNodeDiscarded||D,g=r.onNodeDiscarded||D,f=r.onBeforeElChildrenUpdated||D,A=r.skipFromChildren||D,M=r.addChild||function(h,m){return h.appendChild(m)},y=r.childrenOnly===!0,v=Object.create(null),p=[];function E(h){p.push(h)}function R(h,m){if(h.nodeType===B)for(var x=h.firstChild;x;){var b=void 0;m&&(b=o(x))?E(b):(g(x),x.firstChild&&R(x,m)),x=x.nextSibling}}function C(h,m,x){u(h)!==!1&&(m&&m.removeChild(h),g(h),R(h,x))}function S(h){if(h.nodeType===B||h.nodeType===we)for(var m=h.firstChild;m;){var x=o(m);x&&(v[x]=m),S(m),m=m.nextSibling}}S(t);function T(h){l(h);for(var m=h.firstChild;m;){var x=m.nextSibling,b=o(m);if(b){var w=v[b];w&&j(m,w)?(m.parentNode.replaceChild(w,m),F(w,m)):T(m)}else T(m);m=x}}function L(h,m,x){for(;m;){var b=m.nextSibling;(x=o(m))?E(x):C(m,h,!0),m=b}}function F(h,m,x){var b=o(m);if(b&&delete v[b],!x){var w=c(h,m);if(w===!1||(w instanceof HTMLElement&&(h=w,S(h)),a(h,m),d(h),f(h,m)===!1))return}h.nodeName!=="TEXTAREA"?H(h,m):Ee.TEXTAREA(h,m)}function H(h,m){var x=A(h,m),b=m.firstChild,w=h.firstChild,W,I,$,K,O;e:for(;b;){for(K=b.nextSibling,W=o(b);!x&&w;){if($=w.nextSibling,b.isSameNode&&b.isSameNode(w)){b=K,w=$;continue e}I=o(w);var P=w.nodeType,N=void 0;if(P===b.nodeType&&(P===B?(W?W!==I&&((O=v[W])?$===O?N=!1:(h.insertBefore(O,w),I?E(I):C(w,h,!0),w=O,I=o(w)):N=!1):I&&(N=!1),N=N!==!1&&j(w,b),N&&F(w,b)):(P===Se||P==Ae)&&(N=!0,w.nodeValue!==b.nodeValue&&(w.nodeValue=b.nodeValue))),N){b=K,w=$;continue e}I?E(I):C(w,h,!0),w=$}if(W&&(O=v[W])&&j(O,b))x||M(h,O),F(O,b);else{var le=i(b);le!==!1&&(le&&(b=le),b.actualize&&(b=b.actualize(h.ownerDocument||_)),M(h,b),T(b))}b=K,w=$}L(h,w,I);var ye=Ee[h.nodeName];ye&&ye(h,m)}var k=t,U=k.nodeType,ve=n.nodeType;if(!y){if(U===B)ve===B?j(t,n)||(g(t),k=Ye(t,Xe(n.nodeName,n.namespaceURI))):k=n;else if(U===Se||U===Ae){if(ve===U)return k.nodeValue!==n.nodeValue&&(k.nodeValue=n.nodeValue),k;k=n}}if(k===n)g(t);else{if(n.isSameNode&&n.isSameNode(k))return;if(F(k,n,y),p)for(var oe=0,Ue=p.length;oe<Ue;oe++){var ae=v[p[oe]];ae&&C(ae,ae.parentNode,!1)}}return!y&&k!==t&&t.parentNode&&(k.actualize&&(k=k.actualize(t.ownerDocument||_)),t.parentNode.replaceChild(k,t)),k}}var et=Ze(Ke),Me=et;var ue=["text","textarea","number","email","password","search","tel","url","date","time","datetime-local","color","range"];var q=class{constructor(e){this.logger=e;this.wrapperElement=null;this.focusableElements=[];this.lastFocusedElement=null;this.lastFocusedSelectionStart=null;this.lastFocusedSelectionEnd=null}attach(e){this.wrapperElement=e,e&&(this.updateFocusableElements(),this.setupFocusTracking())}reset(){this.wrapperElement=null,this.focusableElements=[],this.lastFocusedElement=null,this.lastFocusedSelectionStart=null,this.lastFocusedSelectionEnd=null}updateFocusableElements(){if(!this.wrapperElement)return;let n=`${ue.map(r=>r==="textarea"?"textarea:not([disabled])":`input[type="${r}"]:not([disabled])`).join(", ")}, select:not([disabled]), button:not([disabled]), [contenteditable="true"], [tabindex]:not([tabindex="-1"])`;this.focusableElements=Array.from(this.wrapperElement.querySelectorAll(n))}setupFocusTracking(){if(!this.wrapperElement)return;let e=this.wrapperElement.getAttribute("data-lvt-id"),t=`__lvt_focus_tracker_${e}`,n=`__lvt_blur_tracker_${e}`,r=o=>{var l;let i=o.target;!i||!((l=this.wrapperElement)!=null&&l.contains(i))||(this.isTextualInput(i)||i instanceof HTMLSelectElement)&&(this.lastFocusedElement=i,this.logger.debug("[Focus] Tracked focus on:",i.tagName,i.id||i.getAttribute("name")),this.isTextualInput(i)&&(this.lastFocusedSelectionStart=i.selectionStart,this.lastFocusedSelectionEnd=i.selectionEnd))},s=o=>{var l;let i=o.target;!i||!((l=this.wrapperElement)!=null&&l.contains(i))||this.isTextualInput(i)&&i===this.lastFocusedElement&&(this.lastFocusedSelectionStart=i.selectionStart,this.lastFocusedSelectionEnd=i.selectionEnd,this.logger.debug("[Focus] Saved cursor on blur:",this.lastFocusedSelectionStart,"-",this.lastFocusedSelectionEnd))};document[t]&&document.removeEventListener("focus",document[t],!0),document[n]&&document.removeEventListener("blur",document[n],!0),document[t]=r,document[n]=s,document.addEventListener("focus",r,!0),document.addEventListener("blur",s,!0),this.logger.debug("[Focus] Focus tracking set up")}restoreFocusedElement(){var r,s,o;if(this.logger.debug("[Focus] restoreFocusedElement - lastFocusedElement:",(r=this.lastFocusedElement)==null?void 0:r.tagName,((s=this.lastFocusedElement)==null?void 0:s.id)||((o=this.lastFocusedElement)==null?void 0:o.getAttribute("name"))),!this.lastFocusedElement||!this.wrapperElement){this.logger.debug("[Focus] No element to restore");return}let e=this.getElementSelector(this.lastFocusedElement);if(this.logger.debug("[Focus] Selector for last focused:",e),!e){this.logger.debug("[Focus] Could not generate selector");return}let t=null;if(e.startsWith("data-focus-index-")){this.updateFocusableElements();let i=parseInt(e.replace("data-focus-index-",""),10);t=this.focusableElements[i]||null,this.logger.debug("[Focus] Found by index:",i,t==null?void 0:t.tagName)}else t=this.wrapperElement.querySelector(e),this.logger.debug("[Focus] Found by selector:",e,t==null?void 0:t.tagName);if(!t){this.logger.debug("[Focus] Element not found in updated DOM");return}let n=t.matches(":focus");this.logger.debug("[Focus] Already focused:",n),n||(t.focus(),this.logger.debug("[Focus] Restored focus")),this.isTextualInput(t)&&this.lastFocusedSelectionStart!==null&&this.lastFocusedSelectionEnd!==null&&(t.setSelectionRange(this.lastFocusedSelectionStart,this.lastFocusedSelectionEnd),this.logger.debug("[Focus] Restored cursor:",this.lastFocusedSelectionStart,"-",this.lastFocusedSelectionEnd))}isTextualInput(e){return e instanceof HTMLTextAreaElement?!0:e instanceof HTMLInputElement?ue.indexOf(e.type)>=0:!1}getLastFocusedElement(){return this.lastFocusedElement}getElementSelector(e){if(e.id)return`#${e.id}`;if(e.name)return`[name="${e.name}"]`;if(e.getAttribute("data-key"))return`[data-key="${e.getAttribute("data-key")}"]`;let t=this.focusableElements.indexOf(e);return t>=0?`data-focus-index-${t}`:null}};function Te(a){a.querySelectorAll("[lvt-scroll]").forEach(t=>{let n=t,r=n.getAttribute("lvt-scroll"),s=n.getAttribute("lvt-scroll-behavior")||"auto",o=parseInt(n.getAttribute("lvt-scroll-threshold")||"100",10);if(r)switch(r){case"bottom":n.scrollTo({top:n.scrollHeight,behavior:s});break;case"bottom-sticky":{n.scrollHeight-n.scrollTop-n.clientHeight<=o&&n.scrollTo({top:n.scrollHeight,behavior:s});break}case"top":n.scrollTo({top:0,behavior:s});break;case"preserve":break;default:console.warn(`Unknown lvt-scroll mode: ${r}`)}})}function Le(a){a.querySelectorAll("[lvt-highlight]").forEach(t=>{let n=t.getAttribute("lvt-highlight"),r=parseInt(t.getAttribute("lvt-highlight-duration")||"500",10),s=t.getAttribute("lvt-highlight-color")||"#ffc107";if(!n)return;let o=t,i=o.style.backgroundColor,l=o.style.transition;o.style.transition=`background-color ${r}ms ease-out`,o.style.backgroundColor=s,setTimeout(()=>{o.style.backgroundColor=i,setTimeout(()=>{o.style.transition=l},r)},50)})}function ke(a){if(a.querySelectorAll("[lvt-animate]").forEach(t=>{let n=t.getAttribute("lvt-animate"),r=parseInt(t.getAttribute("lvt-animate-duration")||"300",10);if(!n)return;let s=t;switch(s.style.setProperty("--lvt-animate-duration",`${r}ms`),n){case"fade":s.style.animation="lvt-fade-in var(--lvt-animate-duration) ease-out";break;case"slide":s.style.animation="lvt-slide-in var(--lvt-animate-duration) ease-out";break;case"scale":s.style.animation="lvt-scale-in var(--lvt-animate-duration) ease-out";break;default:console.warn(`Unknown lvt-animate mode: ${n}`)}s.addEventListener("animationend",()=>{s.style.animation=""},{once:!0})}),!document.getElementById("lvt-animate-styles")){let t=document.createElement("style");t.id="lvt-animate-styles",t.textContent=`

Copilot AI Dec 22, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This expression always evaluates to true.

Copilot uses AI. Check for mistakes.
Comment thread testing/livetemplate-client.browser.js Outdated
}
}
`,document.head.appendChild(t)}}function de(a,e){let t=null;return function(...r){let s=this;t!==null&&clearTimeout(t),t=window.setTimeout(()=>{a.apply(s,r)},e)}}function ue(a,e){let t=!1;return function(...r){let s=this;t||(a.apply(s,r),t=!0,setTimeout(()=>{t=!1},e))}}var q=class{constructor(e,t){this.context=e;this.logger=t}setupEventDelegation(){let e=this.context.getWrapperElement();if(!e)return;let t=["click","submit","change","input","keydown","keyup","focus","blur","mouseenter","mouseleave"],n=e.getAttribute("data-lvt-id"),r=this.context.getRateLimitedHandlers();t.forEach(s=>{let o=`__lvt_delegated_${s}_${n}`,i=document[o];i&&document.removeEventListener(s,i,!1);let l=c=>{var L;let d=this.context.getWrapperElement();if(!d)return;s==="submit"&&(window.__lvtSubmitListenerTriggered=!0,window.__lvtSubmitEventTarget=(L=c.target)==null?void 0:L.tagName),this.logger.debug("Event listener triggered:",s,c.target);let M=c.target;if(!M)return;let m=M,f=!1;for(;m;){if(m===d){f=!0;break}m=m.parentElement}if(s==="submit"&&(window.__lvtInWrapper=f,window.__lvtWrapperElement=d.getAttribute("data-lvt-id")),!f)return;let F=`lvt-${s}`;for(m=M;m&&m!==d.parentElement;){let b=m.getAttribute(F),h=m;if(!b&&(s==="change"||s==="input")){let u=m.closest("form");u&&u.hasAttribute("lvt-change")&&(b=u.getAttribute("lvt-change"),h=u)}if(b&&h){if(s==="submit"&&(window.__lvtActionFound=b,window.__lvtActionElement=h.tagName),s==="submit"&&c.preventDefault(),(s==="keydown"||s==="keyup")&&h.hasAttribute("lvt-key")){let w=h.getAttribute("lvt-key");if(w&&c.key!==w){m=m.parentElement;continue}}let u=h,y=()=>{if(this.logger.debug("handleAction called",{action:b,eventType:s,targetElement:u}),b==="delete"&&u.hasAttribute("lvt-confirm")){let S=u.getAttribute("lvt-confirm")||"Are you sure you want to delete this item?";if(!confirm(S)){this.logger.debug("Delete action cancelled by user");return}}let w={action:b,data:{}};if(u instanceof HTMLFormElement){this.logger.debug("Processing form element");let S=new FormData(u),k=Array.from(u.querySelectorAll('input[type="checkbox"][name]')),_=new Set(k.map(H=>H.name));_.forEach(H=>{w.data[H]=!1}),S.forEach((H,A)=>{_.has(A)?(w.data[A]=!0,this.logger.debug("Converted checkbox",A,"to true")):w.data[A]=this.context.parseValue(H)}),this.logger.debug("Form data collected:",w.data)}else if(s==="change"||s==="input"){if(u instanceof HTMLInputElement){let S=u.name||"value";w.data[S]=this.context.parseValue(u.value)}else if(u instanceof HTMLSelectElement){let S=u.name||"value";w.data[S]=this.context.parseValue(u.value)}else if(u instanceof HTMLTextAreaElement){let S=u.name||"value";w.data[S]=this.context.parseValue(u.value)}}if(Array.from(u.attributes).forEach(S=>{if(S.name.startsWith("lvt-data-")){let k=S.name.replace("lvt-data-","");w.data[k]=this.context.parseValue(S.value)}}),Array.from(u.attributes).forEach(S=>{if(S.name.startsWith("lvt-value-")){let k=S.name.replace("lvt-value-","");w.data[k]=this.context.parseValue(S.value)}}),s==="submit"&&u instanceof HTMLFormElement){let k=c.submitter,_=null;k&&k.hasAttribute("lvt-disable-with")&&(_=k.textContent,k.disabled=!0,k.textContent=k.getAttribute("lvt-disable-with"),this.logger.debug("Disabled submit button")),this.context.setActiveSubmission(u,k||null,_),u.querySelectorAll('input[type="file"][lvt-upload]').forEach(A=>{let O=A.getAttribute("lvt-upload");O&&(this.logger.debug("Triggering pending uploads for:",O),this.context.triggerPendingUploads(O))}),u.dispatchEvent(new CustomEvent("lvt:pending",{detail:w})),this.logger.debug("Emitted lvt:pending event")}this.logger.debug("About to send message:",w),this.logger.debug("WebSocket state:",this.context.getWebSocketReadyState()),this.context.send(w),this.logger.debug("send() called")},U=h.getAttribute("lvt-throttle"),x=h.getAttribute("lvt-debounce");if(U||x){r.has(h)||r.set(h,new Map);let w=r.get(h),S=`${s}:${b}`,k=w.get(S);if(!k){if(U){let _=parseInt(U,10);k=ue(y,_)}else if(x){let _=parseInt(x,10);k=de(y,_)}k&&w.set(S,k)}k&&k()}else s==="submit"&&(window.__lvtBeforeHandleAction=!0),y(),s==="submit"&&(window.__lvtAfterHandleAction=!0);return}m=m.parentElement}};document[o]=l,document.addEventListener(s,l,!1),this.logger.debug("Registered event listener:",s,"with key:",o)})}setupWindowEventDelegation(){let e=this.context.getWrapperElement();if(!e)return;let t=["keydown","keyup","scroll","resize","focus","blur"],n=e.getAttribute("data-lvt-id"),r=this.context.getRateLimitedHandlers();t.forEach(s=>{let o=`__lvt_window_${s}_${n}`,i=window[o];i&&window.removeEventListener(s,i);let l=c=>{let d=this.context.getWrapperElement();if(!d)return;let M=`lvt-window-${s}`;d.querySelectorAll(`[${M}]`).forEach(f=>{let F=f.getAttribute(M);if(!F)return;if((s==="keydown"||s==="keyup")&&f.hasAttribute("lvt-key")){let y=f.getAttribute("lvt-key");if(y&&c.key!==y)return}let L={action:F,data:{}};Array.from(f.attributes).forEach(y=>{if(y.name.startsWith("lvt-data-")){let U=y.name.replace("lvt-data-","");L.data[U]=this.context.parseValue(y.value)}}),Array.from(f.attributes).forEach(y=>{if(y.name.startsWith("lvt-value-")){let U=y.name.replace("lvt-value-","");L.data[U]=this.context.parseValue(y.value)}});let b=f.getAttribute("lvt-throttle"),h=f.getAttribute("lvt-debounce"),u=()=>this.context.send(L);if(b||h){r.has(f)||r.set(f,new Map);let y=r.get(f),U=`window-${s}:${F}`,x=y.get(U);if(!x){if(b){let w=parseInt(b,10);x=ue(u,w)}else if(h){let w=parseInt(h,10);x=de(u,w)}x&&y.set(U,x)}x&&x()}else u()})};window[o]=l,window.addEventListener(s,l)})}setupClickAwayDelegation(){let e=this.context.getWrapperElement();if(!e)return;let n=`__lvt_click_away_${e.getAttribute("data-lvt-id")}`,r=document[n];r&&document.removeEventListener("click",r);let s=o=>{let i=this.context.getWrapperElement();if(!i)return;let l=o.target;i.querySelectorAll("[lvt-click-away]").forEach(d=>{if(!d.contains(l)){let M=d.getAttribute("lvt-click-away");if(!M)return;let m={action:M,data:{}};Array.from(d.attributes).forEach(f=>{if(f.name.startsWith("lvt-data-")){let F=f.name.replace("lvt-data-","");m.data[F]=this.context.parseValue(f.value)}}),Array.from(d.attributes).forEach(f=>{if(f.name.startsWith("lvt-value-")){let F=f.name.replace("lvt-value-","");m.data[F]=this.context.parseValue(f.value)}}),this.context.send(m)}})};document[n]=s,document.addEventListener("click",s)}setupModalDelegation(){let e=this.context.getWrapperElement();if(!e)return;let t=e.getAttribute("data-lvt-id"),n=`__lvt_modal_open_${t}`,r=document[n];r&&document.removeEventListener("click",r);let s=L=>{var y;let b=this.context.getWrapperElement();if(!b)return;let h=(y=L.target)==null?void 0:y.closest("[lvt-modal-open]");if(!h||!b.contains(h))return;let u=h.getAttribute("lvt-modal-open");u&&(L.preventDefault(),this.context.openModal(u))};document[n]=s,document.addEventListener("click",s);let o=`__lvt_modal_close_${t}`,i=document[o];i&&document.removeEventListener("click",i);let l=L=>{var y;let b=this.context.getWrapperElement();if(!b)return;let h=(y=L.target)==null?void 0:y.closest("[lvt-modal-close]");if(!h||!b.contains(h))return;let u=h.getAttribute("lvt-modal-close");u&&(L.preventDefault(),this.context.closeModal(u))};document[o]=l,document.addEventListener("click",l);let c=`__lvt_modal_backdrop_${t}`,d=document[c];d&&document.removeEventListener("click",d);let M=L=>{let b=L.target;if(!b.hasAttribute("data-modal-backdrop"))return;let h=b.getAttribute("data-modal-id");h&&this.context.closeModal(h)};document[c]=M,document.addEventListener("click",M);let m=`__lvt_modal_escape_${t}`,f=document[m];f&&document.removeEventListener("keydown",f);let F=L=>{if(L.key!=="Escape")return;let b=this.context.getWrapperElement();if(!b)return;let h=b.querySelectorAll('[role="dialog"]:not([hidden])');if(h.length>0){let u=h[h.length-1];u.id&&this.context.closeModal(u.id)}};document[m]=F,document.addEventListener("keydown",F)}};var G=class{constructor(e,t){this.context=e;this.logger=t;this.infiniteScrollObserver=null;this.mutationObserver=null}setupInfiniteScrollObserver(){if(!this.context.getWrapperElement())return;let t=document.getElementById("scroll-sentinel");t&&(this.infiniteScrollObserver&&this.infiniteScrollObserver.disconnect(),this.infiniteScrollObserver=new IntersectionObserver(n=>{n[0].isIntersecting&&(this.logger.debug("Sentinel visible, sending load_more action"),this.context.send({action:"load_more"}))},{rootMargin:"200px"}),this.infiniteScrollObserver.observe(t),this.logger.debug("Observer set up successfully"))}setupInfiniteScrollMutationObserver(){let e=this.context.getWrapperElement();e&&(this.mutationObserver&&this.mutationObserver.disconnect(),this.mutationObserver=new MutationObserver(()=>{this.setupInfiniteScrollObserver()}),this.mutationObserver.observe(e,{childList:!0,subtree:!0}),this.logger.debug("MutationObserver set up successfully"))}teardown(){this.infiniteScrollObserver&&(this.infiniteScrollObserver.disconnect(),this.infiniteScrollObserver=null),this.mutationObserver&&(this.mutationObserver.disconnect(),this.mutationObserver=null)}};var J=class{constructor(e){this.logger=e}open(e){let t=document.getElementById(e);if(!t){this.logger.warn(`Modal with id="${e}" not found`);return}t.removeAttribute("hidden"),t.style.display="flex",t.setAttribute("aria-hidden","false"),t.dispatchEvent(new CustomEvent("lvt:modal-opened",{bubbles:!0})),this.logger.info(`Opened modal: ${e}`);let n=t.querySelector("input, textarea, select");n&&setTimeout(()=>{let r=document.activeElement,s=i=>i?i===document.body||i.offsetParent!==null?!0:i.getClientRects().length>0:!1;(!r||!t.contains(r)||!s(r))&&n.focus()},100)}close(e){let t=document.getElementById(e);if(!t){this.logger.warn(`Modal with id="${e}" not found`);return}t.setAttribute("hidden",""),t.style.display="none",t.setAttribute("aria-hidden","true"),t.dispatchEvent(new CustomEvent("lvt:modal-closed",{bubbles:!0})),this.logger.info(`Closed modal: ${e}`)}};var X=class{constructor(){this.bar=null}show(){if(this.bar)return;let e=document.createElement("div");if(e.style.cssText=`
`,document.head.appendChild(t)}}function pe(a,e){let t=null;return function(...r){let s=this;t!==null&&clearTimeout(t),t=window.setTimeout(()=>{a.apply(s,r)},e)}}function ge(a,e){let t=!1;return function(...r){let s=this;t||(a.apply(s,r),t=!0,setTimeout(()=>{t=!1},e))}}function z(a){if(a.hasAttribute("lvt-confirm")){let e=a.getAttribute("lvt-confirm");if(e&&!confirm(e))return!1}return!0}function xe(a){let e={},t=a.attributes;for(let n=0;n<t.length;n++){let r=t[n];if(r.name.startsWith("lvt-data-")){let s=r.name.substring(9);e[s]=r.value}}return e}var J=class{constructor(e,t){this.context=e;this.logger=t}setupEventDelegation(){let e=this.context.getWrapperElement();if(!e)return;let t=["click","submit","change","input","keydown","keyup","focus","blur","mouseenter","mouseleave"],n=e.getAttribute("data-lvt-id"),r=this.context.getRateLimitedHandlers();t.forEach(s=>{let o=`__lvt_delegated_${s}_${n}`,i=document[o];i&&document.removeEventListener(s,i,!1);let l=c=>{var M;let d=this.context.getWrapperElement();if(!d)return;s==="submit"&&(window.__lvtSubmitListenerTriggered=!0,window.__lvtSubmitEventTarget=(M=c.target)==null?void 0:M.tagName),this.logger.debug("Event listener triggered:",s,c.target);let u=c.target;if(!u)return;let g=u,f=!1;for(;g;){if(g===d){f=!0;break}g=g.parentElement}if(s==="submit"&&(window.__lvtInWrapper=f,window.__lvtWrapperElement=d.getAttribute("data-lvt-id")),!f)return;let A=`lvt-${s}`;for(g=u;g&&g!==d.parentElement;){let y=g.getAttribute(A),v=g;if(!y&&s==="submit"&&g instanceof HTMLFormElement){let p=g.getAttribute("lvt-persist");p&&(y=`persist:${p}`,v=g)}if(!y&&(s==="change"||s==="input")){let p=g.closest("form");p&&p.hasAttribute("lvt-change")&&(y=p.getAttribute("lvt-change"),v=p)}if(y&&v){if(s==="submit"&&(window.__lvtActionFound=y,window.__lvtActionElement=v.tagName),s==="submit"&&c.preventDefault(),(s==="keydown"||s==="keyup")&&v.hasAttribute("lvt-key")){let S=v.getAttribute("lvt-key");if(S&&c.key!==S){g=g.parentElement;continue}}let p=v,E=()=>{if(this.logger.debug("handleAction called",{action:y,eventType:s,targetElement:p}),!z(p)){this.logger.debug("Action cancelled by user:",y);return}let S={action:y,data:{}};if(p instanceof HTMLFormElement){this.logger.debug("Processing form element");let T=new FormData(p),L=Array.from(p.querySelectorAll('input[type="checkbox"][name]')),F=new Set(L.map(H=>H.name));F.forEach(H=>{S.data[H]=!1}),T.forEach((H,k)=>{F.has(k)?(S.data[k]=!0,this.logger.debug("Converted checkbox",k,"to true")):S.data[k]=this.context.parseValue(H)}),this.logger.debug("Form data collected:",S.data)}else if(s==="change"||s==="input"){if(p instanceof HTMLInputElement){let T=p.name||"value";S.data[T]=this.context.parseValue(p.value)}else if(p instanceof HTMLSelectElement){let T=p.name||"value";S.data[T]=this.context.parseValue(p.value)}else if(p instanceof HTMLTextAreaElement){let T=p.name||"value";S.data[T]=this.context.parseValue(p.value)}}if(Array.from(p.attributes).forEach(T=>{if(T.name.startsWith("lvt-data-")){let L=T.name.replace("lvt-data-","");S.data[L]=this.context.parseValue(T.value)}}),Array.from(p.attributes).forEach(T=>{if(T.name.startsWith("lvt-value-")){let L=T.name.replace("lvt-value-","");S.data[L]=this.context.parseValue(T.value)}}),s==="submit"&&p instanceof HTMLFormElement){let L=c.submitter,F=null;L&&L.hasAttribute("lvt-disable-with")&&(F=L.textContent,L.disabled=!0,L.textContent=L.getAttribute("lvt-disable-with"),this.logger.debug("Disabled submit button")),this.context.setActiveSubmission(p,L||null,F),p.querySelectorAll('input[type="file"][lvt-upload]').forEach(k=>{let U=k.getAttribute("lvt-upload");U&&(this.logger.debug("Triggering pending uploads for:",U),this.context.triggerPendingUploads(U))}),p.dispatchEvent(new CustomEvent("lvt:pending",{detail:S})),this.logger.debug("Emitted lvt:pending event")}this.logger.debug("About to send message:",S),this.logger.debug("WebSocket state:",this.context.getWebSocketReadyState()),this.context.send(S),this.logger.debug("send() called")},R=v.getAttribute("lvt-throttle"),C=v.getAttribute("lvt-debounce");if(R||C){r.has(v)||r.set(v,new Map);let S=r.get(v),T=`${s}:${y}`,L=S.get(T);if(!L){if(R){let F=parseInt(R,10);L=ge(E,F)}else if(C){let F=parseInt(C,10);L=pe(E,F)}L&&S.set(T,L)}L&&L()}else s==="submit"&&(window.__lvtBeforeHandleAction=!0),E(),s==="submit"&&(window.__lvtAfterHandleAction=!0);return}g=g.parentElement}};document[o]=l,document.addEventListener(s,l,!1),this.logger.debug("Registered event listener:",s,"with key:",o)})}setupWindowEventDelegation(){let e=this.context.getWrapperElement();if(!e)return;let t=["keydown","keyup","scroll","resize","focus","blur"],n=e.getAttribute("data-lvt-id"),r=this.context.getRateLimitedHandlers();t.forEach(s=>{let o=`__lvt_window_${s}_${n}`,i=window[o];i&&window.removeEventListener(s,i);let l=c=>{let d=this.context.getWrapperElement();if(!d)return;let u=`lvt-window-${s}`;d.querySelectorAll(`[${u}]`).forEach(f=>{let A=f.getAttribute(u);if(!A)return;if((s==="keydown"||s==="keyup")&&f.hasAttribute("lvt-key")){let E=f.getAttribute("lvt-key");if(E&&c.key!==E)return}let M={action:A,data:{}};Array.from(f.attributes).forEach(E=>{if(E.name.startsWith("lvt-data-")){let R=E.name.replace("lvt-data-","");M.data[R]=this.context.parseValue(E.value)}}),Array.from(f.attributes).forEach(E=>{if(E.name.startsWith("lvt-value-")){let R=E.name.replace("lvt-value-","");M.data[R]=this.context.parseValue(E.value)}});let y=f.getAttribute("lvt-throttle"),v=f.getAttribute("lvt-debounce"),p=()=>this.context.send(M);if(y||v){r.has(f)||r.set(f,new Map);let E=r.get(f),R=`window-${s}:${A}`,C=E.get(R);if(!C){if(y){let S=parseInt(y,10);C=ge(p,S)}else if(v){let S=parseInt(v,10);C=pe(p,S)}C&&E.set(R,C)}C&&C()}else p()})};window[o]=l,window.addEventListener(s,l)})}setupClickAwayDelegation(){let e=this.context.getWrapperElement();if(!e)return;let n=`__lvt_click_away_${e.getAttribute("data-lvt-id")}`,r=document[n];r&&document.removeEventListener("click",r);let s=o=>{let i=this.context.getWrapperElement();if(!i)return;let l=o.target;i.querySelectorAll("[lvt-click-away]").forEach(d=>{if(!d.contains(l)){let u=d.getAttribute("lvt-click-away");if(!u)return;let g={action:u,data:{}};Array.from(d.attributes).forEach(f=>{if(f.name.startsWith("lvt-data-")){let A=f.name.replace("lvt-data-","");g.data[A]=this.context.parseValue(f.value)}}),Array.from(d.attributes).forEach(f=>{if(f.name.startsWith("lvt-value-")){let A=f.name.replace("lvt-value-","");g.data[A]=this.context.parseValue(f.value)}}),this.context.send(g)}})};document[n]=s,document.addEventListener("click",s)}setupModalDelegation(){let e=this.context.getWrapperElement();if(!e)return;let t=e.getAttribute("data-lvt-id"),n=`__lvt_modal_open_${t}`,r=document[n];r&&document.removeEventListener("click",r);let s=M=>{var E;let y=this.context.getWrapperElement();if(!y)return;let v=(E=M.target)==null?void 0:E.closest("[lvt-modal-open]");if(!v||!y.contains(v))return;let p=v.getAttribute("lvt-modal-open");p&&(M.preventDefault(),this.context.openModal(p))};document[n]=s,document.addEventListener("click",s);let o=`__lvt_modal_close_${t}`,i=document[o];i&&document.removeEventListener("click",i);let l=M=>{var E;let y=this.context.getWrapperElement();if(!y)return;let v=(E=M.target)==null?void 0:E.closest("[lvt-modal-close]");if(!v||!y.contains(v))return;let p=v.getAttribute("lvt-modal-close");p&&(M.preventDefault(),this.context.closeModal(p))};document[o]=l,document.addEventListener("click",l);let c=`__lvt_modal_backdrop_${t}`,d=document[c];d&&document.removeEventListener("click",d);let u=M=>{let y=M.target;if(!y.hasAttribute("data-modal-backdrop"))return;let v=y.getAttribute("data-modal-id");v&&this.context.closeModal(v)};document[c]=u,document.addEventListener("click",u);let g=`__lvt_modal_escape_${t}`,f=document[g];f&&document.removeEventListener("keydown",f);let A=M=>{if(M.key!=="Escape")return;let y=this.context.getWrapperElement();if(!y)return;let v=y.querySelectorAll('[role="dialog"]:not([hidden])');if(v.length>0){let p=v[v.length-1];p.id&&this.context.closeModal(p.id)}};document[g]=A,document.addEventListener("keydown",A)}setupFocusTrapDelegation(){let e=this.context.getWrapperElement();if(!e)return;let n=`__lvt_focus_trap_${e.getAttribute("data-lvt-id")}`,r=document[n];r&&document.removeEventListener("keydown",r);let s=i=>{let l=["a[href]:not([disabled])","button:not([disabled])","textarea:not([disabled])",'input:not([disabled]):not([type="hidden"])',"select:not([disabled])",'[tabindex]:not([tabindex="-1"]):not([disabled])','[contenteditable="true"]'].join(", ");return Array.from(i.querySelectorAll(l)).filter(c=>{let d=c,u=window.getComputedStyle(d),g=u.display!=="none",f=u.visibility!=="hidden",A=d.offsetParent!==null||u.position==="fixed"||u.position==="absolute"||typeof process!="undefined"&&!1;return g&&f&&A})},o=i=>{if(i.key!=="Tab")return;let l=this.context.getWrapperElement();if(!l)return;let c=l.querySelectorAll("[lvt-focus-trap]"),d=null;if(c.forEach(A=>{A.contains(document.activeElement)&&(!d||A.contains(d))&&(d=A)}),d||c.forEach(A=>{let M=A,y=window.getComputedStyle(M);y.display!=="none"&&y.visibility!=="hidden"&&(d=A)}),!d)return;let u=s(d);if(u.length===0)return;let g=u[0],f=u[u.length-1];i.shiftKey?document.activeElement===g&&(i.preventDefault(),f.focus()):document.activeElement===f&&(i.preventDefault(),g.focus())};document[n]=o,document.addEventListener("keydown",o),this.logger.debug("Focus trap delegation set up")}setupAutofocusDelegation(){let e=this.context.getWrapperElement();if(!e)return;let n=`__lvt_autofocus_observer_${e.getAttribute("data-lvt-id")}`,r=e[n];r&&r.disconnect();let s=()=>{let i=this.context.getWrapperElement();if(!i)return;i.querySelectorAll("[lvt-autofocus]").forEach(c=>{let d=c,u=window.getComputedStyle(d),g=u.display!=="none",f=u.visibility!=="hidden",A=d.offsetParent!==null||u.position==="fixed"||u.position==="absolute"||d.tagName==="BODY"||typeof process!="undefined"&&!1,M=g&&f&&A,y=d.getAttribute("data-lvt-autofocused")==="true";M&&!y?(d.setAttribute("data-lvt-autofocused","true"),requestAnimationFrame(()=>{d.focus(),this.logger.debug("Autofocused element:",d.tagName,d.id||d.getAttribute("name"))})):!M&&y&&d.removeAttribute("data-lvt-autofocused")})};s();let o=new MutationObserver(i=>{let l=!1;i.forEach(c=>{c.type==="childList"&&c.addedNodes.length>0&&c.addedNodes.forEach(d=>{d instanceof Element&&(d.hasAttribute("lvt-autofocus")||d.querySelector("[lvt-autofocus]"))&&(l=!0)}),c.type==="attributes"&&(c.target.hasAttribute("lvt-autofocus")||c.attributeName==="hidden"||c.attributeName==="style"||c.attributeName==="class")&&(l=!0)}),l&&s()});o.observe(e,{childList:!0,subtree:!0,attributes:!0,attributeFilter:["hidden","style","class","lvt-autofocus"]}),e[n]=o,this.logger.debug("Autofocus delegation set up")}};var G=class{constructor(e,t){this.context=e;this.logger=t;this.infiniteScrollObserver=null;this.mutationObserver=null}setupInfiniteScrollObserver(){if(!this.context.getWrapperElement())return;let t=document.getElementById("scroll-sentinel");t&&(this.infiniteScrollObserver&&this.infiniteScrollObserver.disconnect(),this.infiniteScrollObserver=new IntersectionObserver(n=>{n[0].isIntersecting&&(this.logger.debug("Sentinel visible, sending load_more action"),this.context.send({action:"load_more"}))},{rootMargin:"200px"}),this.infiniteScrollObserver.observe(t),this.logger.debug("Observer set up successfully"))}setupInfiniteScrollMutationObserver(){let e=this.context.getWrapperElement();e&&(this.mutationObserver&&this.mutationObserver.disconnect(),this.mutationObserver=new MutationObserver(()=>{this.setupInfiniteScrollObserver()}),this.mutationObserver.observe(e,{childList:!0,subtree:!0}),this.logger.debug("MutationObserver set up successfully"))}teardown(){this.infiniteScrollObserver&&(this.infiniteScrollObserver.disconnect(),this.infiniteScrollObserver=null),this.mutationObserver&&(this.mutationObserver.disconnect(),this.mutationObserver=null)}};var X=class{constructor(e){this.logger=e}open(e){let t=document.getElementById(e);if(!t){this.logger.warn(`Modal with id="${e}" not found`);return}t.removeAttribute("hidden"),t.style.display="flex",t.setAttribute("aria-hidden","false"),t.dispatchEvent(new CustomEvent("lvt:modal-opened",{bubbles:!0})),this.logger.info(`Opened modal: ${e}`);let n=t.querySelector("input, textarea, select");n&&setTimeout(()=>{let r=document.activeElement,s=i=>i?i===document.body||i.offsetParent!==null?!0:i.getClientRects().length>0:!1;(!r||!t.contains(r)||!s(r))&&n.focus()},100)}close(e){let t=document.getElementById(e);if(!t){this.logger.warn(`Modal with id="${e}" not found`);return}t.setAttribute("hidden",""),t.style.display="none",t.setAttribute("aria-hidden","true"),t.dispatchEvent(new CustomEvent("lvt:modal-closed",{bubbles:!0})),this.logger.info(`Closed modal: ${e}`)}};var Y=class{constructor(){this.bar=null}show(){if(this.bar)return;let e=document.createElement("div");if(e.style.cssText=`

Copilot AI Dec 22, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This use of variable 'v' always evaluates to true.

Copilot uses AI. Check for mistakes.
Comment thread testing/livetemplate-client.browser.js Outdated
100% { background-position: -200% 0; }
}
`,document.head.appendChild(t)}document.body.insertBefore(e,document.body.firstChild),this.bar=e}hide(){this.bar&&(this.bar.parentNode&&this.bar.parentNode.removeChild(this.bar),this.bar=null)}};var Y=class{disable(e){if(!e)return;e.querySelectorAll("form").forEach(n=>{n.querySelectorAll("input, textarea, select, button").forEach(s=>{s.disabled=!0})})}enable(e){if(!e)return;e.querySelectorAll("form").forEach(n=>{n.querySelectorAll("input, textarea, select, button").forEach(s=>{s.disabled=!1})})}};var Q=class{constructor(e){this.logger=e;this.treeState={};this.rangeState={};this.rangeIdKeys={}}applyUpdate(e){let t=!1;for(let[r,s]of Object.entries(e))if(Array.isArray(s)&&s.length>0&&Array.isArray(s[0])&&typeof s[0][0]=="string")this.treeState[r]=s,t=!0;else{let i=this.treeState[r],l=typeof s=="object"&&s!==null&&!Array.isArray(s)?this.deepMergeTreeNodes(i,s):s;JSON.stringify(i)!==JSON.stringify(l)&&(this.treeState[r]=l,t=!0)}return{html:this.reconstructFromTree(this.treeState,""),changed:t}}reset(){this.treeState={},this.rangeState={},this.rangeIdKeys={}}getTreeState(){return{...this.treeState}}getStaticStructure(){return this.treeState.s||null}deepMergeTreeNodes(e,t){if(typeof t!="object"||t===null||Array.isArray(t)||typeof e!="object"||e===null||Array.isArray(e))return t;let n={...e};for(let[r,s]of Object.entries(t))typeof s=="object"&&s!==null&&!Array.isArray(s)&&typeof n[r]=="object"&&n[r]!==null&&!Array.isArray(n[r])?n[r]=this.deepMergeTreeNodes(n[r],s):n[r]=s;return n}reconstructFromTree(e,t){if(e.s&&Array.isArray(e.s)){let n="";for(let r=0;r<e.s.length;r++){let s=e.s[r];if(n+=s,r<e.s.length-1){let o=r.toString();if(e[o]!==void 0){let i=t?`${t}.${o}`:o;n+=this.renderValue(e[o],o,i)}}}return n=n.replace(/<root>/g,"").replace(/<\/root>/g,""),n}return this.renderValue(e,"",t)}renderValue(e,t,n){if(e==null||typeof e=="string"&&e.startsWith("{{")&&e.endsWith("}}"))return"";if(typeof e=="object"&&!Array.isArray(e)){if(e.d&&Array.isArray(e.d)&&e.s&&Array.isArray(e.s)){let r=n||t||"";return r&&(this.rangeState[r]={items:e.d,statics:e.s},e.m&&typeof e.m=="object"&&typeof e.m.idKey=="string"&&(this.rangeIdKeys[r]=e.m.idKey)),this.renderRangeStructure(e,t,n)}if("s"in e&&Array.isArray(e.s))return this.reconstructFromTree(e,n||"")}return Array.isArray(e)?e.length>0&&Array.isArray(e[0])&&typeof e[0][0]=="string"?this.applyDifferentialOperations(e,n):e.map((r,s)=>{let o=s.toString(),i=n?`${n}.${o}`:o;return typeof r=="object"&&r&&r.s?this.reconstructFromTree(r,i):this.renderValue(r,o,i)}).join(""):(typeof e=="object"&&(this.logger.error("Object value reached string conversion; this will render as [object Object]."),this.logger.isDebugEnabled()&&this.logger.debug("Value diagnostics:",{valueType:typeof e,isArray:Array.isArray(e),keys:Object.keys(e),hasStatics:!!e.s,hasDynamics:!!e.d,value:e})),String(e))}renderRangeStructure(e,t,n){let{d:r,s}=e;if(!r||!Array.isArray(r))return"";if(r.length===0){if(e.else){let o="else",i=n?`${n}.else`:"else";return this.renderValue(e.else,o,i)}return""}return s&&Array.isArray(s)?r.map((o,i)=>{let l="";for(let c=0;c<s.length;c++)if(l+=s[c],c<s.length-1){let d=c.toString();if(o[d]!==void 0){let M=n?`${n}.${i}.${d}`:`${i}.${d}`;l+=this.renderValue(o[d],d,M)}}return l}).join(""):r.map((o,i)=>{let l=i.toString(),c=n?`${n}.${l}`:l;return this.renderValue(o,l,c)}).join("")}applyDifferentialOperations(e,t){if(!t||!this.rangeState[t])return"";let n=this.rangeState[t],r=[...n.items],s=n.statics;for(let i of e){if(!Array.isArray(i)||i.length<2)continue;switch(i[0]){case"r":{let c=this.findItemIndexByKey(r,i[1],s,t);c>=0&&r.splice(c,1);break}case"u":{let c=this.findItemIndexByKey(r,i[1],s,t),d=i[2];c>=0&&d&&(r[c]={...r[c],...d});break}case"a":{this.addItemsToRange(r,i[1],i[2],n,!1),i[3]&&typeof i[3]=="object"&&i[3].idKey&&(this.rangeIdKeys[t||""]=i[3].idKey);break}case"p":{this.addItemsToRange(r,i[1],i[2],n,!0);break}case"i":{let c=this.findItemIndexByKey(r,i[1],s,t);if(c>=0){let d=Array.isArray(i[2])?i[2]:[i[2]];r.splice(c+1,0,...d)}break}case"o":{let c=i[1],d=[],M=new Map;for(let m of r){let f=this.getItemKey(m,s,t);f&&M.set(f,m)}for(let m of c){let f=M.get(m);f&&d.push(f)}r.length=0,r.push(...d);break}default:break}}this.rangeState[t]={items:r,statics:n.statics},this.treeState[t]={d:r,s:n.statics};let o=this.getCurrentRangeStructure(t);return o&&o.s?this.renderItemsWithStatics(r,o.s):r.map(i=>this.renderValue(i)).join("")}getCurrentRangeStructure(e){if(this.rangeState[e])return{d:this.rangeState[e].items,s:this.rangeState[e].statics};let t=this.treeState[e];return t&&typeof t=="object"&&t.s?t:null}renderItemsWithStatics(e,t){let n=e.map(r=>{let s="";for(let o=0;o<t.length;o++)if(s+=t[o],o<t.length-1){let i=o.toString();r[i]!==void 0&&(s+=this.renderValue(r[i]))}return s}).join("");return this.logger.isDebugEnabled()&&(this.logger.debug("[renderItemsWithStatics] statics:",t),this.logger.debug("[renderItemsWithStatics] items count:",e.length),this.logger.debug("[renderItemsWithStatics] result snippet:",n.substring(0,200))),n}addItemsToRange(e,t,n,r,s){if(n&&(r.statics=n),!t)return;let o=Array.isArray(t)?t:[t];s?e.unshift(...o):e.push(...o)}getItemKey(e,t,n){if(!n||!this.rangeIdKeys[n])return null;let r=this.rangeIdKeys[n];return e[r]||null}findItemIndexByKey(e,t,n,r){return e.findIndex(s=>this.getItemKey(s,n,r)===t)}};var Z=class{constructor(e){this.modalManager=e;this.activeForm=null;this.activeButton=null;this.originalButtonText=null}setActiveSubmission(e,t,n){this.activeForm=e,this.activeButton=t,this.originalButtonText=n}handleResponse(e){this.activeForm&&this.activeForm.dispatchEvent(new CustomEvent("lvt:done",{detail:e})),e.success?this.handleSuccess(e):this.handleError(e),this.restoreFormState()}reset(){this.restoreFormState()}handleSuccess(e){if(!this.activeForm)return;this.activeForm.dispatchEvent(new CustomEvent("lvt:success",{detail:e}));let t=this.activeForm.closest('[role="dialog"]');t&&t.id&&this.modalManager.close(t.id),this.activeForm.hasAttribute("lvt-preserve")||this.activeForm.reset()}handleError(e){this.activeForm&&this.activeForm.dispatchEvent(new CustomEvent("lvt:error",{detail:e}))}restoreFormState(){this.activeButton&&this.originalButtonText!==null&&(this.activeButton.disabled=!1,this.activeButton.textContent=this.originalButtonText),this.activeForm=null,this.activeButton=null,this.originalButtonText=null}};var pe=class{constructor(e){this.options=e;this.socket=null;this.reconnectTimer=null;this.manuallyClosed=!1;this.reconnectAttempts=0}connect(){this.manuallyClosed=!1,this.clearReconnectTimer(),this.socket=new WebSocket(this.options.url);let e=this.socket;e.onopen=()=>{var t,n;this.reconnectAttempts=0,(n=(t=this.options).onOpen)==null||n.call(t,e)},e.onmessage=t=>{var n,r;(r=(n=this.options).onMessage)==null||r.call(n,t)},e.onclose=t=>{var n,r;(r=(n=this.options).onClose)==null||r.call(n,t),!this.manuallyClosed&&this.options.autoReconnect&&this.scheduleReconnect()},e.onerror=t=>{var n,r;(r=(n=this.options).onError)==null||r.call(n,t)}}send(e){this.socket&&this.socket.readyState===1&&this.socket.send(e)}disconnect(){this.manuallyClosed=!0,this.clearReconnectTimer(),this.socket&&(this.socket.close(),this.socket=null)}getSocket(){return this.socket}scheduleReconnect(){var i,l,c,d,M;this.clearReconnectTimer();let e=(i=this.options.maxReconnectAttempts)!=null?i:10;if(e>0&&this.reconnectAttempts>=e){(c=(l=this.options).onReconnectFailed)==null||c.call(l);return}this.reconnectAttempts++;let t=(d=this.options.reconnectDelay)!=null?d:1e3,n=(M=this.options.maxReconnectDelay)!=null?M:16e3,r=t*Math.pow(2,this.reconnectAttempts-1),s=Math.random()*1e3,o=Math.min(r+s,n);this.reconnectTimer=window.setTimeout(()=>{var m,f;(f=(m=this.options).onReconnectAttempt)==null||f.call(m,this.reconnectAttempts,o),this.connect()},o)}clearReconnectTimer(){this.reconnectTimer!==null&&(clearTimeout(this.reconnectTimer),this.reconnectTimer=null)}},ee=class{constructor(e){this.config=e;this.transport=null}async connect(){let e=this.getLiveUrl();return await Xe(e,this.config.logger)?(this.transport=new pe({url:this.getWebSocketUrl(),autoReconnect:this.config.options.autoReconnect,reconnectDelay:this.config.options.reconnectDelay,maxReconnectDelay:16e3,maxReconnectAttempts:10,onOpen:()=>{this.config.onConnected()},onMessage:n=>{try{let r=JSON.parse(n.data);this.config.onMessage(r,n)}catch(r){this.config.logger.error("Failed to parse WebSocket message:",r)}},onClose:()=>{this.config.onDisconnected()},onReconnectAttempt:(n,r)=>{var s,o;(o=(s=this.config).onReconnectAttempt)==null||o.call(s,n,r)},onReconnectFailed:()=>{var n,r;(r=(n=this.config).onReconnectFailed)==null||r.call(n)},onError:n=>{var r,s;(s=(r=this.config).onError)==null||s.call(r,n)}}),this.transport.connect(),{usingWebSocket:!0}):{usingWebSocket:!1,initialState:await Ye(e,this.config.logger)}}disconnect(){var e;(e=this.transport)==null||e.disconnect(),this.transport=null}send(e){var t;(t=this.transport)==null||t.send(e)}getReadyState(){var e,t;return(t=(e=this.transport)==null?void 0:e.getSocket())==null?void 0:t.readyState}getSocket(){var e,t;return(t=(e=this.transport)==null?void 0:e.getSocket())!=null?t:null}getWebSocketUrl(){let e=this.config.options.liveUrl||"/live",t=this.config.options.wsUrl;return t||`ws://${window.location.host}${e}`}getLiveUrl(){return this.config.options.liveUrl||window.location.pathname}};async function Xe(a,e){try{let n=(await fetch(a,{method:"HEAD"})).headers.get("X-LiveTemplate-WebSocket");return n?n==="enabled":!0}catch(t){return e==null||e.warn("Failed to check WebSocket availability:",t),!0}}async function Ye(a,e){try{let t=await fetch(a,{method:"GET",credentials:"include",headers:{Accept:"application/json"}});if(!t.ok)throw new Error(`Failed to fetch initial state: ${t.status}`);return await t.json()}catch(t){return e==null||e.warn("Failed to fetch initial state:",t),null}}var te=class{async upload(e,t,n){let{file:r}=e;e.abortController=new AbortController;try{let s=new XMLHttpRequest;s.upload.addEventListener("progress",i=>{i.lengthComputable&&(e.bytesUploaded=i.loaded,e.progress=Math.round(i.loaded/i.total*100),n&&n(e))}),e.abortController.signal.addEventListener("abort",()=>{s.abort()});let o=new Promise((i,l)=>{s.addEventListener("load",()=>{s.status>=200&&s.status<300?(e.done=!0,e.progress=100,i()):l(new Error(`S3 upload failed with status ${s.status}: ${s.statusText}`))}),s.addEventListener("error",()=>{l(new Error("S3 upload failed: Network error"))}),s.addEventListener("abort",()=>{l(new Error("S3 upload cancelled"))})});if(s.open("PUT",t.url),t.headers)for(let[i,l]of Object.entries(t.headers))s.setRequestHeader(i,l);s.send(r),await o}catch(s){throw e.error=s instanceof Error?s.message:String(s),s}}};var ne=class{constructor(e,t={}){this.sendMessage=e;this.entries=new Map;this.pendingFiles=new Map;this.autoUploadConfig=new Map;this.uploaders=new Map;this.inputHandlers=new WeakMap;this.chunkSize=t.chunkSize||256*1024,this.onProgress=t.onProgress,this.onComplete=t.onComplete,this.onError=t.onError,this.uploaders.set("s3",new te)}initializeFileInputs(e){e.querySelectorAll('input[type="file"][lvt-upload]').forEach(n=>{let r=n.getAttribute("lvt-upload");if(!r)return;let s=this.inputHandlers.get(n);s&&n.removeEventListener("change",s);let o=i=>{let l=i.target.files;!l||l.length===0||this.startUpload(r,Array.from(l))};n.addEventListener("change",o),this.inputHandlers.set(n,o)})}async startUpload(e,t){this.pendingFiles.set(e,t);let n=t.map(s=>({name:s.name,type:s.type||"application/octet-stream",size:s.size})),r={action:"upload_start",upload_name:e,files:n};this.sendMessage(r)}async handleUploadStartResponse(e){let{upload_name:t,entries:n}=e;n.length>0&&this.autoUploadConfig.set(t,n[0].auto_upload);let r=this.pendingFiles.get(t);if(!r){console.error(`No pending files found for upload: ${t}`);return}this.pendingFiles.delete(t);let s=new Map;for(let i of r)s.set(i.name,i);let o=[];for(let i of n){let l=s.get(i.client_name);if(!l){console.warn(`No file found for entry ${i.entry_id} (client_name: ${i.client_name})`);continue}let c={id:i.entry_id,file:l,uploadName:t,progress:0,bytesUploaded:0,valid:i.valid,done:!1,error:i.error,external:i.external};if(this.entries.set(c.id,c),o.push(c),!i.valid){this.onError&&i.error&&this.onError(c,i.error);continue}i.auto_upload&&(i.external?this.uploadExternal(c,i.external):this.uploadChunked(c))}}async uploadExternal(e,t){try{let n=this.uploaders.get(t.uploader);if(!n)throw new Error(`Unknown uploader: ${t.uploader}`);await n.upload(e,t,this.onProgress);let r={action:"upload_complete",upload_name:e.uploadName,entry_ids:[e.id]};this.sendMessage(r),this.onComplete&&this.onComplete(e.uploadName,[e]),this.clearFileInput(e.uploadName),this.cleanupEntries(e.uploadName)}catch(n){let r=n instanceof Error?n.message:String(n);e.error=r,this.onError&&this.onError(e,r),this.cleanupEntries(e.uploadName)}}async uploadChunked(e){let{file:t,id:n}=e,r=0;e.abortController=new AbortController;try{for(;r<t.size;){if(e.abortController.signal.aborted)throw new Error("Upload cancelled");let o=Math.min(r+this.chunkSize,t.size),i=t.slice(r,o),l=await this.fileToBase64(i),c={action:"upload_chunk",entry_id:n,chunk_base64:l,offset:r,total:t.size};this.sendMessage(c),r=o,e.bytesUploaded=r,e.progress=Math.round(r/t.size*100),this.onProgress&&this.onProgress(e)}e.done=!0;let s={action:"upload_complete",upload_name:e.uploadName,entry_ids:[n]};this.sendMessage(s),this.onComplete&&this.onComplete(e.uploadName,[e]),this.clearFileInput(e.uploadName),this.cleanupEntries(e.uploadName)}catch(s){let o=s instanceof Error?s.message:String(s);e.error=o,this.onError&&this.onError(e,o),this.cleanupEntries(e.uploadName)}}handleProgressMessage(e){let t=this.entries.get(e.entry_id);t&&(t.progress=e.progress,t.bytesUploaded=e.bytes_recv,this.onProgress&&this.onProgress(t))}cancelUpload(e){let t=this.entries.get(e);t&&(t.abortController&&t.abortController.abort(),this.sendMessage({action:"cancel_upload",entry_id:e}),this.entries.delete(e))}getEntries(e){let t=[];for(let n of this.entries.values())n.uploadName===e&&t.push(n);return t}triggerPendingUploads(e){let t=[];for(let n of this.entries.values())n.uploadName===e&&n.progress===0&&!n.done&&!n.error&&t.push(n);for(let n of t)n.external?this.uploadExternal(n,n.external):this.uploadChunked(n)}registerUploader(e,t){this.uploaders.set(e,t)}clearFileInput(e){document.querySelectorAll(`input[type="file"][lvt-upload="${e}"]`).forEach(n=>{n.value=""})}cleanupEntries(e,t=5e3){setTimeout(()=>{let n=[];for(let[r,s]of this.entries)e&&s.uploadName!==e||(s.done||s.error)&&n.push(r);for(let r of n)this.entries.delete(r)},t)}fileToBase64(e){return new Promise((t,n)=>{let r=new FileReader;r.onload=()=>{let o=r.result.split(",")[1];t(o)},r.onerror=n,r.readAsDataURL(e)})}};var Te={silent:0,error:1,warn:2,info:3,debug:4},Le="LiveTemplate",ge=class a{constructor(e,t=[],n=console){this.state=e;this.scope=t;this.sink=n}setLevel(e){this.state.level=e}getLevel(){return this.state.level}child(e){return new a(this.state,[...this.scope,e],this.sink)}isDebugEnabled(){return this.shouldLog("debug")}error(...e){this.log("error","error",e)}warn(...e){this.log("warn","warn",e)}info(...e){this.log("info","info",e)}debug(...e){this.log("debug","debug",e)}log(e,t,n){if(!this.shouldLog(e))return;(this.sink[t]||console[t]||console.log).apply(this.sink,[this.formatPrefix(),...n])}shouldLog(e){return Te[e]<=Te[this.state.level]}formatPrefix(){return this.scope.length===0?`[${Le}]`:`[${Le}:${this.scope.join(":")}]`}};function he(a={}){var n,r;let e={level:(n=a.level)!=null?n:"info"},t=Array.isArray(a.scope)?a.scope:a.scope?[a.scope]:[];return new ge(e,t,(r=a.sink)!=null?r:console)}async function xe(a,e){try{let t=globalThis==null?void 0:globalThis.require;if(typeof t=="function"){let s=t("fs"),o=JSON.parse(s.readFileSync(e,"utf8"));return a.applyUpdate(o)}let r=await(await fetch(e)).json();return a.applyUpdate(r)}catch(t){throw new Error(`Failed to load update from ${e}: ${t}`)}}function Fe(a,e){let t=[],n=c=>c.replace(/\s+/g," ").replace(/>\s+</g,"><").trim(),r=n(a),s=n(e);if(r===s)return{match:!0,differences:[]};let o=r.split(`
`,document.head.appendChild(t)}document.body.insertBefore(e,document.body.firstChild),this.bar=e}hide(){this.bar&&(this.bar.parentNode&&this.bar.parentNode.removeChild(this.bar),this.bar=null)}};var Q=class{disable(e){if(!e)return;e.querySelectorAll("form").forEach(n=>{n.querySelectorAll("input, textarea, select, button").forEach(s=>{s.disabled=!0})})}enable(e){if(!e)return;e.querySelectorAll("form").forEach(n=>{n.querySelectorAll("input, textarea, select, button").forEach(s=>{s.disabled=!1})})}};var Ce=["pending","success","error","done"];var tt={reset:"reset",disable:"disable",enable:"enable",addclass:"addClass",removeclass:"removeClass",toggleclass:"toggleClass",setattr:"setAttr",toggleattr:"toggleAttr"};function nt(a,e){let t=a.toLowerCase().match(/^lvt-(\w+)-on:(.+)$/);if(!t)return null;let n=t[1],r=tt[n];if(!r)return null;let o=t[2].split(":"),i=o[o.length-1];if(!Ce.includes(i))return null;let l=i,c=o.length>1?o.slice(0,-1).join(":"):void 0;return{action:r,lifecycle:l,actionName:c||void 0,param:e||void 0}}function rt(a,e,t){switch(e){case"reset":a instanceof HTMLFormElement&&a.reset();break;case"disable":"disabled"in a&&(a.disabled=!0);break;case"enable":"disabled"in a&&(a.disabled=!1);break;case"addClass":if(t){let n=t.split(/\s+/).filter(Boolean);a.classList.add(...n)}break;case"removeClass":if(t){let n=t.split(/\s+/).filter(Boolean);a.classList.remove(...n)}break;case"toggleClass":t&&t.split(/\s+/).filter(Boolean).forEach(r=>a.classList.toggle(r));break;case"setAttr":if(t){let n=t.indexOf(":");if(n>0){let r=t.substring(0,n),s=t.substring(n+1);a.setAttribute(r,s)}}break;case"toggleAttr":t&&a.toggleAttribute(t);break}}function st(a,e,t){return a.lifecycle!==e?!1:a.actionName?a.actionName===t:!0}function it(a,e){document.querySelectorAll("*").forEach(n=>{Array.from(n.attributes).forEach(r=>{if(!r.name.startsWith("lvt-")||!r.name.includes("-on:"))return;let s=nt(r.name,r.value);s&&st(s,a,e)&&rt(n,s.action,s.param)})})}function Z(){Ce.forEach(a=>{document.addEventListener(`lvt:${a}`,e=>{var r;let n=(r=e.detail)==null?void 0:r.action;it(a,n)},!0)})}function Re(a){return typeof structuredClone=="function"?structuredClone(a):JSON.parse(JSON.stringify(a))}var ee=class{constructor(e){this.logger=e;this.treeState={};this.rangeState={};this.rangeIdKeys={}}applyUpdate(e){let t=!1;for(let[r,s]of Object.entries(e))if(Array.isArray(s)&&s.length>0&&Array.isArray(s[0])&&typeof s[0][0]=="string"){let i=this.treeState[r];i&&typeof i=="object"&&!Array.isArray(i)&&Array.isArray(i.d)&&Array.isArray(i.s)?(this.treeState[r]=Re(i),this.applyDifferentialOpsToRange(this.treeState[r],s,r)):this.treeState[r]=s,t=!0}else{let i=this.treeState[r],l=typeof s=="object"&&s!==null&&!Array.isArray(s)?this.deepMergeTreeNodes(i,s,r):s;JSON.stringify(i)!==JSON.stringify(l)&&(this.treeState[r]=l,t=!0)}return{html:this.reconstructFromTree(this.treeState,""),changed:t}}reset(){this.treeState={},this.rangeState={},this.rangeIdKeys={}}getTreeState(){return{...this.treeState}}getStaticStructure(){return this.treeState.s||null}deepMergeTreeNodes(e,t,n=""){var s;if(typeof t!="object"||t===null||Array.isArray(t)||typeof e!="object"||e===null||Array.isArray(e))return t;let r={...e};for(let[o,i]of Object.entries(t)){let l=n?`${n}.${o}`:o,c=Array.isArray(i)&&i.length>0&&Array.isArray(i[0])&&typeof i[0][0]=="string",d=r[o]&&typeof r[o]=="object"&&!Array.isArray(r[o])&&Array.isArray(r[o].d)&&Array.isArray(r[o].s);c&&d?(r[o]=Re(r[o]),this.logger.debug(`[deepMerge] Applying diff ops at path ${l}`,{ops:i,rangeItems:(s=r[o].d)==null?void 0:s.length}),this.applyDifferentialOpsToRange(r[o],i,l)):typeof i=="object"&&i!==null&&!Array.isArray(i)&&typeof r[o]=="object"&&r[o]!==null&&!Array.isArray(r[o])?r[o]=this.deepMergeTreeNodes(r[o],i,l):r[o]=i}return r}applyDifferentialOpsToRange(e,t,n){if(!e||typeof e!="object"||!Array.isArray(e.d)||!Array.isArray(e.s)){this.logger.error(`[applyDiffOpsToRange] Invalid rangeStructure at path ${n}`,{rangeStructure:e});return}let r=e.d;this.rangeState[n]||(this.rangeState[n]={items:r,statics:e.s}),e.m&&typeof e.m=="object"&&typeof e.m.idKey=="string"&&(this.rangeIdKeys[n]=e.m.idKey),this.logger.debug(`[applyDiffOpsToRange] path=${n}, idKey=${this.rangeIdKeys[n]}, items=${r.length}, ops=${t.length}`);for(let s of t){if(!Array.isArray(s)||s.length<2)continue;switch(s[0]){case"r":{let i=s[1],l=this.findItemIndexByKey(r,i,e.s,n);this.logger.debug(`[applyDiffOpsToRange] Remove: key=${i}, index=${l}, total=${r.length}`),l>=0?(r.splice(l,1),this.logger.debug(`[applyDiffOpsToRange] After removal: ${r.length} items`)):this.logger.debug(`[applyDiffOpsToRange] Remove failed: key ${i} not found`);break}case"u":{let i=this.findItemIndexByKey(r,s[1],e.s,n),l=s[2];i>=0&&l&&(r[i]={...r[i],...l});break}case"a":{let i=Array.isArray(s[1])?s[1]:[s[1]];s[2]&&(e.s=s[2]),r.push(...i),s[3]&&typeof s[3]=="object"&&s[3].idKey&&(this.rangeIdKeys[n]=s[3].idKey);break}case"p":{let i=Array.isArray(s[1])?s[1]:[s[1]];s[2]&&(e.s=s[2]),r.unshift(...i);break}case"i":{let i=this.findItemIndexByKey(r,s[1],e.s,n);if(i>=0){let l=Array.isArray(s[2])?s[2]:[s[2]];r.splice(i+1,0,...l)}break}case"o":{let i=s[1],l=[],c=new Map;for(let d of r){let u=this.getItemKey(d,e.s,n);u&&c.set(u,d)}for(let d of i){let u=c.get(d);u&&l.push(u)}r.length=0,r.push(...l);break}default:break}}this.rangeState[n]={items:r,statics:e.s}}reconstructFromTree(e,t){if(e.s&&Array.isArray(e.s)){let n="";for(let r=0;r<e.s.length;r++){let s=e.s[r];if(n+=s,r<e.s.length-1){let o=r.toString();if(e[o]!==void 0){let i=t?`${t}.${o}`:o;n+=this.renderValue(e[o],o,i)}}}return n=n.replace(/<root>/g,"").replace(/<\/root>/g,""),n}return this.renderValue(e,"",t)}renderValue(e,t,n){if(e==null||typeof e=="string"&&e.startsWith("{{")&&e.endsWith("}}"))return"";if(typeof e=="object"&&!Array.isArray(e)){if(e.d&&Array.isArray(e.d)&&e.s&&Array.isArray(e.s)){let o=n||t||"";return o&&(this.rangeState[o]={items:e.d,statics:e.s},e.m&&typeof e.m=="object"&&typeof e.m.idKey=="string"&&(this.rangeIdKeys[o]=e.m.idKey)),this.renderRangeStructure(e,t,n)}if("s"in e&&Array.isArray(e.s))return this.reconstructFromTree(e,n||"");let r=Object.keys(e),s=r.filter(o=>/^\d+$/.test(o)).sort((o,i)=>parseInt(o)-parseInt(i));if(s.length>0&&s.length===r.length)return s.map(o=>{let i=n?`${n}.${o}`:o;return this.renderValue(e[o],o,i)}).join("")}return Array.isArray(e)?e.length>0&&Array.isArray(e[0])&&typeof e[0][0]=="string"?this.applyDifferentialOperations(e,n):e.map((r,s)=>{let o=s.toString(),i=n?`${n}.${o}`:o;return typeof r=="object"&&r&&r.s?this.reconstructFromTree(r,i):this.renderValue(r,o,i)}).join(""):typeof e=="object"?(this.logger.debug("Skipping plain object value (not a tree node) - this is normal for state-only data"),""):String(e)}renderRangeStructure(e,t,n){let{d:r,s}=e;if(!r||!Array.isArray(r))return"";if(r.length===0){if(e.else){let o="else",i=n?`${n}.else`:"else";return this.renderValue(e.else,o,i)}return""}return s&&Array.isArray(s)?r.map((o,i)=>{let l="";for(let c=0;c<s.length;c++)if(l+=s[c],c<s.length-1){let d=c.toString();if(o[d]!==void 0){let u=n?`${n}.${i}.${d}`:`${i}.${d}`;l+=this.renderValue(o[d],d,u)}}return l}).join(""):r.map((o,i)=>{let l=i.toString(),c=n?`${n}.${l}`:l;return this.renderValue(o,l,c)}).join("")}applyDifferentialOperations(e,t){if(!t||!this.rangeState[t])return"";let n=this.rangeState[t],r=[...n.items],s=n.statics;for(let i of e){if(!Array.isArray(i)||i.length<2)continue;switch(i[0]){case"r":{let c=this.findItemIndexByKey(r,i[1],s,t);c>=0&&r.splice(c,1);break}case"u":{let c=this.findItemIndexByKey(r,i[1],s,t),d=i[2];c>=0&&d&&(r[c]={...r[c],...d});break}case"a":{this.addItemsToRange(r,i[1],i[2],n,!1),i[3]&&typeof i[3]=="object"&&i[3].idKey&&(this.rangeIdKeys[t||""]=i[3].idKey);break}case"p":{this.addItemsToRange(r,i[1],i[2],n,!0);break}case"i":{let c=this.findItemIndexByKey(r,i[1],s,t);if(c>=0){let d=Array.isArray(i[2])?i[2]:[i[2]];r.splice(c+1,0,...d)}break}case"o":{let c=i[1],d=[],u=new Map;for(let g of r){let f=this.getItemKey(g,s,t);f&&u.set(f,g)}for(let g of c){let f=u.get(g);f&&d.push(f)}r.length=0,r.push(...d);break}default:break}}this.rangeState[t]={items:r,statics:n.statics},this.treeState[t]={d:r,s:n.statics};let o=this.getCurrentRangeStructure(t);return o&&o.s?this.renderItemsWithStatics(r,o.s):r.map(i=>this.renderValue(i)).join("")}getCurrentRangeStructure(e){if(this.rangeState[e])return{d:this.rangeState[e].items,s:this.rangeState[e].statics};let t=this.treeState[e];return t&&typeof t=="object"&&t.s?t:null}renderItemsWithStatics(e,t){let n=e.map(r=>{let s="";for(let o=0;o<t.length;o++)if(s+=t[o],o<t.length-1){let i=o.toString();r[i]!==void 0&&(s+=this.renderValue(r[i]))}return s}).join("");return this.logger.isDebugEnabled()&&(this.logger.debug("[renderItemsWithStatics] statics:",t),this.logger.debug("[renderItemsWithStatics] items count:",e.length),this.logger.debug("[renderItemsWithStatics] result snippet:",n.substring(0,200))),n}addItemsToRange(e,t,n,r,s){if(n&&(r.statics=n),!t)return;let o=Array.isArray(t)?t:[t];s?e.unshift(...o):e.push(...o)}getItemKey(e,t,n){if(!n||!this.rangeIdKeys[n])return null;let r=this.rangeIdKeys[n];return e[r]||null}findItemIndexByKey(e,t,n,r){return e.findIndex(s=>this.getItemKey(s,n,r)===t)}};var te=class{constructor(e){this.modalManager=e;this.activeForm=null;this.activeButton=null;this.originalButtonText=null}setActiveSubmission(e,t,n){this.activeForm=e,this.activeButton=t,this.originalButtonText=n}handleResponse(e){this.activeForm&&this.activeForm.dispatchEvent(new CustomEvent("lvt:done",{detail:e})),e.success?this.handleSuccess(e):this.handleError(e),this.restoreFormState()}reset(){this.restoreFormState()}handleSuccess(e){if(!this.activeForm)return;this.activeForm.dispatchEvent(new CustomEvent("lvt:success",{detail:e}));let t=this.activeForm.closest('[role="dialog"]');t&&t.id&&this.modalManager.close(t.id),this.activeForm.hasAttribute("lvt-preserve")||this.activeForm.reset()}handleError(e){this.activeForm&&this.activeForm.dispatchEvent(new CustomEvent("lvt:error",{detail:e}))}restoreFormState(){this.activeButton&&this.originalButtonText!==null&&(this.activeButton.disabled=!1,this.activeButton.textContent=this.originalButtonText),this.activeForm=null,this.activeButton=null,this.originalButtonText=null}};var fe=class{constructor(e){this.options=e;this.socket=null;this.reconnectTimer=null;this.manuallyClosed=!1;this.reconnectAttempts=0}connect(){this.manuallyClosed=!1,this.clearReconnectTimer(),this.socket=new WebSocket(this.options.url);let e=this.socket;e.onopen=()=>{var t,n;this.reconnectAttempts=0,(n=(t=this.options).onOpen)==null||n.call(t,e)},e.onmessage=t=>{var n,r;(r=(n=this.options).onMessage)==null||r.call(n,t)},e.onclose=t=>{var n,r;(r=(n=this.options).onClose)==null||r.call(n,t),!this.manuallyClosed&&this.options.autoReconnect&&this.scheduleReconnect()},e.onerror=t=>{var n,r;(r=(n=this.options).onError)==null||r.call(n,t)}}send(e){this.socket&&this.socket.readyState===1&&this.socket.send(e)}disconnect(){this.manuallyClosed=!0,this.clearReconnectTimer(),this.socket&&(this.socket.close(),this.socket=null)}getSocket(){return this.socket}scheduleReconnect(){var i,l,c,d,u;this.clearReconnectTimer();let e=(i=this.options.maxReconnectAttempts)!=null?i:10;if(e>0&&this.reconnectAttempts>=e){(c=(l=this.options).onReconnectFailed)==null||c.call(l);return}this.reconnectAttempts++;let t=(d=this.options.reconnectDelay)!=null?d:1e3,n=(u=this.options.maxReconnectDelay)!=null?u:16e3,r=t*Math.pow(2,this.reconnectAttempts-1),s=Math.random()*1e3,o=Math.min(r+s,n);this.reconnectTimer=window.setTimeout(()=>{var g,f;(f=(g=this.options).onReconnectAttempt)==null||f.call(g,this.reconnectAttempts,o),this.connect()},o)}clearReconnectTimer(){this.reconnectTimer!==null&&(clearTimeout(this.reconnectTimer),this.reconnectTimer=null)}},ne=class{constructor(e){this.config=e;this.transport=null}async connect(){let e=this.getLiveUrl();return await ot(e,this.config.logger)?(this.transport=new fe({url:this.getWebSocketUrl(),autoReconnect:this.config.options.autoReconnect,reconnectDelay:this.config.options.reconnectDelay,maxReconnectDelay:16e3,maxReconnectAttempts:10,onOpen:()=>{this.config.onConnected()},onMessage:n=>{try{let r=JSON.parse(n.data);this.config.onMessage(r,n)}catch(r){this.config.logger.error("Failed to parse WebSocket message:",r)}},onClose:()=>{this.config.onDisconnected()},onReconnectAttempt:(n,r)=>{var s,o;(o=(s=this.config).onReconnectAttempt)==null||o.call(s,n,r)},onReconnectFailed:()=>{var n,r;(r=(n=this.config).onReconnectFailed)==null||r.call(n)},onError:n=>{var r,s;(s=(r=this.config).onError)==null||s.call(r,n)}}),this.transport.connect(),{usingWebSocket:!0}):{usingWebSocket:!1,initialState:await at(e,this.config.logger)}}disconnect(){var e;(e=this.transport)==null||e.disconnect(),this.transport=null}send(e){var t;(t=this.transport)==null||t.send(e)}getReadyState(){var e,t;return(t=(e=this.transport)==null?void 0:e.getSocket())==null?void 0:t.readyState}getSocket(){var e,t;return(t=(e=this.transport)==null?void 0:e.getSocket())!=null?t:null}getWebSocketUrl(){let e=this.config.options.liveUrl||"/live",t=this.config.options.wsUrl;return t||`ws://${window.location.host}${e}`}getLiveUrl(){return this.config.options.liveUrl||window.location.pathname}};async function ot(a,e){try{let n=(await fetch(a,{method:"HEAD"})).headers.get("X-LiveTemplate-WebSocket");return n?n==="enabled":!0}catch(t){return e==null||e.warn("Failed to check WebSocket availability:",t),!0}}async function at(a,e){try{let t=await fetch(a,{method:"GET",credentials:"include",headers:{Accept:"application/json"}});if(!t.ok)throw new Error(`Failed to fetch initial state: ${t.status}`);return await t.json()}catch(t){return e==null||e.warn("Failed to fetch initial state:",t),null}}var re=class{async upload(e,t,n){let{file:r}=e;e.abortController=new AbortController;try{let s=new XMLHttpRequest;s.upload.addEventListener("progress",i=>{i.lengthComputable&&(e.bytesUploaded=i.loaded,e.progress=Math.round(i.loaded/i.total*100),n&&n(e))}),e.abortController.signal.addEventListener("abort",()=>{s.abort()});let o=new Promise((i,l)=>{s.addEventListener("load",()=>{s.status>=200&&s.status<300?(e.done=!0,e.progress=100,i()):l(new Error(`S3 upload failed with status ${s.status}: ${s.statusText}`))}),s.addEventListener("error",()=>{l(new Error("S3 upload failed: Network error"))}),s.addEventListener("abort",()=>{l(new Error("S3 upload cancelled"))})});if(s.open("PUT",t.url),t.headers)for(let[i,l]of Object.entries(t.headers))s.setRequestHeader(i,l);s.send(r),await o}catch(s){throw e.error=s instanceof Error?s.message:String(s),s}}};var se=class{constructor(e,t={}){this.sendMessage=e;this.entries=new Map;this.pendingFiles=new Map;this.autoUploadConfig=new Map;this.uploaders=new Map;this.inputHandlers=new WeakMap;this.chunkSize=t.chunkSize||256*1024,this.onProgress=t.onProgress,this.onComplete=t.onComplete,this.onError=t.onError,this.uploaders.set("s3",new re)}initializeFileInputs(e){e.querySelectorAll('input[type="file"][lvt-upload]').forEach(n=>{let r=n.getAttribute("lvt-upload");if(!r)return;let s=this.inputHandlers.get(n);s&&n.removeEventListener("change",s);let o=i=>{let l=i.target.files;!l||l.length===0||this.startUpload(r,Array.from(l))};n.addEventListener("change",o),this.inputHandlers.set(n,o)})}async startUpload(e,t){this.pendingFiles.set(e,t);let n=t.map(s=>({name:s.name,type:s.type||"application/octet-stream",size:s.size})),r={action:"upload_start",upload_name:e,files:n};this.sendMessage(r)}async handleUploadStartResponse(e){let{upload_name:t,entries:n}=e;n.length>0&&this.autoUploadConfig.set(t,n[0].auto_upload);let r=this.pendingFiles.get(t);if(!r){console.error(`No pending files found for upload: ${t}`);return}this.pendingFiles.delete(t);let s=new Map;for(let i of r)s.set(i.name,i);let o=[];for(let i of n){let l=s.get(i.client_name);if(!l){console.warn(`No file found for entry ${i.entry_id} (client_name: ${i.client_name})`);continue}let c={id:i.entry_id,file:l,uploadName:t,progress:0,bytesUploaded:0,valid:i.valid,done:!1,error:i.error,external:i.external};if(this.entries.set(c.id,c),o.push(c),!i.valid){this.onError&&i.error&&this.onError(c,i.error);continue}i.auto_upload&&(i.external?this.uploadExternal(c,i.external):this.uploadChunked(c))}}async uploadExternal(e,t){try{let n=this.uploaders.get(t.uploader);if(!n)throw new Error(`Unknown uploader: ${t.uploader}`);await n.upload(e,t,this.onProgress);let r={action:"upload_complete",upload_name:e.uploadName,entry_ids:[e.id]};this.sendMessage(r),this.onComplete&&this.onComplete(e.uploadName,[e]),this.clearFileInput(e.uploadName),this.cleanupEntries(e.uploadName)}catch(n){let r=n instanceof Error?n.message:String(n);e.error=r,this.onError&&this.onError(e,r),this.cleanupEntries(e.uploadName)}}async uploadChunked(e){let{file:t,id:n}=e,r=0;e.abortController=new AbortController;try{for(;r<t.size;){if(e.abortController.signal.aborted)throw new Error("Upload cancelled");let o=Math.min(r+this.chunkSize,t.size),i=t.slice(r,o),l=await this.fileToBase64(i),c={action:"upload_chunk",entry_id:n,chunk_base64:l,offset:r,total:t.size};this.sendMessage(c),r=o,e.bytesUploaded=r,e.progress=Math.round(r/t.size*100),this.onProgress&&this.onProgress(e)}e.done=!0;let s={action:"upload_complete",upload_name:e.uploadName,entry_ids:[n]};this.sendMessage(s),this.onComplete&&this.onComplete(e.uploadName,[e]),this.clearFileInput(e.uploadName),this.cleanupEntries(e.uploadName)}catch(s){let o=s instanceof Error?s.message:String(s);e.error=o,this.onError&&this.onError(e,o),this.cleanupEntries(e.uploadName)}}handleProgressMessage(e){let t=this.entries.get(e.entry_id);t&&(t.progress=e.progress,t.bytesUploaded=e.bytes_recv,this.onProgress&&this.onProgress(t))}cancelUpload(e){let t=this.entries.get(e);t&&(t.abortController&&t.abortController.abort(),this.sendMessage({action:"cancel_upload",entry_id:e}),this.entries.delete(e))}getEntries(e){let t=[];for(let n of this.entries.values())n.uploadName===e&&t.push(n);return t}triggerPendingUploads(e){let t=[];for(let n of this.entries.values())n.uploadName===e&&n.progress===0&&!n.done&&!n.error&&t.push(n);for(let n of t)n.external?this.uploadExternal(n,n.external):this.uploadChunked(n)}registerUploader(e,t){this.uploaders.set(e,t)}clearFileInput(e){document.querySelectorAll(`input[type="file"][lvt-upload="${e}"]`).forEach(n=>{n.value=""})}cleanupEntries(e,t=5e3){setTimeout(()=>{let n=[];for(let[r,s]of this.entries)e&&s.uploadName!==e||(s.done||s.error)&&n.push(r);for(let r of n)this.entries.delete(r)},t)}fileToBase64(e){return new Promise((t,n)=>{let r=new FileReader;r.onload=()=>{let o=r.result.split(",")[1];t(o)},r.onerror=n,r.readAsDataURL(e)})}};var Fe={silent:0,error:1,warn:2,info:3,debug:4},_e="LiveTemplate",he=class a{constructor(e,t=[],n=console){this.state=e;this.scope=t;this.sink=n}setLevel(e){this.state.level=e}getLevel(){return this.state.level}child(e){return new a(this.state,[...this.scope,e],this.sink)}isDebugEnabled(){return this.shouldLog("debug")}error(...e){this.log("error","error",e)}warn(...e){this.log("warn","warn",e)}info(...e){this.log("info","info",e)}debug(...e){this.log("debug","debug",e)}log(e,t,n){if(!this.shouldLog(e))return;(this.sink[t]||console[t]||console.log).apply(this.sink,[this.formatPrefix(),...n])}shouldLog(e){return Fe[e]<=Fe[this.state.level]}formatPrefix(){return this.scope.length===0?`[${_e}]`:`[${_e}:${this.scope.join(":")}]`}};function me(a={}){var n,r;let e={level:(n=a.level)!=null?n:"info"},t=Array.isArray(a.scope)?a.scope:a.scope?[a.scope]:[];return new he(e,t,(r=a.sink)!=null?r:console)}async function Ie(a,e){try{let t=globalThis==null?void 0:globalThis.require;if(typeof t=="function"){let s=t("fs"),o=JSON.parse(s.readFileSync(e,"utf8"));return a.applyUpdate(o)}let r=await(await fetch(e)).json();return a.applyUpdate(r)}catch(t){throw new Error(`Failed to load update from ${e}: ${t}`)}}function He(a,e){let t=[],n=c=>c.replace(/\s+/g," ").replace(/>\s+</g,"><").trim(),r=n(a),s=n(e);if(r===s)return{match:!0,differences:[]};let o=r.split(`

Copilot AI Dec 22, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This use of variable 't' always evaluates to true.

Copilot uses AI. Check for mistakes.
- Update github.com/livetemplate/livetemplate from v0.7.1 to v0.7.3
- Update embedded client to v0.7.3

These updates add StaticsMap support for heterogeneous range items,
which is required for proper rendering of ranges with conditional content.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@adnaan
adnaan force-pushed the chore/update-deps-v0.7.3 branch from 90df718 to ef8ea10 Compare December 22, 2025 08:30
@claude

claude Bot commented Dec 22, 2025

Copy link
Copy Markdown

Pull Request Review: Update livetemplate to v0.7.3 and client to v0.7.2

Summary

This PR updates dependency versions for the livetemplate core library and embedded client to support StaticsMap functionality for heterogeneous range items. The changes are minimal and focused on dependency updates plus documentation simplification.


Code Quality: ✅ GOOD

Positive Aspects:

  1. Focused scope: The PR is appropriately scoped to dependency updates and related documentation
  2. Clear commit message: Follows conventional commit format with descriptive body
  3. Related PRs linked: Good traceability to upstream changes
  4. Documentation updated: Settings and skill documentation simplified appropriately

Changes Breakdown:

1. Dependency Updates (go.mod, go.sum)

File: go.mod

-require github.com/livetemplate/livetemplate v0.7.1
+require github.com/livetemplate/livetemplate v0.7.3

GOOD:

  • Minor version bump (v0.7.1 → v0.7.3) is appropriate for feature additions
  • Follows semantic versioning
  • go.sum updated correctly with matching checksums

2. Documentation Updates

File: commands/claude_resources/settings.json

The instructions field was drastically simplified from ~400 lines to ~20 lines. This is a significant change that deserves scrutiny:

BEFORE: Detailed instructions with:

  • Multi-phase brainstorming workflow
  • Step-by-step trigger patterns
  • Examples of when/when not to use lvt-plan skill
  • Comprehensive skill list

AFTER: Condensed to:

  • Skill override directive
  • Brief explanation of lvt-plan workflow
  • Minimal skill list (3 skills vs many)

⚠️ CONCERN: This appears to be a breaking change to AI assistant behavior:

  • Removes detailed context about when to use lvt-plan
  • Removes comprehensive skill list (was this intentional?)
  • Changes from progressive/detailed guidance to minimal directive
  • The commit message describes this as "chore" but this seems like a behavior change

File: commands/claude_resources/skills/plan/SKILL.md

Similarly drastically simplified from ~400 lines to ~56 lines:

BEFORE (v1.6.0):

  • Detailed activation rules
  • Domain-specific defaults with full examples
  • Error handling
  • Version history
  • Multi-step flow diagrams

AFTER (v3.0.0):

  • Template-driven approach
  • Lookup table for domains
  • Minimal instructions

GOOD: The simplification makes the skill easier to understand and use
⚠️ CONCERN: Version jump from 1.6.0 → 3.0.0 suggests major changes, but this is buried in a "chore" PR


3. Client Update

File: testing/livetemplate-client.browser.js

Minified JavaScript bundle updated (appears to be the client v0.7.2). Hard to review minified code, but checksums in browser imports updated consistently.

✅ Properly updated to match the new client version


Potential Bugs/Issues: ⚠️ MODERATE RISK

1. Breaking Changes Disguised as Chore

  • The PR is labeled "chore:" but contains behavioral changes to AI assistance
  • settings.json and SKILL.md changes could significantly alter how Claude Code behaves
  • Recommendation: Consider if this should be a minor/major version bump, not just a patch

2. Missing Context on StaticsMap Changes

  • PR description mentions "StaticsMap support for heterogeneous range items" but doesn't explain:
    • What StaticsMap is
    • Why it's needed
    • What heterogeneous range items are
    • How this affects existing code
  • Recommendation: Add a brief technical explanation to the PR description

3. Removed Skills Not Documented

The settings.json previously listed many skills:

  • lvt-new-app
  • lvt-add-resource
  • lvt-gen-auth
  • lvt-add-migration
  • lvt-deploy
  • And more...

Now only 3 are mentioned:

  • add-resource
  • gen-auth
  • deploy

Question: Were these skills removed? Renamed? Or just unlisted?

  • Recommendation: Clarify the skill inventory changes

4. No Tests Updated

  • No test files modified
  • Question: Are there tests that verify the dependency upgrade works correctly?
  • Recommendation: Consider adding a test or integration check for StaticsMap functionality

Performance Considerations: ✅ ACCEPTABLE

  • Dependency updates are minor version bumps (no expected performance regression)
  • Simplified documentation should reduce token usage for AI assistants (✅ improvement)
  • Client bundle size appears similar (no major bloat)

Security Concerns: ✅ LOW RISK

  • Updates are from the same trusted upstream repository (livetemplate/livetemplate)
  • No new dependencies introduced
  • go.sum checksums properly updated
  • No code execution changes in lvt itself, only in dependencies

Minor note: The minified client bundle is hard to audit, but this is standard practice for browser bundles.


Test Coverage: ⚠️ NEEDS ATTENTION

Missing:

  1. No test changes: PR doesn't update any test files
  2. No integration test: Should verify StaticsMap functionality works
  3. No regression test: Should ensure heterogeneous range items work correctly

Recommendations:

# Suggested test additions
1. Add test case for heterogeneous range items with StaticsMap
2. Update existing tests if StaticsMap affects current behavior
3. Add smoke test for todos example mentioned in PR description

Question for reviewer: Have you manually tested:

  • The todos example works with heterogeneous ranges?
  • Existing templates with Range.Statics still work?
  • Client-side StaticsMap rendering works?

Overall Assessment: ⚠️ APPROVE WITH MINOR CONCERNS

Strengths:

✅ Proper dependency version management
✅ Clean, focused changes
✅ Related PRs properly linked
✅ Simplified documentation is clearer

Concerns:

⚠️ Documentation changes seem significant for a "chore" commit
⚠️ Skill removal/simplification not explicitly documented
⚠️ No tests added for new functionality
⚠️ Missing context on StaticsMap changes


Recommendations:

Before Merging:

  1. Clarify the scope: Is this truly a "chore" or should it be "feat" or "refactor"?
  2. Document skill changes: Explain what happened to the skills no longer listed
  3. Add context: Brief explanation of StaticsMap in PR description
  4. Manual testing: Verify todos example and heterogeneous ranges work

Optional Improvements:

  1. Add tests: Integration test for StaticsMap functionality
  2. Changelog: Document the breaking change in SKILL.md version (1.6.0 → 3.0.0)
  3. Version notes: Add migration guide if AI assistant behavior changed significantly

Final Verdict: APPROVE (with confidence that recommendations will be addressed)

This is a solid dependency update PR with good hygiene. The main concerns are around documentation/scope classification rather than code quality. The changes themselves appear correct and necessary for the new functionality.

Confidence Level: 85% - Would be 95% with test coverage and clearer documentation of behavioral changes.

@adnaan
adnaan merged commit e541db7 into main Dec 22, 2025
2 checks passed
@adnaan
adnaan deleted the chore/update-deps-v0.7.3 branch December 22, 2025 09:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants