Skip to content

feat: add SocialShareDesigner for customizable social share buttons#95

Open
kpj2006 wants to merge 1 commit intoAOSSIE-Org:mainfrom
kpj2006:frame
Open

feat: add SocialShareDesigner for customizable social share buttons#95
kpj2006 wants to merge 1 commit intoAOSSIE-Org:mainfrom
kpj2006:frame

Conversation

@kpj2006
Copy link
Copy Markdown
Contributor

@kpj2006 kpj2006 commented Mar 12, 2026

  • Implemented a visual customization panel for the SocialShareButton using CSS custom properties.
  • Added support for multiple platforms including WhatsApp, Facebook, Twitter, LinkedIn, Telegram, Reddit, and Email.
  • Included options for button styles, colors, shapes, and modal settings.
  • Enabled gradient backgrounds and per-platform color customization.
  • Integrated a toast notification system for user feedback.
  • Provided functionality to export the theme as CSS variables.

Addressed Issues:

Fixes #(TODO:issue number)

Screenshots/Recordings:

TODO: If applicable, add screenshots or recordings that demonstrate the interface before and after the changes.

Additional Notes:

AI Usage Disclosure:

We encourage contributors to use AI tools responsibly when creating Pull Requests. While AI can be a valuable aid, it is essential to ensure that your contributions meet the task requirements, build successfully, include relevant tests, and pass all linters. Submissions that do not meet these standards may be closed without warning to maintain the quality and integrity of the project. Please take the time to understand the changes you are proposing and their impact. AI slop is strongly discouraged and may lead to banning and blocking. Do not spam our repos with AI slop.

Check one of the checkboxes below:

  • [] This PR does not contain AI-generated code at all.
  • This PR contains AI-generated code. I have read the AI Usage Policy and this PR complies with this policy. I have tested the code locally and I am responsible for it.

I have used the following AI models and tools: TODO
claude

Checklist

  • My PR addresses a single issue, fixes a single bug or makes a single improvement.
  • My code follows the project's code style and conventions
  • If applicable, I have made corresponding changes or additions to the documentation
  • If applicable, I have made corresponding changes or additions to tests
  • My changes generate no new warnings or errors
  • I have joined the Discord server and I will share a link to this PR with the project maintainers there
  • I have read the Contribution Guidelines
  • Once I submit my PR, CodeRabbit AI will automatically review it and I will address CodeRabbit's comments.
  • I have filled this PR template completely and carefully, and I understand that my PR may be closed without review otherwise.

Summary by CodeRabbit

New Features

  • Introduced an interactive Theme Designer for social share buttons with live preview functionality
  • Added customization options for button styling including colors, gradients, shapes, and border properties
  • Enabled per-platform icon color customization for major social networks
  • Added modal appearance controls (background, width, position, animation speed)
  • Implemented export functionality to save and share custom themes
  • Included theme presets (light/dark modes) and advanced styling options (font family, shadow intensity, hover effects)

- Implemented a visual customization panel for the SocialShareButton using CSS custom properties.
- Added support for multiple platforms including WhatsApp, Facebook, Twitter, LinkedIn, Telegram, Reddit, and Email.
- Included options for button styles, colors, shapes, and modal settings.
- Enabled gradient backgrounds and per-platform color customization.
- Integrated a toast notification system for user feedback.
- Provided functionality to export the theme as CSS variables.
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 12, 2026

Walkthrough

A new Theme Designer feature has been introduced with an HTML page, core JavaScript class, CSS styling, and React wrapper. The designer enables real-time customization of SocialShareButton appearance through a panel UI with two-pane layout, CSS variable management, and live preview synchronization.

Changes

Cohort / File(s) Summary
Designer Core Implementation
src/social-share-designer.js, src/social-share-designer.css
Core designer class with comprehensive UI panel, state management, CSS variable synchronization, event binding, platform registry, mutation observer for overlay tracking, and destroy cleanup. Complete stylesheet with dark theme, interactive controls (toggles, color swatches, gradient editor, sliders), per-platform icon customization, and toast notifications.
Designer HTML Interface
designer.html
New HTML page implementing two-pane layout with sticky navigation, live preview pane, and designer panel. Loads external scripts, initializes designer and preview instances, includes responsive design and embedded CSS overrides for dark theme.
React Integration
src/social-share-designer-react.jsx
React wrapper component that ties into global window.SocialShareDesigner API, manages panelContainer via ref, handles targetButton prop changes with reinitialize logic, and includes cleanup on unmount.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant DesignerPanel as Designer UI
    participant SocialShareDesigner as Designer Class
    participant DOM as DOM/CSS Variables
    participant Preview as Live Preview Button
    
    User->>DesignerPanel: Opens Theme Designer
    DesignerPanel->>SocialShareDesigner: Initialize(panelContainer)
    SocialShareDesigner->>DOM: Read existing CSS variables
    SocialShareDesigner->>DesignerPanel: Render sections (Theme, Colors, Shape, etc.)
    SocialShareDesigner->>Preview: Initialize preview instance
    SocialShareDesigner->>DOM: Setup mutation observer on overlay
    
    User->>DesignerPanel: Adjust color/style setting
    DesignerPanel->>SocialShareDesigner: Trigger change event
    SocialShareDesigner->>DOM: Update CSS custom property
    DOM->>Preview: Apply updated styles via CSS vars
    Preview-->>User: Display live preview update
    
    User->>DesignerPanel: Toggle platform visibility
    DesignerPanel->>SocialShareDesigner: Update platform state
    SocialShareDesigner->>DOM: Modify overlay platform list
    SocialShareDesigner->>Preview: Sync preview modal content
    Preview-->>User: Show updated modal preview
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Suggested labels

Typescript Lang

Suggested reviewers

  • Zahnentferner

Poem

🐰 A designer blooms, vibrant and bright,
CSS variables dance in the night,
Platforms and colors, shaped just right,
Live previews gleaming with pure delight,
Theme perfection now within sight! ✨

🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: adding a new SocialShareDesigner feature for customizing social share buttons, which aligns with the four new files (designer.html, social-share-designer.js, social-share-designer.css, and social-share-designer-react.jsx) that collectively implement this functionality.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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: 10

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@designer.html`:
- Around line 379-381: The nav export click handler assumes the dynamically
created element '#ssb-btn-export' exists (created by SocialShareDesigner) and
will throw if the designer fails to initialize; update the handler attached to
document.getElementById('nav-export-btn') to first check that
document.getElementById('ssb-btn-export') is non-null (or use optional chaining)
before calling .click(), and/or guard the listener attachment itself by ensuring
the nav button exists; reference the '#nav-export-btn' click handler and the
target '#ssb-btn-export' (and the SocialShareDesigner initialization) when
applying this null-check guard so the handler no-ops safely if the export button
isn't present.
- Line 277: Buttons without an explicit type default to "submit" and can
accidentally submit forms; update each button element to include type="button"
(for example the button with id "nav-export-btn" and the other nav buttons
referenced near lines 304 and 315) so they no longer act as submit
controls—locate the <button> tags (e.g., id="nav-export-btn") and add the
attribute type="button" to each.

In `@src/social-share-designer-react.jsx`:
- Around line 24-47: The bug is double-initializing SocialShareDesigner on mount
and leaving CSS vars set after destroy; fix by (1) in
src/social-share-designer-react.jsx add an initialMount guard (e.g., a ref like
initialMountRef) so the useEffect that depends on targetButton skips running on
the initial mount (only runs on subsequent targetButton changes) and only
recreates the designer when necessary, referencing designerRef and the effect
with [targetButton]; and (2) in src/social-share-designer.js ensure the
destroy() method calls the internal _resetAllVars() (or inline the logic that
clears document.documentElement CSS variables) so CSS variables are cleared when
designer.destroy() is invoked, preventing persistent styling between
destroy/reinitialize cycles.
- Line 1: Add the Next.js client directive by inserting the exact string "use
client" as the very first line of the module (above the import of
useEffect/useRef) so this React component file runs on the client; ensure it
appears before any imports and top-level code in
src/social-share-designer-react.jsx to comply with Next.js and the project's
React component guidelines.
- Around line 16-69: The SocialShareDesigner React component and its two
useEffect blocks lack inline comments describing non-obvious behavior and safety
checks; add brief comments explaining the component purpose and the logic in the
first useEffect (lazy-init: checking panelRef, designerRef, and
window.SocialShareDesigner, warning when the global isn't present, creating
designerRef.current, and the cleanup that destroys the instance) and in the
second useEffect (rebuild-on-targetButton change: destroy existing designer,
guard on panelRef, re-create if global is available). Reference the component
name SocialShareDesigner, refs panelRef and designerRef, the prop targetButton,
and the global window.SocialShareDesigner in your comments so future readers
understand the edge cases and lifecycle reasoning.

In `@src/social-share-designer.js`:
- Around line 25-44: Remove the unused DEFAULTS constant definition from
src/social-share-designer.js (the object labeled DEFAULTS with CSS variable
keys) since it is dead code; delete the entire const DEFAULTS = { ... }; block
and any trailing comma or blank line left behind, then run the repo search
(e.g., ripgrep) and tests to confirm no remaining references and no lint issues;
if the values were intended to be used, instead refactor code paths that build
styles (e.g., functions that reference CSS variables) to import or consume
DEFAULTS rather than deleting it.
- Around line 939-949: The destroy() method currently disconnects observers and
removes DOM elements but doesn't clear global CSS vars; call the existing
_resetAllVars() from destroy() to remove CSS variables on
document.documentElement before finishing cleanup (i.e., add a call to
this._resetAllVars() inside destroy(), alongside disconnecting _observer,
clearing _toastTimer and removing the toast/panel).
- Around line 277-290: In _exportTheme(), replace the current naive clipboard
flow: await navigator.clipboard.writeText(css) inside a try/catch to handle and
log/display errors from the async clipboard API (use .then/.catch or
async/await), and remove reliance on the deprecated document.execCommand; keep
the textarea fallback only as a last-resort copy attempt but wrap it in
try/catch and surface failures via this._showToast or console.error so users are
notified if copy fails; reference navigator.clipboard.writeText and the
_exportTheme function to locate where to add the async error handling and
fallback error reporting.
- Around line 292-317: _reset() currently calls _setupMutationObserver() without
disconnecting any existing observer, causing duplicate MutationObservers; before
calling _setupMutationObserver() (or inside it) check for an existing observer
(e.g., this._mutationObserver) and if present call its disconnect() and clear
the reference (set to null) so only one observer exists, then proceed to create
and assign the new MutationObserver in _setupMutationObserver().
- Around line 68-106: The constructor of SocialShareDesigner lacks inline
comments for its initialization logic; add brief comments inside the
SocialShareDesigner constructor describing purpose and behavior of key
initializations: explain how _panelContainer is resolved, the two target modes
(_targetSelector vs _instance), what each field in _state represents (theme,
buttonStyle, platforms, gradient settings, platformColors), the role of
_modifiedVars and _observer, and the early-return when _panelContainer is
missing, and note that _render() and _setupMutationObserver() are called to
finish initialization; place these comments adjacent to the relevant symbols
(_panelContainer, _targetSelector, _instance, _state, _modifiedVars, _observer,
_render, _setupMutationObserver) and keep them short and focused on intent and
edge-case behavior.
🪄 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: ASSERTIVE

Plan: Pro

Run ID: 0b1d6922-f98a-4779-a2cd-f5cc0f20d708

📥 Commits

Reviewing files that changed from the base of the PR and between 9e7e113 and f23e730.

📒 Files selected for processing (4)
  • designer.html
  • src/social-share-designer-react.jsx
  • src/social-share-designer.css
  • src/social-share-designer.js

</div>
<div class="designer-nav-right">
<a class="nav-btn" href="index.html">← Back to Demo</a>
<button class="nav-btn primary" id="nav-export-btn">Export Theme</button>
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 | 🟡 Minor

Add type="button" to button elements.

Buttons without an explicit type attribute default to type="submit", which can cause unintended form submission behavior. This was also flagged by HTMLHint.

Proposed fix
-      <button class="nav-btn primary" id="nav-export-btn">Export Theme</button>
+      <button type="button" class="nav-btn primary" id="nav-export-btn">Export Theme</button>
-                <button class="social-share-modal-close" aria-label="Close" style="pointer-events:none">✕</button>
+                <button type="button" class="social-share-modal-close" aria-label="Close" style="pointer-events:none">✕</button>
-                <button class="social-share-copy-btn" style="pointer-events:none">Copy</button>
+                <button type="button" class="social-share-copy-btn" style="pointer-events:none">Copy</button>

Also applies to: 304-304, 315-315

🧰 Tools
🪛 HTMLHint (1.9.2)

[warning] 277-277: The type attribute must be present on elements.

(button-type-require)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@designer.html` at line 277, Buttons without an explicit type default to
"submit" and can accidentally submit forms; update each button element to
include type="button" (for example the button with id "nav-export-btn" and the
other nav buttons referenced near lines 304 and 315) so they no longer act as
submit controls—locate the <button> tags (e.g., id="nav-export-btn") and add the
attribute type="button" to each.

Comment on lines +379 to +381
document.getElementById('nav-export-btn').addEventListener('click', () => {
document.getElementById('ssb-btn-export').click();
});
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 | 🟡 Minor

Nav export button relies on dynamically created element.

This click handler delegates to #ssb-btn-export which is created by the SocialShareDesigner. If the designer fails to initialize, this will throw an error.

Proposed fix: add null check
     /* ── Nav export button wires to designer ────────────────── */
     document.getElementById('nav-export-btn').addEventListener('click', () => {
-      document.getElementById('ssb-btn-export').click();
+      const exportBtn = document.getElementById('ssb-btn-export');
+      if (exportBtn) exportBtn.click();
     });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
document.getElementById('nav-export-btn').addEventListener('click', () => {
document.getElementById('ssb-btn-export').click();
});
document.getElementById('nav-export-btn').addEventListener('click', () => {
const exportBtn = document.getElementById('ssb-btn-export');
if (exportBtn) exportBtn.click();
});
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@designer.html` around lines 379 - 381, The nav export click handler assumes
the dynamically created element '#ssb-btn-export' exists (created by
SocialShareDesigner) and will throw if the designer fails to initialize; update
the handler attached to document.getElementById('nav-export-btn') to first check
that document.getElementById('ssb-btn-export') is non-null (or use optional
chaining) before calling .click(), and/or guard the listener attachment itself
by ensuring the nav button exists; reference the '#nav-export-btn' click handler
and the target '#ssb-btn-export' (and the SocialShareDesigner initialization)
when applying this null-check guard so the handler no-ops safely if the export
button isn't present.

@@ -0,0 +1,71 @@
import { useEffect, useRef } from "react";
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 | 🟡 Minor

Missing "use client" directive for Next.js compatibility.

Per coding guidelines, React components in this codebase must include the "use client" directive at the top of the file for Next.js compatibility.

Proposed fix
+"use client";
+
 import { useEffect, useRef } from "react";
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import { useEffect, useRef } from "react";
"use client";
import { useEffect, useRef } from "react";
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/social-share-designer-react.jsx` at line 1, Add the Next.js client
directive by inserting the exact string "use client" as the very first line of
the module (above the import of useEffect/useRef) so this React component file
runs on the client; ensure it appears before any imports and top-level code in
src/social-share-designer-react.jsx to comply with Next.js and the project's
React component guidelines.

Comment on lines +16 to +69
export const SocialShareDesigner = ({
/** Pass the SocialShareButton JS instance for full control,
* or a CSS selector string for CSS-vars-only mode. */
targetButton = null,
}) => {
const panelRef = useRef(null);
const designerRef = useRef(null);

useEffect(() => {
if (!panelRef.current || designerRef.current) return;

if (typeof window === "undefined" || !window.SocialShareDesigner) {
console.warn(
"[SocialShareDesigner] window.SocialShareDesigner not found. " +
"Make sure social-share-designer.js is loaded before this component mounts."
);
return;
}

designerRef.current = new window.SocialShareDesigner({
panelContainer: panelRef.current,
targetButton,
});

return () => {
if (designerRef.current) {
designerRef.current.destroy();
designerRef.current = null;
}
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

// If targetButton changes (e.g. the SocialShareButton instance is replaced),
// tear down and rebuild the designer so it points at the new instance.
useEffect(() => {
if (!designerRef.current) return;

designerRef.current.destroy();
designerRef.current = null;

if (!panelRef.current) return;

if (typeof window !== "undefined" && window.SocialShareDesigner) {
designerRef.current = new window.SocialShareDesigner({
panelContainer: panelRef.current,
targetButton,
});
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [targetButton]);

return <div ref={panelRef} className="ssb-designer-root" />;
};
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.

🧹 Nitpick | 🔵 Trivial

Add brief inline comments for non-obvious logic.

Per coding guidelines for src/**/*.{js,jsx}, newly added functions should have inline comments explaining non-obvious logic, edge cases, and safety checks.

Suggested inline comments
 export const SocialShareDesigner = ({
   /** Pass the SocialShareButton JS instance for full control,
    *  or a CSS selector string for CSS-vars-only mode. */
   targetButton = null,
 }) => {
-  const panelRef    = useRef(null);
-  const designerRef = useRef(null);
+  const panelRef    = useRef(null);   // DOM container for designer panel
+  const designerRef = useRef(null);   // SocialShareDesigner instance

+  // Initialize designer once on mount
   useEffect(() => {
+    // Prevent re-init if already exists or container not ready
     if (!panelRef.current || designerRef.current) return;

+    // SSR guard: skip if window unavailable
     if (typeof window === "undefined" || !window.SocialShareDesigner) {

Based on learnings: "For the AOSSIE-Org/SocialShareButton repository, flag any newly added or modified function that lacks a descriptive comment explaining what it does."

🧰 Tools
🪛 GitHub Check: lint

[failure] 65-65:
Definition for rule 'react-hooks/exhaustive-deps' was not found


[failure] 46-46:
Definition for rule 'react-hooks/exhaustive-deps' was not found


[failure] 28-28:
Unexpected console statement

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/social-share-designer-react.jsx` around lines 16 - 69, The
SocialShareDesigner React component and its two useEffect blocks lack inline
comments describing non-obvious behavior and safety checks; add brief comments
explaining the component purpose and the logic in the first useEffect
(lazy-init: checking panelRef, designerRef, and window.SocialShareDesigner,
warning when the global isn't present, creating designerRef.current, and the
cleanup that destroys the instance) and in the second useEffect
(rebuild-on-targetButton change: destroy existing designer, guard on panelRef,
re-create if global is available). Reference the component name
SocialShareDesigner, refs panelRef and designerRef, the prop targetButton, and
the global window.SocialShareDesigner in your comments so future readers
understand the edge cases and lifecycle reasoning.

Comment on lines +24 to +47
useEffect(() => {
if (!panelRef.current || designerRef.current) return;

if (typeof window === "undefined" || !window.SocialShareDesigner) {
console.warn(
"[SocialShareDesigner] window.SocialShareDesigner not found. " +
"Make sure social-share-designer.js is loaded before this component mounts."
);
return;
}

designerRef.current = new window.SocialShareDesigner({
panelContainer: panelRef.current,
targetButton,
});

return () => {
if (designerRef.current) {
designerRef.current.destroy();
designerRef.current = null;
}
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
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

Double initialization occurs on mount due to both useEffect hooks running.

On initial mount, both effects run: the first initializes the designer, then the second (with [targetButton] dependency) immediately destroys and recreates it since designerRef.current is now truthy. This wastes resources and could cause visual flicker.

Additionally, per context snippet 2 (src/social-share-designer.js:939-949), destroy() does not call _resetAllVars(), so CSS variables set on document.documentElement persist across destroy/reinitialize cycles, potentially causing styling conflicts.

Proposed fix: skip second effect on initial mount
+  // Track if this is the initial mount
+  const isInitialMount = useRef(true);
+
   useEffect(() => {
     if (!panelRef.current || designerRef.current) return;

     if (typeof window === "undefined" || !window.SocialShareDesigner) {
       console.warn(
         "[SocialShareDesigner] window.SocialShareDesigner not found. " +
           "Make sure social-share-designer.js is loaded before this component mounts."
       );
       return;
     }

     designerRef.current = new window.SocialShareDesigner({
       panelContainer: panelRef.current,
       targetButton,
     });

     return () => {
       if (designerRef.current) {
         designerRef.current.destroy();
         designerRef.current = null;
       }
     };
     // eslint-disable-next-line react-hooks/exhaustive-deps
   }, []);

-  // If targetButton changes (e.g. the SocialShareButton instance is replaced),
-  // tear down and rebuild the designer so it points at the new instance.
+  // Rebuild designer when targetButton changes (skip initial mount)
   useEffect(() => {
+    if (isInitialMount.current) {
+      isInitialMount.current = false;
+      return;
+    }
     if (!designerRef.current) return;

     designerRef.current.destroy();
     designerRef.current = null;

Also applies to: 51-66

🧰 Tools
🪛 GitHub Check: lint

[failure] 46-46:
Definition for rule 'react-hooks/exhaustive-deps' was not found


[failure] 28-28:
Unexpected console statement

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/social-share-designer-react.jsx` around lines 24 - 47, The bug is
double-initializing SocialShareDesigner on mount and leaving CSS vars set after
destroy; fix by (1) in src/social-share-designer-react.jsx add an initialMount
guard (e.g., a ref like initialMountRef) so the useEffect that depends on
targetButton skips running on the initial mount (only runs on subsequent
targetButton changes) and only recreates the designer when necessary,
referencing designerRef and the effect with [targetButton]; and (2) in
src/social-share-designer.js ensure the destroy() method calls the internal
_resetAllVars() (or inline the logic that clears document.documentElement CSS
variables) so CSS variables are cleared when designer.destroy() is invoked,
preventing persistent styling between destroy/reinitialize cycles.

Comment on lines +25 to +44
const DEFAULTS = {
'--ssb-btn-bg': '',
'--ssb-btn-radius': '8px',
'--ssb-btn-font-size': '14px',
'--ssb-btn-font-weight': '500',
'--ssb-btn-border-width': '1px',
'--ssb-btn-border-color': 'rgba(255,255,255,0.2)',
'--ssb-btn-gradient-start': '',
'--ssb-btn-gradient-end': '',
'--ssb-modal-bg': '#282828',
'--ssb-modal-width': '540px',
'--ssb-modal-radius': '12px',
'--ssb-modal-speed': '0.3s',
'--ssb-icon-size': '56px',
'--ssb-icon-shape': '50%',
'--ssb-accent': '#3ea6ff',
'--ssb-hover-scale': '1.05',
'--ssb-font-family': '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
'--ssb-shadow-intensity': '0',
};
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.

🧹 Nitpick | 🔵 Trivial

Remove unused DEFAULTS constant.

The DEFAULTS object is defined but never referenced anywhere in the code. This appears to be dead code that should be removed or utilized.

#!/bin/bash
# Verify DEFAULTS is not used anywhere
rg -n "DEFAULTS" src/social-share-designer.js
🧰 Tools
🪛 GitHub Check: lint

[warning] 25-25:
'DEFAULTS' is assigned a value but never used

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/social-share-designer.js` around lines 25 - 44, Remove the unused
DEFAULTS constant definition from src/social-share-designer.js (the object
labeled DEFAULTS with CSS variable keys) since it is dead code; delete the
entire const DEFAULTS = { ... }; block and any trailing comma or blank line left
behind, then run the repo search (e.g., ripgrep) and tests to confirm no
remaining references and no lint issues; if the values were intended to be used,
instead refactor code paths that build styles (e.g., functions that reference
CSS variables) to import or consume DEFAULTS rather than deleting it.

Comment on lines +68 to +106
class SocialShareDesigner {
constructor(options = {}) {
this._panelContainer =
typeof options.panelContainer === 'string'
? document.querySelector(options.panelContainer)
: options.panelContainer;

/* targetButton: string selector → CSS-vars only
instance → full control */
this._targetSelector =
typeof options.targetButton === 'string' ? options.targetButton : null;
this._instance =
typeof options.targetButton === 'object' && options.targetButton !== null
? options.targetButton
: null;

/* Internal state */
this._state = {
theme: 'dark',
buttonStyle: 'default',
platforms: [...ALL_PLATFORMS],
buttonColor: '',
gradientEnabled: false,
gradientStart: '#667eea',
gradientEnd: '#764ba2',
platformColors: {}, // { whatsapp: '#ff0000', ... }
};

this._modifiedVars = {}; // tracks which CSS vars were changed
this._observer = null;

if (!this._panelContainer) {
console.warn('[SocialShareDesigner] panelContainer not found.');
return;
}

this._render();
this._setupMutationObserver();
}
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.

🧹 Nitpick | 🔵 Trivial

Add inline comments for constructor and key initialization logic.

Per coding guidelines for src/**/*.{js,jsx}, newly added methods should have brief inline comments explaining what they do, especially for non-obvious logic and edge cases.

Suggested inline comments
   class SocialShareDesigner {
+    // Initializes the designer panel with options for container and target button
     constructor(options = {}) {
+      // Resolve panelContainer: accept selector string or DOM element
       this._panelContainer =
         typeof options.panelContainer === 'string'
           ? document.querySelector(options.panelContainer)
           : options.panelContainer;

-      /* targetButton: string selector → CSS-vars only
-                       instance       → full control  */
+      // targetButton modes:
+      // - string selector: CSS-vars-only mode (no direct button manipulation)
+      // - instance object: full control mode (can call instance methods)
       this._targetSelector =
         typeof options.targetButton === 'string' ? options.targetButton : null;

Based on learnings: "Flag any newly added or modified function that lacks a descriptive comment explaining what it does."

🧰 Tools
🪛 GitHub Check: lint

[failure] 100-100:
Unexpected console statement

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/social-share-designer.js` around lines 68 - 106, The constructor of
SocialShareDesigner lacks inline comments for its initialization logic; add
brief comments inside the SocialShareDesigner constructor describing purpose and
behavior of key initializations: explain how _panelContainer is resolved, the
two target modes (_targetSelector vs _instance), what each field in _state
represents (theme, buttonStyle, platforms, gradient settings, platformColors),
the role of _modifiedVars and _observer, and the early-return when
_panelContainer is missing, and note that _render() and _setupMutationObserver()
are called to finish initialization; place these comments adjacent to the
relevant symbols (_panelContainer, _targetSelector, _instance, _state,
_modifiedVars, _observer, _render, _setupMutationObserver) and keep them short
and focused on intent and edge-case behavior.

Comment on lines +277 to +290
if (navigator.clipboard && navigator.clipboard.writeText) {
navigator.clipboard.writeText(css).then(() => this._showToast('Copied to clipboard!'));
} else {
const ta = document.createElement('textarea');
ta.value = css;
ta.style.position = 'fixed';
ta.style.opacity = '0';
document.body.appendChild(ta);
ta.select();
document.execCommand('copy');
document.body.removeChild(ta);
this._showToast('Copied to clipboard!');
}
}
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.

🧹 Nitpick | 🔵 Trivial

_exportTheme() fallback uses deprecated document.execCommand.

The clipboard fallback uses document.execCommand('copy') which is deprecated. While the modern navigator.clipboard.writeText is preferred, consider adding error handling for the async clipboard API and noting the deprecation.

Suggested improvement with error handling
       if (navigator.clipboard && navigator.clipboard.writeText) {
-        navigator.clipboard.writeText(css).then(() => this._showToast('Copied to clipboard!'));
+        navigator.clipboard.writeText(css)
+          .then(() => this._showToast('Copied to clipboard!'))
+          .catch(() => this._fallbackCopy(css));
       } else {
-        const ta = document.createElement('textarea');
-        ta.value = css;
-        ta.style.position = 'fixed';
-        ta.style.opacity = '0';
-        document.body.appendChild(ta);
-        ta.select();
-        document.execCommand('copy');
-        document.body.removeChild(ta);
-        this._showToast('Copied to clipboard!');
+        this._fallbackCopy(css);
       }
     }
+
+    // Fallback copy using deprecated execCommand (for older browsers)
+    _fallbackCopy(text) {
+      const ta = document.createElement('textarea');
+      ta.value = text;
+      ta.style.position = 'fixed';
+      ta.style.opacity = '0';
+      document.body.appendChild(ta);
+      ta.select();
+      document.execCommand('copy');
+      document.body.removeChild(ta);
+      this._showToast('Copied to clipboard!');
+    }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/social-share-designer.js` around lines 277 - 290, In _exportTheme(),
replace the current naive clipboard flow: await
navigator.clipboard.writeText(css) inside a try/catch to handle and log/display
errors from the async clipboard API (use .then/.catch or async/await), and
remove reliance on the deprecated document.execCommand; keep the textarea
fallback only as a last-resort copy attempt but wrap it in try/catch and surface
failures via this._showToast or console.error so users are notified if copy
fails; reference navigator.clipboard.writeText and the _exportTheme function to
locate where to add the async error handling and fallback error reporting.

Comment on lines +292 to +317
/* ── Reset ─────────────────────────────────────────────── */
_reset() {
this._resetAllVars();
this._state.platformColors = {};

/* Reset state */
this._state.theme = 'dark';
this._state.buttonStyle = 'default';
this._state.platforms = [...ALL_PLATFORMS];
this._state.buttonColor = '';
this._state.gradientEnabled = false;
this._state.gradientStart = '#667eea';
this._state.gradientEnd = '#764ba2';

/* Re-apply theme + platforms */
this._applyTheme('dark');
this._applyPlatforms([...ALL_PLATFORMS]);

/* Reset real modal to dark */
const overlay = this._getOverlayEl();
if (overlay) overlay.className = 'social-share-modal-overlay dark';

/* Re-render panel so controls reflect defaults */
this._render();
this._setupMutationObserver();
}
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

_reset() creates duplicate MutationObserver without disconnecting existing one.

When _reset() is called, it invokes _setupMutationObserver() at line 316, but doesn't disconnect the existing observer first. This creates duplicate observers that will both fire on mutations.

Proposed fix
     /* Re-render panel so controls reflect defaults */
     this._render();
+    // Disconnect existing observer before creating new one
+    if (this._observer) {
+      this._observer.disconnect();
+      this._observer = null;
+    }
     this._setupMutationObserver();
   }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
/* ── Reset ─────────────────────────────────────────────── */
_reset() {
this._resetAllVars();
this._state.platformColors = {};
/* Reset state */
this._state.theme = 'dark';
this._state.buttonStyle = 'default';
this._state.platforms = [...ALL_PLATFORMS];
this._state.buttonColor = '';
this._state.gradientEnabled = false;
this._state.gradientStart = '#667eea';
this._state.gradientEnd = '#764ba2';
/* Re-apply theme + platforms */
this._applyTheme('dark');
this._applyPlatforms([...ALL_PLATFORMS]);
/* Reset real modal to dark */
const overlay = this._getOverlayEl();
if (overlay) overlay.className = 'social-share-modal-overlay dark';
/* Re-render panel so controls reflect defaults */
this._render();
this._setupMutationObserver();
}
/* ── Reset ─────────────────────────────────────────────── */
_reset() {
this._resetAllVars();
this._state.platformColors = {};
/* Reset state */
this._state.theme = 'dark';
this._state.buttonStyle = 'default';
this._state.platforms = [...ALL_PLATFORMS];
this._state.buttonColor = '';
this._state.gradientEnabled = false;
this._state.gradientStart = '#667eea';
this._state.gradientEnd = '#764ba2';
/* Re-apply theme + platforms */
this._applyTheme('dark');
this._applyPlatforms([...ALL_PLATFORMS]);
/* Reset real modal to dark */
const overlay = this._getOverlayEl();
if (overlay) overlay.className = 'social-share-modal-overlay dark';
/* Re-render panel so controls reflect defaults */
this._render();
// Disconnect existing observer before creating new one
if (this._observer) {
this._observer.disconnect();
this._observer = null;
}
this._setupMutationObserver();
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/social-share-designer.js` around lines 292 - 317, _reset() currently
calls _setupMutationObserver() without disconnecting any existing observer,
causing duplicate MutationObservers; before calling _setupMutationObserver() (or
inside it) check for an existing observer (e.g., this._mutationObserver) and if
present call its disconnect() and clear the reference (set to null) so only one
observer exists, then proceed to create and assign the new MutationObserver in
_setupMutationObserver().

Comment on lines +939 to +949
/* ── Public: destroy ───────────────────────────────────── */
destroy() {
if (this._observer) {
this._observer.disconnect();
this._observer = null;
}
clearTimeout(this._toastTimer);
const toast = document.getElementById('ssb-designer-toast');
if (toast && toast.parentNode) toast.parentNode.removeChild(toast);
if (this._panelContainer) this._panelContainer.innerHTML = '';
}
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

destroy() should clean up global CSS variables.

The destroy() method does not call _resetAllVars(), leaving CSS variables on document.documentElement after destruction. This causes styling to persist incorrectly when the React wrapper reinitializes the designer (e.g., on targetButton changes) or when multiple designers are used.

Proposed fix
   /* ── Public: destroy ───────────────────────────────────── */
   destroy() {
+    // Clean up CSS variables set on document root
+    this._resetAllVars();
+
     if (this._observer) {
       this._observer.disconnect();
       this._observer = null;
     }
     clearTimeout(this._toastTimer);
     const toast = document.getElementById('ssb-designer-toast');
     if (toast && toast.parentNode) toast.parentNode.removeChild(toast);
     if (this._panelContainer) this._panelContainer.innerHTML = '';
   }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
/* ── Public: destroy ───────────────────────────────────── */
destroy() {
if (this._observer) {
this._observer.disconnect();
this._observer = null;
}
clearTimeout(this._toastTimer);
const toast = document.getElementById('ssb-designer-toast');
if (toast && toast.parentNode) toast.parentNode.removeChild(toast);
if (this._panelContainer) this._panelContainer.innerHTML = '';
}
/* ── Public: destroy ───────────────────────────────────── */
destroy() {
// Clean up CSS variables set on document root
this._resetAllVars();
if (this._observer) {
this._observer.disconnect();
this._observer = null;
}
clearTimeout(this._toastTimer);
const toast = document.getElementById('ssb-designer-toast');
if (toast && toast.parentNode) toast.parentNode.removeChild(toast);
if (this._panelContainer) this._panelContainer.innerHTML = '';
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/social-share-designer.js` around lines 939 - 949, The destroy() method
currently disconnects observers and removes DOM elements but doesn't clear
global CSS vars; call the existing _resetAllVars() from destroy() to remove CSS
variables on document.documentElement before finishing cleanup (i.e., add a call
to this._resetAllVars() inside destroy(), alongside disconnecting _observer,
clearing _toastTimer and removing the toast/panel).

@kpj2006
Copy link
Copy Markdown
Contributor Author

kpj2006 commented Mar 20, 2026

@coderabbitai apply the suggested fix and make a new pull request into this branch.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 20, 2026

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

Let me first look at the review comments on this PR to understand which suggested fix you're referring to.


🏁 Script executed:

#!/bin/bash
# Fetch all review comments on PR `#95`
gh pr view 95 --repo AOSSIE-Org/SocialShareButton --json reviews,comments

# Also fetch review comments (inline)
gh api repos/AOSSIE-Org/SocialShareButton/pulls/95/comments --jq '.[] | {id: .id, user: .user.login, body: .body, path: .path, line: .line}'

Length of output: 50434


@kpj2006, I've identified all 10 review comments from the PR. I cannot push code directly, but I'll plan all the changes and have another agent implement them and open a new PR into the frame branch.

Fixes for PR #95 review comments

Apply all actionable review comments across designer.html, src/social-share-designer-react.jsx, and src/social-share-designer.js, then open a PR targeting the frame branch.


🏁 Script executed:

grep -n "<button" designer.html

Length of output: 432


🏁 Script executed:

sed -n '375,385p' designer.html

Length of output: 270


🏁 Script executed:

head -5 src/social-share-designer-react.jsx

Length of output: 164


🏁 Script executed:

cat src/social-share-designer-react.jsx

Length of output: 2184


🏁 Script executed:

sed -n '20,70p' src/social-share-designer-react.jsx

Length of output: 1461


🏁 Script executed:

sed -n '20,50p' src/social-share-designer.js

Length of output: 1130


🏁 Script executed:

sed -n '65,110p' src/social-share-designer.js

Length of output: 1613


🏁 Script executed:

sed -n '270,300p' src/social-share-designer.js

Length of output: 1066


🏁 Script executed:

sed -n '288,320p' src/social-share-designer.js

Length of output: 1047


🏁 Script executed:

sed -n '935,955p' src/social-share-designer.js

Length of output: 701


🏁 Script executed:

gh pr create \
  --repo AOSSIE-Org/SocialShareButton \
  --base frame \
  --title "fix: apply review fixes for SocialShareDesigner (PR `#95`)" \
  --body "Applies all actionable review fixes from `#95`:\n\n- Add \`type=\"button\"\` to button elements in designer.html\n- Add null-check guard for \`#ssb-btn-export\` in nav export handler\n- Add \`\"use client\"\` directive to social-share-designer-react.jsx\n- Add inline comments to React component useEffect blocks\n- Fix double-initialization with \`isInitialMount\` ref guard\n- Remove unused \`DEFAULTS\` constant from social-share-designer.js\n- Add inline comments to SocialShareDesigner constructor\n- Refactor \`_exportTheme()\` with async error handling and \`_fallbackCopy()\`\n- Fix \`_reset()\` to disconnect existing MutationObserver before creating a new one\n- Fix \`destroy()\` to call \`_resetAllVars()\` for CSS variable cleanup\n\nAddresses review comments in: https://github.com/AOSSIE-Org/SocialShareButton/pull/95\n\ncc `@kpj2006`"

Length of output: 112


@kpj2006, I've gathered all the file contents and confirmed the exact changes needed. Here are all 10 fixes — another agent will implement them and open a PR into your frame branch.

Apply all 10 review fixes from PR #95 and open a PR into frame

  • Fix designer.html: type="button" on 3 buttons, null-check on export handler
  • Fix src/social-share-designer-react.jsx: "use client", inline comments, isInitialMount guard
  • Fix src/social-share-designer.js: remove DEFAULTS, constructor comments, _exportTheme error handling, _reset observer guard, destroy CSS cleanup

🧠 Learnings used
Learnt from: kpj2006
Repo: AOSSIE-Org/SocialShareButton PR: 0
File: :0-0
Timestamp: 2026-03-10T07:25:17.136Z
Learning: For the AOSSIE-Org/SocialShareButton repository, kpj2006 wants contributors to add proper inline comments to all methods in library source files (especially src/social-share-button.js). The preferred enforcement mechanism is CodeRabbit path-based review instructions (skills) in .coderabbit.yaml, rather than scripts or workflows. During reviews, flag any newly added or modified function that lacks a descriptive comment explaining what it does, why it exists, and its parameters/return values.

Learnt from: kpj2006
Repo: AOSSIE-Org/SocialShareButton PR: 0
File: :0-0
Timestamp: 2026-03-10T07:25:17.136Z
Learning: For the AOSSIE-Org/SocialShareButton repository, kpj2006 wants contributors to add proper inline comments to all methods in library source files (especially src/social-share-button.js). The preferred enforcement mechanism is CodeRabbit path-based review instructions (skills) in .coderabbit.yaml rather than scripts or workflows. Flag any newly added or modified function that lacks a descriptive comment during reviews.

Learnt from: kpj2006
Repo: AOSSIE-Org/SocialShareButton PR: 0
File: :0-0
Timestamp: 2026-03-17T17:12:57.712Z
Learning: In AOSSIE-Org/SocialShareButton, new wrapper files (e.g., src/social-share-button-preact.jsx) added in a PR are not part of any CDN release until the next version tag is cut. When reviewing PRs that add new wrapper/source files, flag any README or index.html CDN URLs that reference a version predating the new file, and check that acquisition instructions (CDN URL, npm, or download link) are provided for the new file itself.

Failed to handle agent chat message. Please try again.

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.

1 participant