User:Polygnotus/Scripts/ReplyButtonsv4.js

(function() {

if (mw.config.get('wgDiscussionToolsFeaturesEnabled')) {

mw.loader.using(['ext.discussionTools.ReplyWidget', 'mediawiki.api'], () => {

// Improved configuration loading with caching

async function loadToolsConfig() {

const username = mw.config.get('wgUserName');

if (!username) return [];

const api = new mw.Api();

try {

const result = await api.get({

action: 'query',

prop: 'revisions',

titles: `User:${username}/ReplyButtonsJSON`,

rvslots: '*',

rvprop: 'content',

formatversion: '2',

uselang: 'content', // Enable caching

smaxage: '86400', // Cache for 1 day

maxage: '86400' // Cache for 1 day

});

if (result.query.pages[0].missing) {

return [];

}

const content = result.query.pages[0].revisions[0].slots.main.content;

return JSON.parse(content);

} catch (error) {

console.error('Error loading reply buttons configuration:', error);

return [];

}

}

// Helper function to create tools from config

function createToolFromConfig(config) {

// Add error handling for icon URLs

if (config.icon?.startsWith('http')) {

try {

addCustomIconCSS(config.name, config.icon);

} catch (error) {

console.warn(`Failed to add custom icon for ${config.name}:`, error);

config.icon = 'help'; // Fallback icon

}

}

// Create command with improved error handling

const CommandClass = function() {

ve.ui.Command.call(this, config.name);

};

OO.inheritClass(CommandClass, ve.ui.Command);

CommandClass.prototype.execute = function() {

try {

const target = ve.init.target;

if (!target) {

console.warn('Visual editor target not found');

return false;

}

const surface = target.getSurface();

const surfaceModel = surface.getModel();

const content = typeof config.insertContent === 'function'

? config.insertContent()

: config.insertText.replace(/'\s*\+\s*'/g, '');

surfaceModel.getFragment()

.collapseToEnd()

.insertContent(content)

.collapseToEnd()

.select();

return true;

} catch (error) {

console.error(`Error executing command ${config.name}:`, error);

return false;

}

};

ve.ui.commandRegistry.register(new CommandClass());

// Create tool with improved validation

const ToolClass = function() {

ve.ui.Tool.apply(this, arguments);

};

OO.inheritClass(ToolClass, ve.ui.Tool);

ToolClass.static.name = config.name;

ToolClass.static.title = config.title || config.name;

ToolClass.static.commandName = config.name;

ToolClass.static.icon = config.icon?.startsWith('http')

? 'custom-' + config.name

: (config.icon || 'help');

ve.ui.toolFactory.register(ToolClass);

// Add to toolbar groups with improved module detection

const replyWidget = mw.loader.moduleRegistry['ext.discussionTools.ReplyWidget']

?.packageExports?.['dt-ve/CommentTarget.js'];

const newTopicWidget = mw.loader.moduleRegistry['ext.discussionTools.NewTopic']

?.packageExports?.['dt-ve/NewTopicTarget.js'];

if (replyWidget?.static?.toolbarGroups) {

const toolbarGroup = replyWidget.static.toolbarGroups[3];

if (toolbarGroup && Array.isArray(toolbarGroup.include)) {

toolbarGroup.include.push(config.name);

}

}

if (newTopicWidget?.static?.toolbarGroups) {

let customGroup = newTopicWidget.static.toolbarGroups.find(g => g.name === 'custom');

if (!customGroup) {

customGroup = {

name: 'custom',

include: [],

demote: ['custom']

};

newTopicWidget.static.toolbarGroups.push(customGroup);

}

if (Array.isArray(customGroup.include)) {

customGroup.include.push(config.name);

}

}

}

// Function to add custom CSS with error handling

function addCustomIconCSS(name, iconUrl) {

const styleId = `custom-icon-${name}`;

if (!document.getElementById(styleId)) {

const style = document.createElement('style');

style.id = styleId;

style.textContent = `

.oo-ui-icon-custom-${name} {

background-image: url(${iconUrl}) !important;

background-size: contain !important;

background-position: center !important;

background-repeat: no-repeat !important;

}

`;

document.head.appendChild(style);

}

}

// Initialize tools with improved error handling

loadToolsConfig().then(tools => {

if (Array.isArray(tools) && tools.length > 0) {

tools.forEach(config => {

try {

createToolFromConfig(config);

} catch (error) {

console.error(`Failed to create tool from config:`, config, error);

}

});

}

}).catch(error => {

console.error('Failed to load tools configuration:', error);

});

});

}

})();