Content deleted Content added
Polygnotus (talk | contribs) No edit summary |
Polygnotus (talk | contribs) v5 but with OOUI buttons |
||
Line 7:
this.isVisible = localStorage.getItem('claude_sidebar_visible') !== 'false';
this.currentResults = localStorage.getItem('claude_current_results') || '';
this.buttons = {};
this.init();
}
init() {
this.
this.
this.
this.adjustMainContent();
});
}
async loadOOUI() {
// Ensure OOUI is loaded
await mw.loader.using(['oojs-ui-core', 'oojs-ui-widgets', 'oojs-ui-windows']);
}
createUI() {
// Create sidebar container
const sidebar = document.createElement('div');
sidebar.id = 'claude-proofreader-sidebar';
// Create OOUI buttons
this.createOOUIButtons();
sidebar.innerHTML = `
<div id="claude-sidebar-header">
<h3>Claude Proofreader</h3>
<div id="claude-sidebar-controls">
<
</div>
</div>
<div id="claude-sidebar-content">
<div id="claude-controls">
<
</div>
<div id="claude-results">
Line 39 ⟶ 49:
<div id="claude-resize-handle"></div>
`;
// Create Claude tab for when sidebar is closed
this.createClaudeTab();
// Add CSS styles
const style = document.createElement('style');
Line 76 ⟶ 88:
display: flex;
gap: 8px;
}
#claude-sidebar-content {
Line 105 ⟶ 100:
flex-shrink: 0;
}
#claude-
}
#claude-
}
#claude-
}
#claude-results {
Line 196 ⟶ 179:
}
body {
margin-right: ${this.isVisible ? this.sidebarWidth : '0'}
transition: margin-right 0.3s ease;
}
Line 224 ⟶ 189:
border-radius: 4px;
}
.claude-sidebar-hidden body
margin-right: 0 !important;
}
Line 245 ⟶ 200:
`;
document.head.appendChild(style);
document.body.
// Append OOUI buttons to their containers
this.appendOOUIButtons();
// Set initial state
Line 251 ⟶ 209:
this.hideSidebar();
}
// Make sidebar resizable
this.makeResizable();
}
createOOUIButtons() {
// Close button (icon button)
this.buttons.close = new OO.ui.ButtonWidget({
icon: 'close',
title: 'Close',
framed: false,
classes: ['claude-close-button']
});
// Set API Key button
this.buttons.setKey = new OO.ui.ButtonWidget({
label: 'Set API Key',
flags: ['primary', 'progressive'],
disabled: false
});
// Proofread button
this.buttons.proofread = new OO.ui.ButtonWidget({
label: 'Proofread Article',
flags: ['primary', 'progressive'],
icon: 'check',
disabled: !this.apiKey
});
// Change key button
this.buttons.changeKey = new OO.ui.ButtonWidget({
label: 'Change Key',
flags: ['safe'],
icon: 'edit',
disabled: false
});
// Remove key button
this.buttons.removeKey = new OO.ui.ButtonWidget({
label: 'Remove API Key',
flags: ['destructive'],
icon: 'trash',
disabled: false
});
// Set initial visibility
this.updateButtonVisibility();
}
appendOOUIButtons() {
// Append close button
document.getElementById('claude-close-btn-container').appendChild(this.buttons.close.$element[0]);
// Append main buttons
const container = document.getElementById('claude-buttons-container');
if (this.apiKey) {
container.appendChild(this.buttons.proofread.$element[0]);
container.appendChild(this.buttons.changeKey.$element[0]);
container.appendChild(this.buttons.removeKey.$element[0]);
} else {
container.appendChild(this.buttons.setKey.$element[0]);
}
}
updateButtonVisibility() {
const container = document.getElementById('claude-buttons-container');
if (!container) return;
// Clear container
container.innerHTML = '';
// Add appropriate buttons based on API key state
if (this.apiKey) {
container.appendChild(this.buttons.proofread.$element[0]);
container.appendChild(this.buttons.changeKey.$element[0]);
container.appendChild(this.buttons.removeKey.$element[0]);
} else {
container.appendChild(this.buttons.setKey.$element[0]);
}
}
createClaudeTab() {
// Only create tab if we're in the main article namespace
if (typeof mw !== 'undefined' && mw.config.get('wgNamespaceNumber') === 0) {
let portletId = 'p-namespaces';
if (
portletId = 'p-associated-pages';
}
const claudeLink = mw.util.addPortletLink(
portletId,
'#',
Line 275 ⟶ 311:
}
}
makeResizable() {
const handle = document.getElementById('claude-resize-handle');
Line 288 ⟶ 325:
e.preventDefault();
});
const handleMouseMove = (e) => {
if (!isResizing) return;
Line 298 ⟶ 336:
const widthPx = newWidth + 'px';
sidebar.style.width = widthPx;
if (mw.config.get('skin') === 'vector') {
const head = document.querySelector('#mw-head');
head.style.width = `calc(100% - ${widthPx})`;
head.style.right = widthPx;
}
this.sidebarWidth = widthPx;
localStorage.setItem('claude_sidebar_width', widthPx);
}
};
const handleMouseUp = () => {
isResizing = false;
Line 309 ⟶ 353:
};
}
showSidebar() {
const claudeTab = document.getElementById('ca-claude');
Line 314 ⟶ 359:
document.body.classList.remove('claude-sidebar-hidden');
if (claudeTab) claudeTab.style.display = 'none';
if (mw.config.get('skin') === 'vector') {
const head = document.querySelector('#mw-head');
head.style.width = `calc(100% - ${this.sidebarWidth})`;
head.style.right = this.sidebarWidth;
}
this.isVisible = true;
localStorage.setItem('claude_sidebar_visible', 'true');
}
hideSidebar() {
const claudeTab = document.getElementById('ca-claude');
Line 326 ⟶ 377:
document.body.classList.add('claude-sidebar-hidden');
if (claudeTab) claudeTab.style.display = 'list-item';
document.body.style.marginRight = '0';
const head = document.querySelector('#mw-head');
head.style.width = '100%';
head.style.right = '0';
}
this.isVisible = false;
localStorage.setItem('claude_sidebar_visible', 'false');
}
adjustMainContent() {
if (this.isVisible) {
} else {
}
}
attachEventListeners() {
this.hideSidebar();
});
this.buttons.setKey.on('click', () => {
this.setApiKey();
});
this.buttons.changeKey.on('click', () => {
this.setApiKey();
});
this.buttons.proofread.on('click', () => {
this.proofreadArticle();
});
this.buttons.removeKey.on('click', () => {
this.removeApiKey();
});
}
setApiKey() {
dialog.title = 'Set Claude
const textInput = new OO.ui.TextInputWidget({
});
dialog.initialize = function()
padded: true,
expanded: false
});
this.content.$element.append(
$('<p>').text('Enter your Claude API Key to enable proofreading:'),
textInput.$element
);
this.$body.append(this.content.$element);
};
dialog.getActionProcess = (action) => {
if (action === 'save') {
return new OO.ui.Process(() => {
const key = textInput.getValue().trim();
if (key) {
this.apiKey = key;
localStorage.setItem('claude_api_key', this.apiKey);
this.updateButtonVisibility();
this.updateStatus('API key set successfully!');
windowManager.closeWindow(dialog);
} else {
textInput.setValidityFlag(false);
return OO.ui.Process.static.createRejectProcess('Please enter a valid API key');
}
});
}
return OO.ui.ProcessDialog.prototype.getActionProcess.call(this, action);
};
dialog.getSetupProcess = function(data) {
return OO.ui.ProcessDialog.prototype.getSetupProcess.call(this, data)
.next(() => {
this.actions.setMode('save');
});
};
const windowManager = new OO.ui.WindowManager();
$('body').append(windowManager.$element);
windowManager.addWindows([dialog]);
dialog.getActions().add([
{
action: 'save',
label: 'Save',
flags: ['primary', 'progressive']
},
{
label: 'Cancel',
flags: ['safe']
}
]);
windowManager.openWindow(dialog);
}
removeApiKey() {
// Create OOUI confirmation dialog
OO.ui.confirm('Are you sure you want to remove the stored API key?').done((confirmed) => {
this.apiKey = null;
this.updateButtonVisibility();
}
}
updateStatus(message, isError = false) {
const statusEl = document.getElementById('claude-status');
Line 414 ⟶ 506:
statusEl.className = isError ? 'claude-error' : '';
}
updateOutput(content, isMarkdown = false) {
const outputEl = document.getElementById('claude-output');
Line 423 ⟶ 516:
outputEl.textContent = content;
}
// Store results
if (content) {
Line 429 ⟶ 523:
}
}
markdownToHtml(markdown) {
return markdown
Line 454 ⟶ 549:
.replace(/(<\/[hul]>)<\/p>/g, '$1');
}
async proofreadArticle() {
if (!this.apiKey) {
Line 459 ⟶ 555:
return;
}
try {
this.updateStatus('Fetching article content...', false);
// Get current article title
const articleTitle = this.getArticleTitle();
Line 468 ⟶ 565:
throw new Error('Could not extract article title from current page');
}
// Fetch wikicode
const wikicode = await this.fetchWikicode(articleTitle);
Line 473 ⟶ 571:
throw new Error('Could not fetch article wikicode');
}
// Check length and warn user
if (wikicode.length > 100000) {
OO.ui.confirm(`This article is quite long (${wikicode.length} characters). Processing may take a while and use significant API credits. Continue?`) .done(resolve);
});
if (!confirmed) {
this.updateStatus('Operation cancelled by user.');
return;
}
}
this.updateStatus('Processing with Claude... Please wait...');
// Call Claude API
const result = await this.callClaudeAPI(wikicode);
Line 487 ⟶ 593:
this.updateStatus('Proofreading complete!');
this.updateOutput(result, true);
} catch (error) {
console.error('Proofreading error:', error);
Line 492 ⟶ 599:
this.updateOutput('');
} finally {
}
}
getArticleTitle() {
// Extract title from URL
Line 511 ⟶ 619:
return null;
}
async fetchWikicode(articleTitle) {
// Get language from current URL
Line 518 ⟶ 627:
`action=query&titles=${encodeURIComponent(articleTitle)}&` +
`prop=revisions&rvprop=content&format=json&formatversion=2&origin=*`;
try {
const response = await fetch(apiUrl);
Line 523 ⟶ 633:
throw new Error(`Wikipedia API request failed: ${response.status}`);
}
const data = await response.json();
Line 528 ⟶ 639:
throw new Error('No pages found in API response');
}
const page = data.query.pages[0];
if (page.missing) {
throw new Error('Wikipedia page not found');
}
if (!page.revisions || page.revisions.length === 0) {
throw new Error('No revisions found');
}
const content = page.revisions[0].content;
if (!content || content.length < 50) {
throw new Error('Retrieved content is too short');
}
return content;
} catch (error) {
console.error('Error fetching wikicode:', error);
Line 545 ⟶ 661:
}
}
async callClaudeAPI(wikicode) {
const requestBody = {
Line 554 ⟶ 671:
}]
};
try {
const response = await fetch('https://api.anthropic.com/v1/messages', {
Line 565 ⟶ 683:
body: JSON.stringify(requestBody)
});
if (!response.ok) {
const errorText = await response.text();
throw new Error(`API request failed (${response.status}): ${errorText}`);
}
const data = await response.json();
Line 574 ⟶ 694:
throw new Error('Invalid API response format');
}
return data.content[0].text;
} catch (error) {
console.error('Claude API error:', error);
Line 581 ⟶ 703:
}
}
mw.loader.using(['mediawiki.util', 'oojs-ui-core', 'oojs-ui-widgets', 'oojs-ui-windows']).then(function() {
// Initialize the proofreader when page loads
if (document.readyState === 'loading') {
Line 590 ⟶ 713:
new WikipediaClaudeProofreader();
}
}
})();
|