Replace Google Docs viewer with download card for non-PDF file embeds
PDF files continue to embed in an iframe. Non-PDF files (DOC, DOCX, XLS, etc.) now show a download card with the file name and download icon instead of relying on Google Docs Viewer, which often fails with "No preview available." Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
148
js/assets.js
148
js/assets.js
@@ -478,19 +478,47 @@
|
||||
const fileInput = modal.querySelector('#asset-browser-file');
|
||||
modal.querySelector('#asset-browser-upload-btn').addEventListener('click', () => fileInput.click());
|
||||
fileInput.addEventListener('change', async (e) => {
|
||||
this._handleFiles(e.target.files);
|
||||
const files = Array.from(e.target.files);
|
||||
fileInput.value = '';
|
||||
// Wait a moment for uploads to complete, then re-render
|
||||
setTimeout(() => {
|
||||
const activeTab = modal.querySelector('.asset-tab.active');
|
||||
this.renderBrowserGrid(activeTab?.dataset.type || 'image');
|
||||
}, 500);
|
||||
// Upload files and re-render after all complete
|
||||
const uploads = files.map(file => {
|
||||
let type = 'file';
|
||||
if (file.type.startsWith('image/')) type = 'image';
|
||||
else if (file.type.startsWith('video/')) type = 'video';
|
||||
else if (file.name.endsWith('.css')) type = 'css';
|
||||
else if (file.name.endsWith('.js')) type = 'js';
|
||||
if (this.serverAvailable) {
|
||||
return this._uploadFileToServer(file, type);
|
||||
} else {
|
||||
this._showServerRequiredError();
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
});
|
||||
await Promise.all(uploads);
|
||||
const activeTab = modal.querySelector('.asset-tab.active');
|
||||
this.renderBrowserGrid(activeTab?.dataset.type || 'image');
|
||||
});
|
||||
}
|
||||
|
||||
openBrowser(filterType) {
|
||||
async openBrowser(filterType) {
|
||||
if (!this.modal) return Promise.reject('No modal');
|
||||
|
||||
// Refresh assets from server to pick up uploads from other code paths
|
||||
if (this.serverAvailable) {
|
||||
try {
|
||||
const resp = await fetch(API_BASE + '/api/assets');
|
||||
if (resp.ok) {
|
||||
const data = await resp.json();
|
||||
if (data.success && Array.isArray(data.assets)) {
|
||||
this.assets = data.assets;
|
||||
this._saveMetadataIndex();
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
// Use existing cached assets on failure
|
||||
}
|
||||
}
|
||||
|
||||
// Set active tab
|
||||
const tabs = this.modal.querySelectorAll('.asset-tab');
|
||||
tabs.forEach(t => t.classList.remove('active'));
|
||||
@@ -603,66 +631,10 @@
|
||||
});
|
||||
}
|
||||
|
||||
// -- Patch Video Block Traits --
|
||||
// -- Video traits are defined directly in editor.js component types --
|
||||
// Browse Video Assets buttons reference window.assetManager.openBrowser()
|
||||
patchVideoTraits() {
|
||||
const editor = this.editor;
|
||||
const self = this;
|
||||
|
||||
const browseVideoTrait = {
|
||||
type: 'button',
|
||||
label: '',
|
||||
text: '📁 Browse Video Assets',
|
||||
full: true,
|
||||
command: () => {
|
||||
self.openBrowser('video').then(asset => {
|
||||
if (!asset) return;
|
||||
const selected = editor.getSelected();
|
||||
if (selected) {
|
||||
selected.addAttributes({ videoUrl: asset.url });
|
||||
// Trigger the video URL change handler
|
||||
const event = 'change:attributes:videoUrl';
|
||||
selected.trigger(event);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Patch video-wrapper if it exists
|
||||
const videoWrapperType = editor.DomComponents.getType('video-wrapper');
|
||||
if (videoWrapperType) {
|
||||
const origDefaults = videoWrapperType.model.prototype.defaults;
|
||||
const origTraits = Array.isArray(origDefaults.traits) ? origDefaults.traits : [];
|
||||
|
||||
editor.DomComponents.addType('video-wrapper', {
|
||||
model: {
|
||||
defaults: {
|
||||
traits: [
|
||||
...origTraits.filter(t => t.type !== 'button'),
|
||||
browseVideoTrait,
|
||||
...origTraits.filter(t => t.type === 'button')
|
||||
]
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Also patch the 'video' type
|
||||
const videoType = editor.DomComponents.getType('video');
|
||||
if (videoType) {
|
||||
const origDefaults = videoType.model.prototype.defaults;
|
||||
const origTraits = Array.isArray(origDefaults.traits) ? origDefaults.traits : [];
|
||||
|
||||
editor.DomComponents.addType('video', {
|
||||
model: {
|
||||
defaults: {
|
||||
traits: [
|
||||
...origTraits.filter(t => t.type !== 'button'),
|
||||
browseVideoTrait
|
||||
]
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
// No-op: browse buttons are now built into video-wrapper and video-section types
|
||||
}
|
||||
|
||||
// -- Patch File/PDF Block Traits --
|
||||
@@ -683,22 +655,46 @@
|
||||
|
||||
selected.addAttributes({ fileUrl: asset.url });
|
||||
|
||||
// Apply the file: show iframe, hide placeholder
|
||||
const url = asset.url;
|
||||
const height = selected.getAttributes().frameHeight || 600;
|
||||
let embedUrl = url;
|
||||
if (!url.match(/\.pdf(\?.*)?$/i) && !url.includes('docs.google.com')) {
|
||||
embedUrl = `https://docs.google.com/viewer?url=${encodeURIComponent(url)}&embedded=true`;
|
||||
}
|
||||
const isPdf = url.match(/\.pdf(\?.*)?$/i);
|
||||
|
||||
const iframe = selected.components().find(c => c.getClasses().includes('file-embed-frame'));
|
||||
const placeholder = selected.components().find(c => c.getClasses().includes('file-embed-placeholder'));
|
||||
const downloadCard = selected.components().find(c => c.getClasses().includes('file-download-card'));
|
||||
|
||||
if (iframe) {
|
||||
iframe.addAttributes({ src: embedUrl });
|
||||
iframe.addStyle({ display: 'block', height: height + 'px' });
|
||||
const el = iframe.getEl();
|
||||
if (el) { el.src = embedUrl; el.style.display = 'block'; el.style.height = height + 'px'; }
|
||||
if (isPdf) {
|
||||
// PDF: embed in iframe
|
||||
if (iframe) {
|
||||
iframe.addAttributes({ src: url });
|
||||
iframe.addStyle({ display: 'block', height: height + 'px' });
|
||||
const el = iframe.getEl();
|
||||
if (el) { el.src = url; el.style.display = 'block'; el.style.height = height + 'px'; }
|
||||
}
|
||||
if (downloadCard) {
|
||||
downloadCard.addStyle({ display: 'none' });
|
||||
const el = downloadCard.getEl();
|
||||
if (el) el.style.display = 'none';
|
||||
}
|
||||
} else {
|
||||
// Non-PDF: show download card
|
||||
if (iframe) {
|
||||
iframe.addStyle({ display: 'none' });
|
||||
const el = iframe.getEl();
|
||||
if (el) el.style.display = 'none';
|
||||
}
|
||||
if (downloadCard) {
|
||||
const fileName = decodeURIComponent(url.split('/').pop().split('?')[0]) || 'File';
|
||||
downloadCard.addAttributes({ href: url });
|
||||
downloadCard.addStyle({ display: 'flex' });
|
||||
const cardEl = downloadCard.getEl();
|
||||
if (cardEl) {
|
||||
cardEl.href = url;
|
||||
cardEl.style.display = 'flex';
|
||||
const nameNode = cardEl.querySelector('.file-download-name');
|
||||
if (nameNode) nameNode.textContent = fileName;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (placeholder) {
|
||||
placeholder.addStyle({ display: 'none' });
|
||||
|
||||
Reference in New Issue
Block a user