feat: add SocialShareDesigner for customizable social share buttons#95
feat: add SocialShareDesigner for customizable social share buttons#95kpj2006 wants to merge 1 commit intoAOSSIE-Org:mainfrom
Conversation
- 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.
WalkthroughA 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
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
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 2✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
📝 Coding Plan
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. Comment |
There was a problem hiding this comment.
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
📒 Files selected for processing (4)
designer.htmlsrc/social-share-designer-react.jsxsrc/social-share-designer.csssrc/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> |
There was a problem hiding this comment.
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.
| document.getElementById('nav-export-btn').addEventListener('click', () => { | ||
| document.getElementById('ssb-btn-export').click(); | ||
| }); |
There was a problem hiding this comment.
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.
| 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"; | |||
There was a problem hiding this comment.
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.
| 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.
| 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" />; | ||
| }; |
There was a problem hiding this comment.
🧹 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.
| 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 | ||
| }, []); |
There was a problem hiding this comment.
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.
| 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', | ||
| }; |
There was a problem hiding this comment.
🧹 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.
| 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(); | ||
| } |
There was a problem hiding this comment.
🧹 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.
| 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!'); | ||
| } | ||
| } |
There was a problem hiding this comment.
🧹 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.
| /* ── 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(); | ||
| } |
There was a problem hiding this comment.
_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.
| /* ── 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().
| /* ── 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 = ''; | ||
| } |
There was a problem hiding this comment.
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.
| /* ── 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).
|
@coderabbitai apply the suggested fix and make a new pull request into this branch. |
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chainLet me first look at the review comments on this PR to understand which suggested fix you're referring to. 🏁 Script executed: Length of output: 50434
Fixes for PR
|
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:
I have used the following AI models and tools: TODO
claude
Checklist
Summary by CodeRabbit
New Features