Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 3 additions & 6 deletions demos/cdn/demo-config.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
{
"tags": [
"editing",
"viewing"
],
"title": "CDN"
}
"tags": ["editing", "viewing"],
"title": "CDN"
}
18 changes: 9 additions & 9 deletions demos/cdn/file-upload.css
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
.file-upload-button {
cursor: pointer;
padding: 8px 12px;
border-radius: 8px;
margin: 10px;
outline: none;
border: none;
background-color: #1355ff;
color: white;
}
cursor: pointer;
padding: 8px 12px;
border-radius: 8px;
margin: 10px;
outline: none;
border: none;
background-color: #1355ff;
color: white;
}
8 changes: 4 additions & 4 deletions demos/cdn/file-upload.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ const fileInput = document.querySelector('.file-upload-input');
uploadBtn.addEventListener('click', () => fileInput.click());

fileInput.addEventListener('change', (event) => {
const file = event.target.files?.[0];
const uploadEvent = new CustomEvent("file-upload", { detail: file });
if (file) window.dispatchEvent(uploadEvent);
});
const file = event.target.files?.[0];
const uploadEvent = new CustomEvent('file-upload', { detail: file });
if (file) window.dispatchEvent(uploadEvent);
});
86 changes: 43 additions & 43 deletions demos/chrome-extension/chrome-extension/background.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@ importScripts('dist/docx-validator.bundle.js');
function updateIcon(enabled) {
const suffix = enabled ? '' : '-disabled';
const iconPath = {
"16": `icons/icon-16x16${suffix}.png`,
"19": `icons/icon-19x19${suffix}.png`,
"48": `icons/icon-48x48${suffix}.png`,
"128": `icons/icon-128x128${suffix}.png`
16: `icons/icon-16x16${suffix}.png`,
19: `icons/icon-19x19${suffix}.png`,
48: `icons/icon-48x48${suffix}.png`,
128: `icons/icon-128x128${suffix}.png`,
};

chrome.action.setIcon({ path: iconPath });
}

Expand All @@ -26,9 +26,9 @@ chrome.storage.sync.get(['extensionEnabled'], (result) => {
// Create context menu on installation for selecting text
chrome.runtime.onInstalled.addListener(() => {
chrome.contextMenus.create({
id: "openSelectedInSuperdoc",
title: "Open selected content in SuperDoc",
contexts: ["selection"]
id: 'openSelectedInSuperdoc',
title: 'Open selected content in SuperDoc',
contexts: ['selection'],
});
});

Expand All @@ -55,13 +55,13 @@ async function handleDownloadFile(request, _sender, sendResponse) {
const downloadId = await chrome.downloads.download({
url: request.url,
filename: request.filename,
saveAs: true
saveAs: true,
});

// Track this download to ignore it if downloaded from viewer
viewerDownloadIds.add(downloadId);
console.log('File download initiated:', request.filename, 'ID:', downloadId);

sendResponse({ success: true, downloadId: downloadId });
} catch (error) {
console.error('Error downloading file:', error);
Expand All @@ -72,8 +72,8 @@ async function handleDownloadFile(request, _sender, sendResponse) {

// Action to handler mapping
const messageHandlers = {
'toggleExtension': handleToggleExtension,
'downloadFile': handleDownloadFile,
toggleExtension: handleToggleExtension,
downloadFile: handleDownloadFile,
};

// Listen for messages
Expand All @@ -87,13 +87,13 @@ chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {

// Handle context menu clicks
chrome.contextMenus.onClicked.addListener(async (info, tab) => {
if (!(info.menuItemId === "openSelectedInSuperdoc" && info.selectionText)) return;
if (!(info.menuItemId === 'openSelectedInSuperdoc' && info.selectionText)) return;

// Send message to content script to capture HTML and open in SuperDoc
try {
await chrome.tabs.sendMessage(tab.id, {
action: 'captureSelectedHTML',
selectedText: info.selectionText
selectedText: info.selectionText,
});
} catch (error) {
console.error('Error sending message to content script:', error);
Expand All @@ -109,14 +109,14 @@ chrome.downloads.onChanged.addListener(async (downloadDelta) => {
console.log('Extension disabled, ignoring download');
return;
}

// Check if this is a download from viewer - if so, ignore it, otherwise we get endless loop of opening modals
if (viewerDownloadIds.has(downloadDelta.id)) {
viewerDownloadIds.delete(downloadDelta.id);
console.log('Ignoring viewer download completion:', downloadDelta.id);
return;
}

try {
await processDownload(downloadDelta.id);
} catch (error) {
Expand All @@ -131,7 +131,7 @@ async function sendMessageToActiveTab(action, payload) {

await chrome.tabs.sendMessage(tabs[0].id, {
action,
...payload
...payload,
});
} catch (error) {
console.error('Error sending message to content script:', error);
Expand All @@ -144,19 +144,19 @@ async function processDownload(downloadId) {

const download = downloads[0];
const filename = download.filename.toLowerCase();

// File type handlers
// We will handle markdown like html, since they are interoperable (to a point)
const fileHandlers = {
'.docx': processDocxFile,
'.md': processMarkdownFile,
'.markdown': processMarkdownFile
'.markdown': processMarkdownFile,
};

const extension = filename.substring(filename.lastIndexOf('.'));
const handler = fileHandlers[extension];
if (!handler) throw new Error(`No handler for file type: ${extension}`);

await handler(download);
}

Expand All @@ -165,7 +165,7 @@ async function processDocxFile(download) {
// fetch and stringify (actual blob was getting dropped on message to viewer.js)
const response = await fetch(`file://${download.filename}`); // background scripts let us do cool stuff like this
const blob = await response.blob();

// Validate and correct the DOCX file
// Some DOCX files are generate with little to no style info or a poor schema,
// here we try to fill in the blanks.
Expand All @@ -178,86 +178,86 @@ async function processDocxFile(download) {
console.error('Error validating DOCX:', error);
// Continue with original blob if validation fails
}

// convert to b64, actual blobs were getting dropped on message to content script
const base64Data = await new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => resolve(reader.result.split(',')[1]);
reader.onerror = reject;
reader.readAsDataURL(correctedBlob);
});

// Send message to content script to display modal
await sendMessageToActiveTab('displayDOCX', {
data: {
filename: download.filename,
mimeType: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
fileSize: correctedBlob.size,
base64Data
}
base64Data,
},
});
}

async function processMarkdownFile(download) {
// Fetch the markdown file content
const response = await fetch(`file://${download.filename}`);
const markdownText = await response.text();

// Convert markdown to HTML
const htmlContent = markdownToHtml(markdownText);

// Send message to content script with HTML content
await sendMessageToActiveTab('displayMarkdown', {
data: {
filename: download.filename,
htmlContent: htmlContent,
originalMarkdown: markdownText
}
originalMarkdown: markdownText,
},
});
}

// Simple markdown to HTML converter
function markdownToHtml(markdown) {
let html = markdown;

// Headers
html = html.replace(/^### (.*$)/gim, '<h3>$1</h3>');
html = html.replace(/^## (.*$)/gim, '<h2>$1</h2>');
html = html.replace(/^# (.*$)/gim, '<h1>$1</h1>');

// Bold
html = html.replace(/\*\*(.*?)\*\*/gim, '<strong>$1</strong>');
html = html.replace(/__(.*?)__/gim, '<strong>$1</strong>');

// Italic
html = html.replace(/\*(.*?)\*/gim, '<em>$1</em>');
html = html.replace(/_(.+?)_/gim, '<em>$1</em>');

// Code blocks
html = html.replace(/```([\s\S]*?)```/gim, '<pre><code>$1</code></pre>');

// Inline code
html = html.replace(/`(.*?)`/gim, '<code>$1</code>');

// Links
html = html.replace(/\[([^\]]*)\]\(([^\)]*)\)/gim, '<a href="$2">$1</a>');

// Images
html = html.replace(/!\[([^\]]*)\]\(([^\)]*)\)/gim, '<img alt="$1" src="$2" />');

// Lists
html = html.replace(/^\* (.*$)/gim, '<li>$1</li>');
html = html.replace(/^\- (.*$)/gim, '<li>$1</li>');
html = html.replace(/^\+ (.*$)/gim, '<li>$1</li>');

// Wrap consecutive list items in ul tags
html = html.replace(/(<li>.*<\/li>)/gims, '<ul>$1</ul>');
html = html.replace(/<\/ul>\s*<ul>/gim, '');

// Line breaks
html = html.replace(/\n\n/gim, '</p><p>');
html = html.replace(/\n/gim, '<br>');

// Clean up empty paragraphs
html = html.replace(/<p><\/p>/gim, '');
html = html.replace(/<p>(<h[1-6]>)/gim, '$1');
Expand All @@ -266,9 +266,9 @@ function markdownToHtml(markdown) {
html = html.replace(/(<\/ul>)<\/p>/gim, '$1');
html = html.replace(/<p>(<pre>)/gim, '$1');
html = html.replace(/(<\/pre>)<\/p>/gim, '$1');

// Wrap in paragraphs
html = '<p>' + html + '</p>';

return html;
}
}
Loading
Loading