Browse Source

bug-fixes

Nostr-Signature: 6fd5146b5f5980987adc5e6b93a1bcb31cfbb6a2e4fce6f1dbe5c7fdf54b717a 573634b648634cbad10f2451776089ea21090d9407f715e83c577b4611ae6edc 41422b07b63fc494c0aba22b85f1b56a6a5527f9df48d49816f7be417ad74608f8d1dff8302f562b2f47690720c089aab76db948f5bf1ecdda68741b256d7a2b
main
Silberengel 3 weeks ago
parent
commit
c8e7d34509
  1. 1
      nostr/commit-signatures.jsonl
  2. 237
      src/app.css
  3. 9
      src/lib/components/RepoHeaderEnhanced.svelte
  4. 12
      src/lib/styles/components.css
  5. 2190
      src/lib/styles/repo.css
  6. 2
      src/routes/api/repos/[npub]/[repo]/download/+server.ts
  7. 171
      src/routes/repos/[npub]/[repo]/+page.svelte

1
nostr/commit-signatures.jsonl

@ -46,3 +46,4 @@ @@ -46,3 +46,4 @@
{"kind":1640,"pubkey":"573634b648634cbad10f2451776089ea21090d9407f715e83c577b4611ae6edc","created_at":1771690183,"tags":[["author","Silberengel","silberengel7@protonmail.com"],["message","get rid of tabs on repo page"]],"content":"Signed commit: get rid of tabs on repo page","id":"d34fb23385a23f479c683e76f5676356a11d63bcd0ecf71d25f1b85dbb0cfe57","sig":"1f6454f9961b9245d1e32f4a903ee9636201670491145d0185e95e7b7d33bf1027ac5b8e370070640e103740ab19e9915baa7755c6008fd32fe41e9cb86d33b8"}
{"kind":1640,"pubkey":"573634b648634cbad10f2451776089ea21090d9407f715e83c577b4611ae6edc","created_at":1771691277,"tags":[["author","Silberengel","silberengel7@protonmail.com"],["message","fix docs"]],"content":"Signed commit: fix docs","id":"4671648712f19537cbf0fd00cf19e254eae4a1ac9c1274ea396e62dac193b88c","sig":"49a3e89e312ec4caebfeacdaade3e4cc6d027ab9c50d8e6aa1998f120a81d8d51235ae397df6e42b9efca4147497b8881731dda6d58fee7d28d2ac07cec295ec"}
{"kind":1640,"pubkey":"573634b648634cbad10f2451776089ea21090d9407f715e83c577b4611ae6edc","created_at":1771705699,"tags":[["author","Silberengel","silberengel7@protonmail.com"],["message","bug-fixes"]],"content":"Signed commit: bug-fixes","id":"59d0c409196dccb8109a29829002df69dbca43c5e95c1fdc1e7baa0b88ee5927","sig":"af8726a86e30c64b098ad13946d5bc84cb08d5ea8b75f08641c03fbdd8b9c91683e8091b206159dde2239ea8964cb3589bcb4ec2892541d2980f186a0fb09af9"}
{"kind":1640,"pubkey":"573634b648634cbad10f2451776089ea21090d9407f715e83c577b4611ae6edc","created_at":1771708933,"tags":[["author","Silberengel","silberengel7@protonmail.com"],["message","bug-fixes"]],"content":"Signed commit: bug-fixes","id":"6d8832125b76095b2e7ed57b71e26a6c05d9b19a14dfa76724c71f392147fe95","sig":"6ddebfa995b5b3f469db5f3cdbd7d13fa2307d7988c2667479015d6bc2ff442be357ee97e51340a944eb34fed73522db3930016d343810927486bdbcabddae5c"}

237
src/app.css

@ -639,42 +639,80 @@ textarea:disabled::placeholder { @@ -639,42 +639,80 @@ textarea:disabled::placeholder {
}
/* Make icon images theme-aware using filters */
/* Target both img inside .icon and img with icon classes */
img.icon,
.icon img,
img.icon-small,
.icon-small img,
img.icon-inline,
.icon-inline img,
.hamburger-icon img,
.platform-icon img,
.repo-badge-icon img,
.btn-icon img {
filter: var(--icon-filter, brightness(0) saturate(100%) invert(var(--icon-invert, 0)));
transition: filter 0.3s ease;
opacity: 0.9;
}
/* Theme icons - handled separately for better control */
.theme-icon img:not(.theme-toggle .theme-icon img),
.theme-icon-option img:not(.theme-option .theme-icon-option img) {
filter: var(--icon-filter, brightness(0) saturate(100%) invert(var(--icon-invert, 0)));
transition: filter 0.3s ease;
opacity: 0.9;
filter: brightness(0) saturate(100%) invert(1) !important; /* Default white for dark themes */
opacity: 1 !important;
transition: filter 0.3s ease, opacity 0.3s ease;
}
/* Light theme: icons should be dark (black) */
:root,
[data-theme="light"] {
--icon-filter: brightness(0) saturate(100%);
--icon-invert: 0;
[data-theme="light"] img.icon,
[data-theme="light"] .icon img,
[data-theme="light"] img.icon-small,
[data-theme="light"] .icon-small img,
[data-theme="light"] img.icon-inline,
[data-theme="light"] .icon-inline img,
[data-theme="light"] .hamburger-icon img,
[data-theme="light"] .platform-icon img,
[data-theme="light"] .repo-badge-icon img,
[data-theme="light"] .btn-icon img {
filter: brightness(0) saturate(100%) !important; /* Black in light theme */
opacity: 1 !important;
}
/* Dark theme: icons should be light (white/light gray) */
[data-theme="dark"] {
--icon-filter: brightness(0) saturate(100%) invert(1);
--icon-invert: 1;
/* Dark theme (Purple): icons should be light (white/light gray) */
[data-theme="dark"] img.icon,
[data-theme="dark"] .icon img,
[data-theme="dark"] img.icon-small,
[data-theme="dark"] .icon-small img,
[data-theme="dark"] img.icon-inline,
[data-theme="dark"] .icon-inline img,
[data-theme="dark"] .hamburger-icon img,
[data-theme="dark"] .platform-icon img,
[data-theme="dark"] .repo-badge-icon img,
[data-theme="dark"] .btn-icon img {
filter: brightness(0) saturate(100%) invert(1) !important; /* White in dark/purple theme */
opacity: 1 !important;
}
/* Black theme: icons should be light (white/light gray) */
[data-theme="black"] {
--icon-filter: brightness(0) saturate(100%) invert(1);
--icon-invert: 1;
[data-theme="black"] img.icon,
[data-theme="black"] .icon img,
[data-theme="black"] img.icon-small,
[data-theme="black"] .icon-small img,
[data-theme="black"] img.icon-inline,
[data-theme="black"] .icon-inline img,
[data-theme="black"] .hamburger-icon img,
[data-theme="black"] .platform-icon img,
[data-theme="black"] .repo-badge-icon img,
[data-theme="black"] .btn-icon img {
filter: brightness(0) saturate(100%) invert(1) !important; /* White in black theme */
opacity: 1 !important;
}
/* Theme icons - handled separately for better control */
.theme-icon img:not(.theme-toggle .theme-icon img),
.theme-icon-option img:not(.theme-option .theme-icon-option img) {
filter: brightness(0) saturate(100%) invert(1) !important; /* Default white for dark backgrounds */
opacity: 1 !important;
transition: filter 0.3s ease, opacity 0.3s ease;
}
/* Light theme: theme icons should be dark */
[data-theme="light"] .theme-icon img:not(.theme-toggle .theme-icon img),
[data-theme="light"] .theme-icon-option img:not(.theme-option .theme-icon-option img) {
filter: brightness(0) saturate(100%) !important; /* Black in light theme */
opacity: 1 !important;
}
.icon-inline {
@ -692,11 +730,71 @@ textarea:disabled::placeholder { @@ -692,11 +730,71 @@ textarea:disabled::placeholder {
}
/* Ensure icons in buttons and interactive elements have proper contrast */
button img.icon,
button .icon img,
button img.icon-inline,
button .icon-inline img,
button img.icon-small,
button .icon-small img,
.button img.icon,
.button .icon img,
.button img.icon-inline,
.button .icon-inline img,
.button img.icon-small,
.button .icon-small img {
filter: var(--icon-filter, brightness(0) saturate(100%) invert(var(--icon-invert, 0)));
filter: brightness(0) saturate(100%) invert(1) !important; /* Default white for dark themes */
opacity: 1 !important;
}
/* Light theme: button icons should be dark */
[data-theme="light"] button img.icon,
[data-theme="light"] button .icon img,
[data-theme="light"] button img.icon-inline,
[data-theme="light"] button .icon-inline img,
[data-theme="light"] button img.icon-small,
[data-theme="light"] button .icon-small img,
[data-theme="light"] .button img.icon,
[data-theme="light"] .button .icon img,
[data-theme="light"] .button img.icon-inline,
[data-theme="light"] .button .icon-inline img,
[data-theme="light"] .button img.icon-small,
[data-theme="light"] .button .icon-small img {
filter: brightness(0) saturate(100%) !important; /* Black in light theme */
opacity: 1 !important;
}
/* Dark theme (Purple): button icons should be light */
[data-theme="dark"] button img.icon,
[data-theme="dark"] button .icon img,
[data-theme="dark"] button img.icon-inline,
[data-theme="dark"] button .icon-inline img,
[data-theme="dark"] button img.icon-small,
[data-theme="dark"] button .icon-small img,
[data-theme="dark"] .button img.icon,
[data-theme="dark"] .button .icon img,
[data-theme="dark"] .button img.icon-inline,
[data-theme="dark"] .button .icon-inline img,
[data-theme="dark"] .button img.icon-small,
[data-theme="dark"] .button .icon-small img {
filter: brightness(0) saturate(100%) invert(1) !important; /* White in dark/purple theme */
opacity: 1 !important;
}
/* Black theme: button icons should be light */
[data-theme="black"] button img.icon,
[data-theme="black"] button .icon img,
[data-theme="black"] button img.icon-inline,
[data-theme="black"] button .icon-inline img,
[data-theme="black"] button img.icon-small,
[data-theme="black"] button .icon-small img,
[data-theme="black"] .button img.icon,
[data-theme="black"] .button .icon img,
[data-theme="black"] .button img.icon-inline,
[data-theme="black"] .button .icon-inline img,
[data-theme="black"] .button img.icon-small,
[data-theme="black"] .button .icon-small img {
filter: brightness(0) saturate(100%) invert(1) !important; /* White in black theme */
opacity: 1 !important;
}
/* Icons on accent backgrounds should be white */
@ -705,7 +803,8 @@ button[class*="primary"] .icon-small img, @@ -705,7 +803,8 @@ button[class*="primary"] .icon-small img,
.button[class*="primary"] .icon-inline img,
.button[class*="primary"] .icon-small img,
.theme-toggle:hover .theme-icon img {
filter: brightness(0) saturate(100%) invert(1);
filter: brightness(0) saturate(100%) invert(1) !important;
opacity: 1 !important;
}
/* Theme toggle icons - ensure high contrast with maximum specificity */
@ -1330,30 +1429,33 @@ button.theme-option.active img.theme-icon-option, @@ -1330,30 +1429,33 @@ button.theme-option.active img.theme-icon-option,
color: var(--error-text);
}
/* Code blocks - consistent dark-gray background in both themes */
code {
background: var(--bg-secondary);
/* Unified code styling - all code uses highlight.js standard theme with dark-gray background */
/* Inline code - matches highlight.js theme */
code:not(pre code) {
background: #1e1e1e !important;
color: #d4d4d4 !important;
padding: 0.125rem 0.25rem;
border-radius: 0.25rem;
font-family: 'IBM Plex Mono', monospace;
font-size: 0.875em;
color: var(--text-primary);
border: 1px solid #3a3a3a;
}
/* Pre wrappers are transparent - only code.hljs has styling */
pre {
background: #1e1e1e; /* Consistent dark-gray background */
color: #d4d4d4; /* Light gray text for good contrast */
padding: 1rem;
border-radius: 0.5rem;
overflow-x: auto;
margin: 1rem 0;
border: 1px solid #3a3a3a;
margin: 0;
padding: 0;
background: transparent;
border: none;
overflow: visible;
}
pre code {
background: transparent;
padding: 0;
color: inherit;
/* Code blocks - highlight.js handles everything */
pre code.hljs {
display: block;
width: 100%;
box-sizing: border-box;
}
.clone-urls {
@ -1365,14 +1467,14 @@ pre code { @@ -1365,14 +1467,14 @@ pre code {
.clone-urls code {
display: block;
background: var(--bg-secondary);
background: #1e1e1e !important; /* Dark-gray background - theme independent */
color: #d4d4d4 !important; /* Light gray text - theme independent */
padding: 0.5rem;
border-radius: 0.25rem;
margin: 0.25rem 0;
font-family: 'IBM Plex Mono', monospace;
font-size: 0.875rem;
color: var(--text-primary);
border: 1px solid var(--border-light);
border: 1px solid #3a3a3a;
word-break: break-all;
overflow-wrap: break-word;
}
@ -1806,93 +1908,98 @@ label.filter-checkbox > span, @@ -1806,93 +1908,98 @@ label.filter-checkbox > span,
/* All pages use the same container width for consistency */
/* Highlight.js Syntax Highlighting - Consistent dark-gray background */
/* Highlight.js - unified code block styling - theme independent */
.hljs {
background: #1e1e1e !important; /* Consistent dark-gray background in both themes */
color: #d4d4d4 !important; /* Light gray text for good contrast */
border: 1px solid #3a3a3a;
border-radius: 4px;
padding: 1rem;
display: block;
background: #1e1e1e !important;
color: #d4d4d4 !important;
border: 1px solid #3a3a3a !important;
border-radius: 4px !important;
padding: 1rem !important;
overflow-x: auto;
margin: 0;
font-family: 'IBM Plex Mono', monospace;
font-size: 0.875rem;
line-height: 1.5;
}
/* Syntax highlighting colors - high contrast for dark-gray background */
/* Syntax highlighting colors - high contrast for dark-gray background - theme independent */
.hljs-comment,
.hljs-quote {
color: #6a9955; /* Green comments - good contrast */
color: #6a9955 !important; /* Green comments - good contrast */
font-style: italic;
}
.hljs-keyword,
.hljs-selector-tag,
.hljs-subst {
color: #c586c0; /* Purple/magenta keywords - good contrast */
color: #c586c0 !important; /* Purple/magenta keywords - good contrast */
font-weight: 500;
}
.hljs-number,
.hljs-literal {
color: #b5cea8; /* Light green numbers - good contrast */
color: #b5cea8 !important; /* Light green numbers - good contrast */
font-weight: 500;
}
.hljs-variable,
.hljs-template-variable,
.hljs-tag .hljs-attr {
color: #9cdcfe; /* Light blue variables - good contrast */
color: #9cdcfe !important; /* Light blue variables - good contrast */
}
.hljs-string,
.hljs-doctag {
color: #ce9178; /* Orange strings - good contrast */
color: #ce9178 !important; /* Orange strings - good contrast */
}
.hljs-title,
.hljs-section,
.hljs-selector-id {
color: #dcdcaa; /* Yellow titles - good contrast */
color: #dcdcaa !important; /* Yellow titles - good contrast */
font-weight: 600;
}
.hljs-type,
.hljs-class .hljs-title {
color: #4ec9b0; /* Cyan types - good contrast */
color: #4ec9b0 !important; /* Cyan types - good contrast */
font-weight: 500;
}
.hljs-tag,
.hljs-name,
.hljs-attribute {
color: #569cd6; /* Blue tags - good contrast */
color: #569cd6 !important; /* Blue tags - good contrast */
}
.hljs-regexp,
.hljs-link {
color: #d16969; /* Red regexp - good contrast */
color: #d16969 !important; /* Red regexp - good contrast */
}
.hljs-symbol,
.hljs-bullet {
color: #dcdcaa; /* Yellow symbols - good contrast */
color: #dcdcaa !important; /* Yellow symbols - good contrast */
}
.hljs-built_in,
.hljs-builtin-name {
color: #4ec9b0; /* Cyan built-ins - good contrast */
color: #4ec9b0 !important; /* Cyan built-ins - good contrast */
}
.hljs-meta {
color: #808080; /* Gray meta - good contrast */
color: #808080 !important; /* Gray meta - good contrast */
}
.hljs-deletion {
background: #4a1f1f; /* Dark red background */
color: #ff8a8a; /* Light red text */
background: #4a1f1f !important; /* Dark red background */
color: #ff8a8a !important; /* Light red text */
}
.hljs-addition {
background: #1a3a2a; /* Dark green background */
color: #6aff9a; /* Light green text */
background: #1a3a2a !important; /* Dark green background */
color: #6aff9a !important; /* Light green text */
}
.hljs-emphasis {

9
src/lib/components/RepoHeaderEnhanced.svelte

@ -41,6 +41,7 @@ @@ -41,6 +41,7 @@
hasUnlimitedAccess?: boolean;
needsClone?: boolean;
allMaintainers?: Array<{ pubkey: string; isOwner: boolean }>;
onCopyEventId?: () => void;
}
let {
@ -80,7 +81,8 @@ @@ -80,7 +81,8 @@
deletingAnnouncement = false,
hasUnlimitedAccess = false,
needsClone = false,
allMaintainers = []
allMaintainers = [],
onCopyEventId
}: Props = $props();
let showCloneMenu = $state(false);
@ -184,6 +186,11 @@ @@ -184,6 +186,11 @@
Create New Branch
</button>
{/if}
{#if onCopyEventId}
<button class="menu-item" onclick={() => { onCopyEventId(); showMoreMenu = false; }}>
Copy Event ID
</button>
{/if}
{#if onDeleteAnnouncement}
<button
class="menu-item menu-item-danger"

12
src/lib/styles/components.css

@ -115,21 +115,21 @@ @@ -115,21 +115,21 @@
height: 18px;
flex-shrink: 0;
/* Theme-aware icon colors */
filter: brightness(0) saturate(100%) invert(1); /* Default white for dark themes */
opacity: 1;
filter: brightness(0) saturate(100%) invert(1) !important; /* Default white for dark themes */
opacity: 1 !important;
}
/* Light theme: black icon */
:global([data-theme="light"]) .icon {
filter: brightness(0) saturate(100%); /* Black in light theme */
opacity: 1;
filter: brightness(0) saturate(100%) !important; /* Black in light theme */
opacity: 1 !important;
}
/* Dark themes: white icon */
:global([data-theme="dark"]) .icon,
:global([data-theme="black"]) .icon {
filter: brightness(0) saturate(100%) invert(1); /* White in dark themes */
opacity: 1;
filter: brightness(0) saturate(100%) invert(1) !important; /* White in dark themes */
opacity: 1 !important;
}
.repo-description {

2190
src/lib/styles/repo.css

File diff suppressed because it is too large Load Diff

2
src/routes/api/repos/[npub]/[repo]/download/+server.ts

@ -17,6 +17,8 @@ import { handleApiError, handleNotFoundError } from '$lib/utils/error-handler.js @@ -17,6 +17,8 @@ import { handleApiError, handleNotFoundError } from '$lib/utils/error-handler.js
import { KIND } from '$lib/types/nostr.js';
import { existsSync } from 'fs';
import { repoCache, RepoCache } from '$lib/services/git/repo-cache.js';
import { eventCache } from '$lib/services/nostr/event-cache.js';
import { fetchRepoAnnouncementsWithCache, findRepoAnnouncement } from '$lib/utils/nostr-utils.js';
const repoRoot = typeof process !== 'undefined' && process.env?.GIT_REPO_ROOT
? process.env.GIT_REPO_ROOT

171
src/routes/repos/[npub]/[repo]/+page.svelte

@ -227,6 +227,26 @@ @@ -227,6 +227,26 @@
let isRepoCloned = $state<boolean | null>(null); // null = unknown, true = cloned, false = not cloned
let checkingCloneStatus = $state(false);
let cloning = $state(false);
// Word wrap toggle
let wordWrap = $state(false);
// Function to toggle word wrap and refresh highlighting
async function toggleWordWrap() {
wordWrap = !wordWrap;
console.log('Word wrap toggled:', wordWrap);
// Force DOM update by accessing the element
await new Promise(resolve => {
requestAnimationFrame(() => {
requestAnimationFrame(resolve);
});
});
// Re-apply syntax highlighting to refresh the display
if (currentFile && fileContent) {
const ext = currentFile.split('.').pop() || '';
await applySyntaxHighlighting(fileContent, ext);
}
}
let copyingCloneUrl = $state(false);
// Helper: Check if repo needs to be cloned for write operations
@ -706,6 +726,10 @@ @@ -706,6 +726,10 @@
// Mobile view toggle for file list/file viewer
let showFileListOnMobile = $state(true);
// Guard to prevent README auto-load loop
let readmeAutoLoadAttempted = $state(false);
let readmeAutoLoadTimeout: ReturnType<typeof setTimeout> | null = null;
async function loadReadme() {
if (repoNotFound) return;
@ -1838,6 +1862,10 @@ @@ -1838,6 +1862,10 @@
clearInterval(autoSaveInterval);
autoSaveInterval = null;
}
if (readmeAutoLoadTimeout) {
clearTimeout(readmeAutoLoadTimeout);
readmeAutoLoadTimeout = null;
}
});
async function checkAuth() {
@ -2031,6 +2059,59 @@ @@ -2031,6 +2059,59 @@
}
}
async function copyEventId() {
if (!repoAddress || !repoOwnerPubkey) {
alert('Repository address not available');
return;
}
try {
// Parse the repo address: kind:pubkey:identifier
const parts = repoAddress.split(':');
if (parts.length < 3) {
throw new Error('Invalid repository address format');
}
const kind = parseInt(parts[0]);
const pubkey = parts[1];
const identifier = parts.slice(2).join(':'); // In case identifier contains ':'
// Generate naddr synchronously
const naddr = nip19.naddrEncode({
kind,
pubkey,
identifier,
relays: [] // Optional: could include relays if available
});
// Copy naddr to clipboard immediately (while we have user activation)
try {
await navigator.clipboard.writeText(naddr);
} catch (clipboardErr) {
// Fallback: use execCommand for older browsers or if clipboard API fails
const textArea = document.createElement('textarea');
textArea.value = naddr;
textArea.style.position = 'fixed';
textArea.style.opacity = '0';
document.body.appendChild(textArea);
textArea.select();
try {
document.execCommand('copy');
textArea.remove();
} catch (execErr) {
textArea.remove();
throw new Error('Failed to copy to clipboard. Please copy manually: ' + naddr);
}
}
// Show message with naddr
alert(`Event ID copied to clipboard!\n\nnaddr (repository address):\n${naddr}`);
} catch (err) {
console.error('Failed to copy event ID:', err);
alert(`Failed to copy event ID: ${err instanceof Error ? err.message : String(err)}`);
}
}
async function checkMaintainerStatus() {
if (repoNotFound || !userPubkey) {
isMaintainer = false;
@ -2380,14 +2461,40 @@ @@ -2380,14 +2461,40 @@
currentPath = path;
// Auto-load README if we're in the root directory and no file is currently selected
if (path === '' && !currentFile) {
// Only attempt once per path to prevent loops
if (path === '' && !currentFile && !readmeAutoLoadAttempted) {
const readmeFile = findReadmeFile(files);
if (readmeFile) {
readmeAutoLoadAttempted = true;
// Clear any existing timeout
if (readmeAutoLoadTimeout) {
clearTimeout(readmeAutoLoadTimeout);
}
// Small delay to ensure UI is ready
setTimeout(() => {
loadFile(readmeFile.path);
readmeAutoLoadTimeout = setTimeout(() => {
loadFile(readmeFile.path).catch(err => {
// If load fails (e.g., 429 rate limit), reset the flag after a delay
// so we can retry later, but not immediately
if (err instanceof Error && err.message.includes('Too Many Requests')) {
console.warn('[README] Rate limited, will retry later');
setTimeout(() => {
readmeAutoLoadAttempted = false;
}, 5000); // Retry after 5 seconds
} else {
// For other errors, reset immediately
readmeAutoLoadAttempted = false;
}
});
readmeAutoLoadTimeout = null;
}, 100);
}
} else if (path !== '' || currentFile) {
// Reset flag when navigating away from root or when a file is selected
readmeAutoLoadAttempted = false;
if (readmeAutoLoadTimeout) {
clearTimeout(readmeAutoLoadTimeout);
readmeAutoLoadTimeout = null;
}
}
} catch (err) {
error = err instanceof Error ? err.message : 'Failed to load files';
@ -2458,6 +2565,12 @@ @@ -2458,6 +2565,12 @@
});
if (!response.ok) {
// Handle rate limiting specifically to prevent loops
if (response.status === 429) {
const error = new Error(`Failed to load file: Too Many Requests`);
console.warn('[File Load] Rate limited, please wait before retrying');
throw error;
}
throw new Error(`Failed to load file: ${response.statusText}`);
}
@ -2466,6 +2579,11 @@ @@ -2466,6 +2579,11 @@
editedContent = data.content;
currentFile = filePath;
hasChanges = false;
// Reset README auto-load flag when a file is successfully loaded
if (filePath && filePath.toLowerCase().includes('readme')) {
readmeAutoLoadAttempted = false;
}
// Determine language from file extension
const ext = filePath.split('.').pop()?.toLowerCase();
@ -3709,6 +3827,7 @@ @@ -3709,6 +3827,7 @@
hasUnlimitedAccess={hasUnlimitedAccess($userStore.userLevel)}
needsClone={needsClone}
allMaintainers={allMaintainers}
onCopyEventId={copyEventId}
/>
{/if}
@ -3810,6 +3929,14 @@ @@ -3810,6 +3929,14 @@
onTabChange={(tab) => activeTab = tab as typeof activeTab}
/>
<h2>Files</h2>
<button
onclick={toggleWordWrap}
class="word-wrap-button"
title={wordWrap ? 'Disable word wrap' : 'Enable word wrap'}
aria-label={wordWrap ? 'Disable word wrap' : 'Enable word wrap'}
>
{wordWrap ? 'Wrap' : 'No Wrap'}
</button>
<div class="file-tree-actions">
{#if pathStack.length > 0 || currentPath}
<button onclick={handleBack} class="back-button">← Back</button>
@ -4148,7 +4275,7 @@ @@ -4148,7 +4275,7 @@
readOnly={needsClone}
/>
{:else}
<div class="read-only-editor">
<div class="read-only-editor" class:word-wrap={wordWrap}>
{#if highlightedFileContent}
{@html highlightedFileContent}
{:else}
@ -5078,3 +5205,39 @@ @@ -5078,3 +5205,39 @@
</div>
{/if}
</div>
<style>
/* Word wrap styles - ensure they apply with highest specificity */
:global(.read-only-editor.word-wrap) {
overflow-x: hidden !important;
}
:global(.read-only-editor.word-wrap pre) {
white-space: pre-wrap !important;
word-wrap: break-word !important;
overflow-wrap: break-word !important;
overflow-x: hidden !important;
overflow-y: visible !important;
max-width: 100% !important;
}
:global(.read-only-editor.word-wrap pre code),
:global(.read-only-editor.word-wrap pre code.hljs),
:global(.read-only-editor.word-wrap code.hljs),
:global(.read-only-editor.word-wrap .hljs) {
white-space: pre-wrap !important;
word-wrap: break-word !important;
overflow-wrap: break-word !important;
overflow-x: hidden !important;
overflow-y: visible !important;
display: block !important;
max-width: 100% !important;
}
:global(.read-only-editor.word-wrap pre code.hljs *),
:global(.read-only-editor.word-wrap pre code.hljs span),
:global(.read-only-editor.word-wrap code.hljs *),
:global(.read-only-editor.word-wrap .hljs *) {
white-space: pre-wrap !important;
}
</style>

Loading…
Cancel
Save