Skip to content

Add click ripple extension example#425

Closed
willytop8 wants to merge 1 commit intowebadderallorg:mainfrom
willytop8:feat/click-ripple-example
Closed

Add click ripple extension example#425
willytop8 wants to merge 1 commit intowebadderallorg:mainfrom
willytop8:feat/click-ripple-example

Conversation

@willytop8
Copy link
Copy Markdown

@willytop8 willytop8 commented May 5, 2026

What

Adds extension-examples/willytop8.click-ripple, a cursor-effect example with ripple, pulse, and burst click animations.

Why

EXTENSIONS.md documents registerCursorEffect(), but there was no packaged cursor-effect example.

Testing

  • Ran npm run build
  • Ran npx tsc --noEmit
  • Manually verified activation, settings, and preview rendering

Summary by CodeRabbit

  • New Features

    • Added "Click Ripple" cursor effect extension example with three animation styles: ripple, pulse, and burst
    • Includes customizable settings for enable/disable toggle, animation style, color, size, duration, and line thickness
    • Supports distinct right-click styling for enhanced visual feedback
  • Documentation

    • Added comprehensive README with installation, configuration, and build instructions for the extension

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 5, 2026

📝 Walkthrough

Walkthrough

A new cursor-effect extension example "Click Ripple" is added, demonstrating Recordly's extension API with TypeScript implementation of ripple, pulse, and burst click animations. Includes complete project structure with build configuration, type definitions, settings UI, and documentation.

Changes

Click Ripple Extension Example

Layer / File(s) Summary
API Type Definitions
extension-examples/willytop8.click-ripple/src/recordly-types.d.ts
Comprehensive TypeScript declarations for the Recordly Extension API, including contributed assets (cursor styles, sounds, wallpapers, frames), render hooks, cursor effects, settings panels, events, and the main RecordlyExtensionAPI interface with registration, query, and subscription methods.
Extension Implementation
extension-examples/willytop8.click-ripple/src/index.ts
Implements activate() and deactivate() functions that register a settings panel for click effects (enable, style, color, size, duration, line thickness, distinct right-click mode) and a cursor-effect callback that renders ripple/pulse/burst animations on canvas using easing and coordinate transforms.
Build & Compilation
extension-examples/willytop8.click-ripple/build.mjs, tsconfig.json, package.json
ESBuild configuration targeting ESM output (dist/index.js) with ES2022 and watch-mode support; TypeScript compiler config with strict mode and bundler module resolution; Node.js package metadata with build and watch scripts.
Extension Manifest
extension-examples/willytop8.click-ripple/recordly-extension.json
Declares extension identity (dev.willytop8.click-ripple), version, description, required Recordly engine version (1.2.0), entry point (dist/index.js), and permissions (cursor, ui).
Documentation & Project Setup
extension-examples/willytop8.click-ripple/README.md, LICENSE, .gitignore, EXTENSIONS.md
Installation instructions, settings reference table, animation style descriptions, build commands, permission requirements, MIT license, standard ignore patterns, and registry entry linking to the example.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • webadderallorg/Recordly#249: Updates to the extension system's render hooks, cursor coordinates, and extension host infrastructure directly support the cursor-effect API demonstrated by this example.

Suggested labels

Checked

Poem

🐰 A ripple, a pulse, a burst of light,
Click effects dancing, shimmering bright,
TypeScript whispers, canvas does sing,
Extensions bloom with each recorded thing! ✨

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description covers the essential information (What/Why/Testing) but deviates significantly from the provided template structure and is missing required sections. Align the description with the repository template: add Type of Change, Related Issue(s), Screenshots/Video sections, and expand Testing Guide with detailed steps.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main change: adding a click ripple extension example.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@extension-examples/willytop8.click-ripple/src/index.ts`:
- Around line 9-35: registerSettingsPanel(...) and registerCursorEffect(...)
return dispose functions that are currently ignored, causing duplicate
registrations on re-activation; capture their return values (e.g., const
disposeSettings = api.registerSettingsPanel(...); const disposeEffect =
api.registerCursorEffect(...); or push them into a disposers array) and
implement deactivate() to call each stored disposer (and clear the array) so
panels/effects are removed on deactivate; reference the registerSettingsPanel,
registerCursorEffect and deactivate function names when making the change.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: c265f039-e710-45f2-8084-2aacfbddc293

📥 Commits

Reviewing files that changed from the base of the PR and between 42c6cf4 and 2e17ba8.

⛔ Files ignored due to path filters (2)
  • extension-examples/willytop8.click-ripple/dist/index.js is excluded by !**/dist/**
  • extension-examples/willytop8.click-ripple/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (10)
  • EXTENSIONS.md
  • extension-examples/willytop8.click-ripple/.gitignore
  • extension-examples/willytop8.click-ripple/LICENSE
  • extension-examples/willytop8.click-ripple/README.md
  • extension-examples/willytop8.click-ripple/build.mjs
  • extension-examples/willytop8.click-ripple/package.json
  • extension-examples/willytop8.click-ripple/recordly-extension.json
  • extension-examples/willytop8.click-ripple/src/index.ts
  • extension-examples/willytop8.click-ripple/src/recordly-types.d.ts
  • extension-examples/willytop8.click-ripple/tsconfig.json

Comment on lines +9 to +35
api.registerSettingsPanel({
id: "click-ripple-settings",
label: "Click Effects",
icon: "sparkles",
parentSection: "cursor",
fields: [
{ id: "enabled", label: "Enable click effects", type: "toggle", defaultValue: true },
{
id: "style",
label: "Style",
type: "select",
defaultValue: "ripple",
options: [
{ label: "Ripple (concentric rings)", value: "ripple" },
{ label: "Pulse (soft halo)", value: "pulse" },
{ label: "Burst (radial spokes)", value: "burst" },
],
},
{ id: "color", label: "Color", type: "color", defaultValue: "#2563EB" },
{ id: "size", label: "Size", type: "slider", defaultValue: 1.0, min: 0.5, max: 2.5, step: 0.1 },
{ id: "durationMs", label: "Duration (ms)", type: "slider", defaultValue: 600, min: 200, max: 1500, step: 50 },
{ id: "thickness", label: "Line thickness", type: "slider", defaultValue: 2, min: 1, max: 8, step: 1 },
{ id: "differentiateRightClick", label: "Distinct right-click style", type: "toggle", defaultValue: true },
],
});

api.registerCursorEffect((ctx: CursorEffectContext): boolean => {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Persist disposers and clean up on deactivate.

registerSettingsPanel() and registerCursorEffect() return dispose functions, but they’re ignored, while deactivate() is empty. On re-activation this can stack duplicate panel/effect registrations.

♻️ Proposed fix
 import type { RecordlyExtensionAPI, CursorEffectContext } from "./recordly-types";
 
 type Style = "ripple" | "pulse" | "burst";
+const disposers: Array<() => void> = [];
 
 const easeOut2 = (t: number) => 1 - Math.pow(1 - t, 2);
 const easeOut3 = (t: number) => 1 - Math.pow(1 - t, 3);
 
 export function activate(api: RecordlyExtensionAPI) {
-  api.registerSettingsPanel({
+  disposers.push(api.registerSettingsPanel({
     id: "click-ripple-settings",
     label: "Click Effects",
     icon: "sparkles",
     parentSection: "cursor",
     fields: [
@@
       { id: "thickness", label: "Line thickness", type: "slider", defaultValue: 2, min: 1, max: 8, step: 1 },
       { id: "differentiateRightClick", label: "Distinct right-click style", type: "toggle", defaultValue: true },
     ],
-  });
+  }));
 
-  api.registerCursorEffect((ctx: CursorEffectContext): boolean => {
+  disposers.push(api.registerCursorEffect((ctx: CursorEffectContext): boolean => {
     if (!api.getSetting("enabled")) return false;
@@
-    return true;
-  });
+    return true;
+  }));
 }
 
-export function deactivate() {}
+export function deactivate() {
+  while (disposers.length > 0) {
+    const dispose = disposers.pop();
+    dispose?.();
+  }
+}

Also applies to: 77-77

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@extension-examples/willytop8.click-ripple/src/index.ts` around lines 9 - 35,
registerSettingsPanel(...) and registerCursorEffect(...) return dispose
functions that are currently ignored, causing duplicate registrations on
re-activation; capture their return values (e.g., const disposeSettings =
api.registerSettingsPanel(...); const disposeEffect =
api.registerCursorEffect(...); or push them into a disposers array) and
implement deactivate() to call each stored disposer (and clear the array) so
panels/effects are removed on deactivate; reference the registerSettingsPanel,
registerCursorEffect and deactivate function names when making the change.

@webadderall
Copy link
Copy Markdown
Collaborator

i think this would be better served as an upload to extensions :)

@webadderall webadderall closed this May 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants