Browse Source

refactor nostr-client and add more powerful syntax highlighting

main
Silberengel 4 weeks ago
parent
commit
cc571e4719
  1. 126
      package-lock.json
  2. 7
      package.json
  3. 34
      src/lib/services/nostr/nostr-client.ts
  4. 5
      src/routes/api/git/[...path]/+server.ts
  5. 20
      src/routes/docs/nip34/+page.svelte
  6. 217
      src/routes/repos/[npub]/[repo]/+page.svelte

126
package-lock.json generated

@ -17,13 +17,16 @@
"@sveltejs/vite-plugin-svelte": "^4.0.0", "@sveltejs/vite-plugin-svelte": "^4.0.0",
"codemirror": "^6.0.2", "codemirror": "^6.0.2",
"codemirror-asciidoc": "^2.0.1", "codemirror-asciidoc": "^2.0.1",
"marked": "^17.0.2", "highlight.js": "^11.10.0",
"markdown-it": "^14.1.0",
"nostr-tools": "^2.22.1", "nostr-tools": "^2.22.1",
"simple-git": "^3.31.1", "simple-git": "^3.31.1",
"svelte": "^5.0.0" "svelte": "^5.0.0",
"ws": "^8.19.0"
}, },
"devDependencies": { "devDependencies": {
"@sveltejs/adapter-node": "^5.0.0", "@sveltejs/adapter-node": "^5.0.0",
"@types/markdown-it": "^14.1.2",
"@types/node": "^20.0.0", "@types/node": "^20.0.0",
"@typescript-eslint/eslint-plugin": "^6.0.0", "@typescript-eslint/eslint-plugin": "^6.0.0",
"@typescript-eslint/parser": "^6.0.0", "@typescript-eslint/parser": "^6.0.0",
@ -1817,6 +1820,31 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/@types/linkify-it": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz",
"integrity": "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/markdown-it": {
"version": "14.1.2",
"resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.2.tgz",
"integrity": "sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/linkify-it": "^5",
"@types/mdurl": "^2"
}
},
"node_modules/@types/mdurl": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz",
"integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/node": { "node_modules/@types/node": {
"version": "20.19.33", "version": "20.19.33",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.33.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.33.tgz",
@ -2155,7 +2183,6 @@
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
"dev": true,
"license": "Python-2.0" "license": "Python-2.0"
}, },
"node_modules/aria-query": { "node_modules/aria-query": {
@ -2525,6 +2552,18 @@
"node": ">=6.0.0" "node": ">=6.0.0"
} }
}, },
"node_modules/entities": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
"integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
"license": "BSD-2-Clause",
"engines": {
"node": ">=0.12"
},
"funding": {
"url": "https://github.com/fb55/entities?sponsor=1"
}
},
"node_modules/es6-promise": { "node_modules/es6-promise": {
"version": "3.3.1", "version": "3.3.1",
"resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.3.1.tgz", "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.3.1.tgz",
@ -3088,6 +3127,15 @@
"node": ">= 0.4" "node": ">= 0.4"
} }
}, },
"node_modules/highlight.js": {
"version": "11.11.1",
"resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.11.1.tgz",
"integrity": "sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w==",
"license": "BSD-3-Clause",
"engines": {
"node": ">=12.0.0"
}
},
"node_modules/ignore": { "node_modules/ignore": {
"version": "5.3.2", "version": "5.3.2",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
@ -3307,6 +3355,15 @@
"node": ">= 0.8.0" "node": ">= 0.8.0"
} }
}, },
"node_modules/linkify-it": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz",
"integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==",
"license": "MIT",
"dependencies": {
"uc.micro": "^2.0.0"
}
},
"node_modules/locate-character": { "node_modules/locate-character": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/locate-character/-/locate-character-3.0.0.tgz", "resolved": "https://registry.npmjs.org/locate-character/-/locate-character-3.0.0.tgz",
@ -3345,18 +3402,29 @@
"@jridgewell/sourcemap-codec": "^1.5.5" "@jridgewell/sourcemap-codec": "^1.5.5"
} }
}, },
"node_modules/marked": { "node_modules/markdown-it": {
"version": "17.0.2", "version": "14.1.1",
"resolved": "https://registry.npmjs.org/marked/-/marked-17.0.2.tgz", "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.1.tgz",
"integrity": "sha512-s5HZGFQea7Huv5zZcAGhJLT3qLpAfnY7v7GWkICUr0+Wd5TFEtdlRR2XUL5Gg+RH7u2Df595ifrxR03mBaw7gA==", "integrity": "sha512-BuU2qnTti9YKgK5N+IeMubp14ZUKUUw7yeJbkjtosvHiP0AZ5c8IAgEMk79D0eC8F23r4Ac/q8cAIFdm2FtyoA==",
"license": "MIT", "license": "MIT",
"bin": { "dependencies": {
"marked": "bin/marked.js" "argparse": "^2.0.1",
"entities": "^4.4.0",
"linkify-it": "^5.0.0",
"mdurl": "^2.0.0",
"punycode.js": "^2.3.1",
"uc.micro": "^2.1.0"
}, },
"engines": { "bin": {
"node": ">= 20" "markdown-it": "bin/markdown-it.mjs"
} }
}, },
"node_modules/mdurl": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz",
"integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==",
"license": "MIT"
},
"node_modules/merge2": { "node_modules/merge2": {
"version": "1.4.1", "version": "1.4.1",
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
@ -3734,6 +3802,15 @@
"node": ">=6" "node": ">=6"
} }
}, },
"node_modules/punycode.js": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz",
"integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==",
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/queue-microtask": { "node_modules/queue-microtask": {
"version": "1.2.3", "version": "1.2.3",
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
@ -4326,6 +4403,12 @@
"node": ">=14.17" "node": ">=14.17"
} }
}, },
"node_modules/uc.micro": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz",
"integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==",
"license": "MIT"
},
"node_modules/undici-types": { "node_modules/undici-types": {
"version": "6.21.0", "version": "6.21.0",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
@ -4460,6 +4543,27 @@
"dev": true, "dev": true,
"license": "ISC" "license": "ISC"
}, },
"node_modules/ws": {
"version": "8.19.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz",
"integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==",
"license": "MIT",
"engines": {
"node": ">=10.0.0"
},
"peerDependencies": {
"bufferutil": "^4.0.1",
"utf-8-validate": ">=5.0.2"
},
"peerDependenciesMeta": {
"bufferutil": {
"optional": true
},
"utf-8-validate": {
"optional": true
}
}
},
"node_modules/yocto-queue": { "node_modules/yocto-queue": {
"version": "0.1.0", "version": "0.1.0",
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",

7
package.json

@ -21,13 +21,16 @@
"@sveltejs/vite-plugin-svelte": "^4.0.0", "@sveltejs/vite-plugin-svelte": "^4.0.0",
"codemirror": "^6.0.2", "codemirror": "^6.0.2",
"codemirror-asciidoc": "^2.0.1", "codemirror-asciidoc": "^2.0.1",
"marked": "^17.0.2", "highlight.js": "^11.10.0",
"markdown-it": "^14.1.0",
"nostr-tools": "^2.22.1", "nostr-tools": "^2.22.1",
"simple-git": "^3.31.1", "simple-git": "^3.31.1",
"svelte": "^5.0.0" "svelte": "^5.0.0",
"ws": "^8.19.0"
}, },
"devDependencies": { "devDependencies": {
"@sveltejs/adapter-node": "^5.0.0", "@sveltejs/adapter-node": "^5.0.0",
"@types/markdown-it": "^14.1.2",
"@types/node": "^20.0.0", "@types/node": "^20.0.0",
"@typescript-eslint/eslint-plugin": "^6.0.0", "@typescript-eslint/eslint-plugin": "^6.0.0",
"@typescript-eslint/parser": "^6.0.0", "@typescript-eslint/parser": "^6.0.0",

34
src/lib/services/nostr/nostr-client.ts

@ -3,6 +3,34 @@
*/ */
import type { NostrEvent, NostrFilter } from '../../types/nostr.js'; import type { NostrEvent, NostrFilter } from '../../types/nostr.js';
import { createRequire } from 'module';
// Polyfill WebSocket for Node.js environments (lazy initialization)
let wsPolyfillInitialized = false;
function initializeWebSocketPolyfill() {
if (wsPolyfillInitialized) return;
if (typeof global === 'undefined' || typeof global.WebSocket !== 'undefined') {
wsPolyfillInitialized = true;
return;
}
try {
// Use createRequire for ES modules compatibility
const requireFunc = createRequire(import.meta.url);
const WebSocketImpl = requireFunc('ws');
global.WebSocket = WebSocketImpl as any;
wsPolyfillInitialized = true;
} catch {
// ws package not available, will fail at runtime in Node.js
console.warn('WebSocket polyfill not available. Install "ws" package for Node.js support.');
wsPolyfillInitialized = true; // Mark as initialized to avoid repeated warnings
}
}
// Initialize on module load if in Node.js
if (typeof process !== 'undefined' && process.versions?.node) {
initializeWebSocketPolyfill();
}
export class NostrClient { export class NostrClient {
private relays: string[] = []; private relays: string[] = [];
@ -36,6 +64,9 @@ export class NostrClient {
} }
private async fetchFromRelay(relay: string, filters: NostrFilter[]): Promise<NostrEvent[]> { private async fetchFromRelay(relay: string, filters: NostrFilter[]): Promise<NostrEvent[]> {
// Ensure WebSocket polyfill is initialized
initializeWebSocketPolyfill();
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const ws = new WebSocket(relay); const ws = new WebSocket(relay);
const events: NostrEvent[] = []; const events: NostrEvent[] = [];
@ -131,6 +162,9 @@ export class NostrClient {
} }
private async publishToRelay(relay: string, nostrEvent: NostrEvent): Promise<void> { private async publishToRelay(relay: string, nostrEvent: NostrEvent): Promise<void> {
// Ensure WebSocket polyfill is initialized
initializeWebSocketPolyfill();
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const ws = new WebSocket(relay); const ws = new WebSocket(relay);
let resolved = false; let resolved = false;

5
src/routes/api/git/[...path]/+server.ts

@ -25,8 +25,11 @@ const ownershipTransferService = new OwnershipTransferService(DEFAULT_NOSTR_RELA
const maintainerService = new MaintainerService(DEFAULT_NOSTR_RELAYS); const maintainerService = new MaintainerService(DEFAULT_NOSTR_RELAYS);
// Path to git-http-backend (common locations) // Path to git-http-backend (common locations)
// Alpine Linux: /usr/lib/git-core/git-http-backend
// Debian/Ubuntu: /usr/lib/git-core/git-http-backend
// macOS: /usr/local/libexec/git-core/git-http-backend or /opt/homebrew/libexec/git-core/git-http-backend
const GIT_HTTP_BACKEND_PATHS = [ const GIT_HTTP_BACKEND_PATHS = [
'/usr/lib/git-core/git-http-backend', '/usr/lib/git-core/git-http-backend', // Alpine, Debian, Ubuntu
'/usr/libexec/git-core/git-http-backend', '/usr/libexec/git-core/git-http-backend',
'/usr/local/libexec/git-core/git-http-backend', '/usr/local/libexec/git-core/git-http-backend',
'/opt/homebrew/libexec/git-core/git-http-backend' '/opt/homebrew/libexec/git-core/git-http-backend'

20
src/routes/docs/nip34/+page.svelte

@ -10,8 +10,24 @@
try { try {
const docContent = $page.data.content; const docContent = $page.data.content;
if (docContent) { if (docContent) {
const { marked } = await import('marked'); const MarkdownIt = (await import('markdown-it')).default;
content = marked.parse(docContent) as string; const hljsModule = await import('highlight.js');
const hljs = hljsModule.default || hljsModule;
const md: any = new MarkdownIt({
highlight: function (str: string, lang: string): string {
if (lang && hljs.getLanguage(lang)) {
try {
return '<pre class="hljs"><code>' +
hljs.highlight(str, { language: lang }).value +
'</code></pre>';
} catch (__) {}
}
return '<pre class="hljs"><code>' + md.utils.escapeHtml(str) + '</code></pre>';
}
});
content = md.render(docContent);
} else { } else {
error = $page.data.error || 'Failed to load NIP-34 documentation'; error = $page.data.error || 'Failed to load NIP-34 documentation';
} }

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

@ -101,6 +101,7 @@
let readmeIsMarkdown = $state(false); let readmeIsMarkdown = $state(false);
let loadingReadme = $state(false); let loadingReadme = $state(false);
let readmeHtml = $state<string>(''); let readmeHtml = $state<string>('');
let highlightedFileContent = $state<string>('');
// Fork // Fork
let forkInfo = $state<{ isFork: boolean; originalRepo: { npub: string; repo: string } | null } | null>(null); let forkInfo = $state<{ isFork: boolean; originalRepo: { npub: string; repo: string } | null } | null>(null);
@ -123,8 +124,24 @@
// Render markdown if needed // Render markdown if needed
if (readmeIsMarkdown && readmeContent) { if (readmeIsMarkdown && readmeContent) {
const { marked } = await import('marked'); const MarkdownIt = (await import('markdown-it')).default;
readmeHtml = marked.parse(readmeContent) as string; const hljsModule = await import('highlight.js');
const hljs = hljsModule.default || hljsModule;
const md: any = new MarkdownIt({
highlight: function (str: string, lang: string): string {
if (lang && hljs.getLanguage(lang)) {
try {
return '<pre class="hljs"><code>' +
hljs.highlight(str, { language: lang }).value +
'</code></pre>';
} catch (__) {}
}
return '<pre class="hljs"><code>' + md.utils.escapeHtml(str) + '</code></pre>';
}
});
readmeHtml = md.render(readmeContent);
} }
} }
} }
@ -135,6 +152,145 @@
} }
} }
// Map file extensions to highlight.js language names
function getHighlightLanguage(ext: string): string {
const langMap: Record<string, string> = {
'js': 'javascript',
'ts': 'typescript',
'jsx': 'javascript',
'tsx': 'typescript',
'json': 'json',
'css': 'css',
'html': 'xml',
'xml': 'xml',
'yaml': 'yaml',
'yml': 'yaml',
'py': 'python',
'rb': 'ruby',
'go': 'go',
'rs': 'rust',
'java': 'java',
'c': 'c',
'cpp': 'cpp',
'h': 'c',
'hpp': 'cpp',
'sh': 'bash',
'bash': 'bash',
'zsh': 'bash',
'sql': 'sql',
'php': 'php',
'swift': 'swift',
'kt': 'kotlin',
'scala': 'scala',
'r': 'r',
'm': 'objectivec',
'mm': 'objectivec',
'vue': 'xml',
'svelte': 'xml',
'dockerfile': 'dockerfile',
'toml': 'toml',
'ini': 'ini',
'conf': 'ini',
'log': 'plaintext',
'txt': 'plaintext',
'adoc': 'asciidoc',
'asciidoc': 'asciidoc',
'ad': 'asciidoc',
};
return langMap[ext.toLowerCase()] || 'plaintext';
}
async function applySyntaxHighlighting(content: string, ext: string) {
try {
const hljsModule = await import('highlight.js');
// highlight.js v11+ uses default export
const hljs = hljsModule.default || hljsModule;
const lang = getHighlightLanguage(ext);
// Register AsciiDoc language if needed (not in highlight.js by default)
if (lang === 'asciidoc' && !hljs.getLanguage('asciidoc')) {
hljs.registerLanguage('asciidoc', function(hljs: any) {
return {
name: 'AsciiDoc',
aliases: ['adoc', 'asciidoc', 'ad'],
contains: [
// Headers
{
className: 'section',
begin: /^={1,6}\s+/,
relevance: 10
},
// Bold
{
className: 'strong',
begin: /\*\*[^*]+\*\*/,
relevance: 0
},
// Italic
{
className: 'emphasis',
begin: /_[^_]+_/,
relevance: 0
},
// Inline code
{
className: 'code',
begin: /`[^`]+`/,
relevance: 0
},
// Code blocks
{
className: 'code',
begin: /^----+$/,
end: /^----+$/,
contains: [{ begin: /./ }]
},
// Lists
{
className: 'bullet',
begin: /^(\*+|\.+|-+)\s+/,
relevance: 0
},
// Links
{
className: 'link',
begin: /link:/,
end: /\[/,
contains: [{ begin: /\[/, end: /\]/ }]
},
// Comments
{
className: 'comment',
begin: /^\/\/.*$/,
relevance: 0
},
// Attributes
{
className: 'attr',
begin: /^:.*:$/,
relevance: 0
}
]
};
});
}
// Apply highlighting
if (lang === 'plaintext') {
highlightedFileContent = `<pre><code class="hljs">${hljs.highlight(content, { language: 'plaintext' }).value}</code></pre>`;
} else if (hljs.getLanguage(lang)) {
highlightedFileContent = `<pre><code class="hljs language-${lang}">${hljs.highlight(content, { language: lang }).value}</code></pre>`;
} else {
// Fallback to auto-detection
highlightedFileContent = `<pre><code class="hljs">${hljs.highlightAuto(content).value}</code></pre>`;
}
} catch (err) {
console.error('Error applying syntax highlighting:', err);
// Fallback to plain text
highlightedFileContent = `<pre><code class="hljs">${content}</code></pre>`;
}
}
async function loadForkInfo() { async function loadForkInfo() {
try { try {
const response = await fetch(`/api/repos/${npub}/${repo}/fork`); const response = await fetch(`/api/repos/${npub}/${repo}/fork`);
@ -387,6 +543,17 @@
} else { } else {
fileLanguage = 'text'; fileLanguage = 'text';
} }
// Apply syntax highlighting for read-only view (non-maintainers)
if (fileContent && !isMaintainer) {
await applySyntaxHighlighting(fileContent, ext || '');
}
// Apply syntax highlighting to file content if not in editor
if (fileContent && !isMaintainer) {
// For read-only view, apply highlight.js
await applySyntaxHighlighting(fileContent, ext || '');
}
} catch (err) { } catch (err) {
error = err instanceof Error ? err.message : 'Failed to load file'; error = err instanceof Error ? err.message : 'Failed to load file';
console.error('Error loading file:', err); console.error('Error loading file:', err);
@ -1211,7 +1378,9 @@
{@html readmeHtml} {@html readmeHtml}
</div> </div>
{:else if readmeContent} {:else if readmeContent}
<pre class="readme-content"><code>{readmeContent}</code></pre> <div class="readme-content">
<pre><code class="hljs language-text">{readmeContent}</code></pre>
</div>
{/if} {/if}
</div> </div>
{/if} {/if}
@ -1245,7 +1414,11 @@
/> />
{:else} {:else}
<div class="read-only-editor"> <div class="read-only-editor">
<pre><code>{editedContent}</code></pre> {#if highlightedFileContent}
{@html highlightedFileContent}
{:else}
<pre><code class="hljs">{fileContent}</code></pre>
{/if}
</div> </div>
{/if} {/if}
</div> </div>
@ -2236,6 +2409,42 @@
white-space: pre; white-space: pre;
} }
.read-only-editor {
height: 100%;
overflow: auto;
}
.read-only-editor :global(.hljs) {
padding: 1rem;
background: #1e1e1e;
color: #d4d4d4;
border-radius: 4px;
overflow-x: auto;
margin: 0;
}
.read-only-editor :global(pre) {
margin: 0;
padding: 0;
}
.read-only-editor :global(code) {
font-family: 'Courier New', Courier, monospace;
font-size: 14px;
line-height: 1.5;
}
.readme-content :global(.hljs) {
background: #f5f5f5;
padding: 1rem;
border-radius: 4px;
overflow-x: auto;
}
.readme-content :global(pre.hljs) {
margin: 1rem 0;
}
/* Issues and PRs */ /* Issues and PRs */
.issues-sidebar, .prs-sidebar { .issues-sidebar, .prs-sidebar {
width: 300px; width: 300px;

Loading…
Cancel
Save