|
@@ -167,6 +167,14 @@ export class ContentPage {
|
|
|
<span class="badge" id="modal-change-badge">Status</span>
|
|
<span class="badge" id="modal-change-badge">Status</span>
|
|
|
</div>
|
|
</div>
|
|
|
<div class="flex space-x-2">
|
|
<div class="flex space-x-2">
|
|
|
|
|
+ <div class="flex rounded-md shadow-sm" role="group">
|
|
|
|
|
+ <button type="button" class="btn btn-secondary btn-sm rounded-r-none border-r-0" id="view-raw-btn">
|
|
|
|
|
+ Raw
|
|
|
|
|
+ </button>
|
|
|
|
|
+ <button type="button" class="btn btn-primary btn-sm rounded-l-none" id="view-formatted-btn">
|
|
|
|
|
+ Formatted
|
|
|
|
|
+ </button>
|
|
|
|
|
+ </div>
|
|
|
<button type="button" class="btn btn-secondary btn-sm" id="copy-content-btn">
|
|
<button type="button" class="btn btn-secondary btn-sm" id="copy-content-btn">
|
|
|
<div class="w-4 h-4 mr-1">${getIcon('copy')}</div>
|
|
<div class="w-4 h-4 mr-1">${getIcon('copy')}</div>
|
|
|
Copy
|
|
Copy
|
|
@@ -178,9 +186,12 @@ export class ContentPage {
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
<div class="bg-gray-50 dark:bg-gray-700 rounded-lg p-4 max-h-96 overflow-y-auto">
|
|
<div class="bg-gray-50 dark:bg-gray-700 rounded-lg p-4 max-h-96 overflow-y-auto">
|
|
|
- <pre class="whitespace-pre-wrap text-sm text-gray-800 dark:text-gray-200" id="modal-content">
|
|
|
|
|
|
|
+ <pre class="whitespace-pre-wrap text-sm text-gray-800 dark:text-gray-200 hidden" id="modal-content-raw">
|
|
|
Content will appear here...
|
|
Content will appear here...
|
|
|
</pre>
|
|
</pre>
|
|
|
|
|
+ <div class="text-sm text-gray-800 dark:text-gray-200 prose dark:prose-invert max-w-none" id="modal-content-formatted">
|
|
|
|
|
+ Formatted content will appear here...
|
|
|
|
|
+ </div>
|
|
|
</div>
|
|
</div>
|
|
|
<div class="mt-4 text-xs text-gray-500 dark:text-gray-400" id="modal-metadata">
|
|
<div class="mt-4 text-xs text-gray-500 dark:text-gray-400" id="modal-metadata">
|
|
|
Last updated: --
|
|
Last updated: --
|
|
@@ -260,6 +271,18 @@ export class ContentPage {
|
|
|
}
|
|
}
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
|
|
+ // View toggle buttons
|
|
|
|
|
+ const viewRawBtn = this.element.querySelector('#view-raw-btn');
|
|
|
|
|
+ const viewFormattedBtn = this.element.querySelector('#view-formatted-btn');
|
|
|
|
|
+
|
|
|
|
|
+ viewRawBtn?.addEventListener('click', () => {
|
|
|
|
|
+ this.showRawView();
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ viewFormattedBtn?.addEventListener('click', () => {
|
|
|
|
|
+ this.showFormattedView();
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
// ESC key to close modal
|
|
// ESC key to close modal
|
|
|
document.addEventListener('keydown', (e) => {
|
|
document.addEventListener('keydown', (e) => {
|
|
|
if (e.key === 'Escape') {
|
|
if (e.key === 'Escape') {
|
|
@@ -652,9 +675,18 @@ export class ContentPage {
|
|
|
const modalTypeBadge = this.element?.querySelector('#modal-type-badge');
|
|
const modalTypeBadge = this.element?.querySelector('#modal-type-badge');
|
|
|
const modalChangeBadge = this.element?.querySelector('#modal-change-badge');
|
|
const modalChangeBadge = this.element?.querySelector('#modal-change-badge');
|
|
|
|
|
|
|
|
- if (modalTitle) modalTitle.textContent = `${getContentTypeLabel(foundItem.content_type)} Content`;
|
|
|
|
|
|
|
+ // Use page title if available, otherwise fallback to content type
|
|
|
|
|
+ const pageTitle = foundItem.title || `${getContentTypeLabel(foundItem.content_type)} Content`;
|
|
|
|
|
+ if (modalTitle) modalTitle.textContent = pageTitle;
|
|
|
if (modalUrl) modalUrl.textContent = foundItem.url;
|
|
if (modalUrl) modalUrl.textContent = foundItem.url;
|
|
|
- if (modalContent) modalContent.textContent = foundItem.content || '';
|
|
|
|
|
|
|
+
|
|
|
|
|
+ // Update both content views
|
|
|
|
|
+ const content = foundItem.content || '';
|
|
|
|
|
+ const modalContentRaw = this.element?.querySelector('#modal-content-raw');
|
|
|
|
|
+ const modalContentFormatted = this.element?.querySelector('#modal-content-formatted');
|
|
|
|
|
+
|
|
|
|
|
+ if (modalContentRaw) modalContentRaw.textContent = content;
|
|
|
|
|
+ if (modalContentFormatted) modalContentFormatted.innerHTML = this.renderMarkdown(content);
|
|
|
if (modalMetadata) {
|
|
if (modalMetadata) {
|
|
|
const content = foundItem.content || '';
|
|
const content = foundItem.content || '';
|
|
|
const contentHash = foundItem.content_hash || '';
|
|
const contentHash = foundItem.content_hash || '';
|
|
@@ -684,6 +716,74 @@ export class ContentPage {
|
|
|
modal?.classList.remove('hidden');
|
|
modal?.classList.remove('hidden');
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ private showRawView(): void {
|
|
|
|
|
+ const rawElement = this.element?.querySelector('#modal-content-raw');
|
|
|
|
|
+ const formattedElement = this.element?.querySelector('#modal-content-formatted');
|
|
|
|
|
+ const rawBtn = this.element?.querySelector('#view-raw-btn');
|
|
|
|
|
+ const formattedBtn = this.element?.querySelector('#view-formatted-btn');
|
|
|
|
|
+
|
|
|
|
|
+ // Toggle visibility
|
|
|
|
|
+ rawElement?.classList.remove('hidden');
|
|
|
|
|
+ formattedElement?.classList.add('hidden');
|
|
|
|
|
+
|
|
|
|
|
+ // Update button styles
|
|
|
|
|
+ rawBtn?.classList.remove('btn-secondary');
|
|
|
|
|
+ rawBtn?.classList.add('btn-primary');
|
|
|
|
|
+ formattedBtn?.classList.remove('btn-primary');
|
|
|
|
|
+ formattedBtn?.classList.add('btn-secondary');
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private showFormattedView(): void {
|
|
|
|
|
+ const rawElement = this.element?.querySelector('#modal-content-raw');
|
|
|
|
|
+ const formattedElement = this.element?.querySelector('#modal-content-formatted');
|
|
|
|
|
+ const rawBtn = this.element?.querySelector('#view-raw-btn');
|
|
|
|
|
+ const formattedBtn = this.element?.querySelector('#view-formatted-btn');
|
|
|
|
|
+
|
|
|
|
|
+ // Toggle visibility
|
|
|
|
|
+ rawElement?.classList.add('hidden');
|
|
|
|
|
+ formattedElement?.classList.remove('hidden');
|
|
|
|
|
+
|
|
|
|
|
+ // Update button styles
|
|
|
|
|
+ rawBtn?.classList.remove('btn-primary');
|
|
|
|
|
+ rawBtn?.classList.add('btn-secondary');
|
|
|
|
|
+ formattedBtn?.classList.remove('btn-secondary');
|
|
|
|
|
+ formattedBtn?.classList.add('btn-primary');
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private renderMarkdown(markdown: string): string {
|
|
|
|
|
+ // Simple markdown renderer - handles basic formatting
|
|
|
|
|
+ return markdown
|
|
|
|
|
+ // Headers
|
|
|
|
|
+ .replace(/^### (.*$)/gim, '<h3 class="text-lg font-semibold text-gray-900 dark:text-white mt-4 mb-2">$1</h3>')
|
|
|
|
|
+ .replace(/^## (.*$)/gim, '<h2 class="text-xl font-semibold text-gray-900 dark:text-white mt-6 mb-3">$1</h2>')
|
|
|
|
|
+ .replace(/^# (.*$)/gim, '<h1 class="text-2xl font-bold text-gray-900 dark:text-white mt-8 mb-4">$1</h1>')
|
|
|
|
|
+
|
|
|
|
|
+ // Bold and italic
|
|
|
|
|
+ .replace(/\*\*(.*?)\*\*/g, '<strong class="font-semibold">$1</strong>')
|
|
|
|
|
+ .replace(/\*(.*?)\*/g, '<em class="italic">$1</em>')
|
|
|
|
|
+
|
|
|
|
|
+ // Links
|
|
|
|
|
+ .replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2" class="text-blue-600 dark:text-blue-400 hover:underline" target="_blank" rel="noopener noreferrer">$1</a>')
|
|
|
|
|
+
|
|
|
|
|
+ // Lists
|
|
|
|
|
+ .replace(/^\- (.*$)/gim, '<li class="ml-4">• $1</li>')
|
|
|
|
|
+ .replace(/^(\d+)\. (.*$)/gim, '<li class="ml-4">$1. $2</li>')
|
|
|
|
|
+
|
|
|
|
|
+ // Code blocks (basic)
|
|
|
|
|
+ .replace(/```([\s\S]*?)```/g, '<pre class="bg-gray-100 dark:bg-gray-800 p-3 rounded text-sm overflow-x-auto mt-2 mb-2"><code>$1</code></pre>')
|
|
|
|
|
+ .replace(/`([^`]+)`/g, '<code class="bg-gray-100 dark:bg-gray-800 px-1 rounded text-sm">$1</code>')
|
|
|
|
|
+
|
|
|
|
|
+ // Line breaks
|
|
|
|
|
+ .replace(/\n\n/g, '</p><p class="mb-2">')
|
|
|
|
|
+ .replace(/\n/g, '<br>')
|
|
|
|
|
+
|
|
|
|
|
+ // Wrap in paragraphs
|
|
|
|
|
+ .replace(/^(.*)$/gm, '<p class="mb-2">$1</p>')
|
|
|
|
|
+
|
|
|
|
|
+ // Clean up empty paragraphs
|
|
|
|
|
+ .replace(/<p class="mb-2"><\/p>/g, '');
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
destroy(): void {
|
|
destroy(): void {
|
|
|
this.eventsbound = false;
|
|
this.eventsbound = false;
|
|
|
// Clean up event listeners
|
|
// Clean up event listeners
|