diff --git a/src/index.html b/src/index.html index f928ac8..0cfaea6 100644 --- a/src/index.html +++ b/src/index.html @@ -11,6 +11,15 @@ +
+ Pena — Markdown Viewer +
+ + + +
+
+

Pena

@@ -30,7 +39,23 @@
+ + +
diff --git a/src/main.js b/src/main.js index a73f1ee..78f6782 100644 --- a/src/main.js +++ b/src/main.js @@ -1,6 +1,21 @@ let currentPath = null; let currentMode = null; // 'file' | 'dir' +const titlebarTitle = document.getElementById('titlebar-title'); +const win = window.__TAURI__.window.getCurrentWindow(); + +document.getElementById('titlebar').addEventListener('mousedown', (e) => { + if (e.target.closest('.titlebar-controls')) return; + if (e.buttons === 1) win.startDragging(); +}); + +document.getElementById('titlebar-minimize').addEventListener('click', () => win.minimize()); +document.getElementById('titlebar-maximize').addEventListener('click', async () => { + if (await win.isMaximized()) win.unmaximize(); + else win.maximize(); +}); +document.getElementById('titlebar-close').addEventListener('click', () => win.close()); + const viewHome = document.getElementById('view-home'); const viewReader = document.getElementById('view-reader'); const sidebar = document.querySelector('#sidebar'); @@ -165,9 +180,10 @@ async function loadPage(filePath) { const basename = filePath.split('/').pop().split('\\').pop(); const title = basename.replace(/\.md$/i, ''); - await window.__TAURI__.window.getCurrentWindow().setTitle(title); + await win.setTitle(title); + titlebarTitle.textContent = title; - if (currentMode === 'dir') { + if (currentMode === 'dir' || currentMode === 'file') { sidebar.querySelectorAll('a').forEach(a => { a.classList.toggle('active', a.dataset.path === filePath); }); @@ -191,10 +207,19 @@ async function openPath(path, mode) { saveRecent(path, mode); if (mode === 'file') { - sidebar.innerHTML = ''; - sidebar.classList.add('hidden'); - content.style.marginLeft = '0'; showReader(); + const sep = path.includes('\\') ? '\\' : '/'; + const parentDir = path.split(sep).slice(0, -1).join(sep); + try { + const files = await window.__TAURI__.core.invoke('list_md_files', { dir: parentDir }); + sidebar.classList.remove('hidden'); + content.style.marginLeft = ''; + renderSidebar(parentDir, files, path); + } catch { + sidebar.innerHTML = ''; + sidebar.classList.add('hidden'); + content.style.marginLeft = '0'; + } await loadPage(path); return; } @@ -243,5 +268,54 @@ document.getElementById('btn-back').addEventListener('click', () => { currentPath = null; currentMode = null; sidebar.innerHTML = ''; + win.setTitle('Pena — Markdown Viewer'); + titlebarTitle.textContent = 'Pena — Markdown Viewer'; showHome(); }); + +// ── CSS personnalisé ── +const CSS_STORAGE_KEY = 'pena_custom_css'; + +function applyCustomCss(css) { + let styleEl = document.getElementById('pena-custom-style'); + if (!styleEl) { + styleEl = document.createElement('style'); + styleEl.id = 'pena-custom-style'; + document.head.appendChild(styleEl); + } + styleEl.textContent = css; +} + +function loadCustomCss() { + return localStorage.getItem(CSS_STORAGE_KEY) ?? ''; +} + +const savedCss = loadCustomCss(); +if (savedCss) applyCustomCss(savedCss); + +const btnCustomizeCss = document.getElementById('btn-customize-css'); +const cssEditorPanel = document.getElementById('css-editor-panel'); +const cssEditor = document.getElementById('css-editor'); + +cssEditor.value = loadCustomCss(); + +btnCustomizeCss.addEventListener('click', () => { + cssEditorPanel.classList.toggle('hidden'); +}); + +document.getElementById('btn-css-close').addEventListener('click', () => { + cssEditorPanel.classList.add('hidden'); +}); + +document.getElementById('btn-css-apply').addEventListener('click', () => { + const css = cssEditor.value.trim(); + applyCustomCss(css); + localStorage.setItem(CSS_STORAGE_KEY, css); + cssEditorPanel.classList.add('hidden'); +}); + +document.getElementById('btn-css-reset').addEventListener('click', () => { + cssEditor.value = ''; + applyCustomCss(''); + localStorage.removeItem(CSS_STORAGE_KEY); +}); diff --git a/src/style.css b/src/style.css index e0a6304..4102cf1 100644 --- a/src/style.css +++ b/src/style.css @@ -11,6 +11,64 @@ body { overflow-x: hidden; } +/* ── Titlebar ── */ +#titlebar { + position: fixed; + top: 0; + left: 0; + right: 0; + height: 36px; + background: #1a1b2e; + display: flex; + align-items: center; + justify-content: center; + z-index: 200; + user-select: none; + -webkit-user-select: none; +} + +#titlebar-title { + color: rgba(255,255,255,0.5); + font-size: 12px; + font-weight: 500; + letter-spacing: 0.02em; + pointer-events: none; +} + +.titlebar-controls { + position: absolute; + right: 0; + top: 0; + height: 36px; + display: flex; + align-items: center; +} + +.titlebar-controls button { + width: 46px; + height: 36px; + background: transparent; + border: none; + color: rgba(255,255,255,0.45); + font-size: 13px; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + transition: background 0.15s, color 0.15s; + font-family: inherit; +} + +.titlebar-controls button:hover { + background: rgba(255,255,255,0.08); + color: rgba(255,255,255,0.9); +} + +#titlebar-close:hover { + background: #e81123 !important; + color: #fff !important; +} + /* ── Utility ── */ .hidden { display: none !important; } @@ -21,6 +79,7 @@ body { align-items: center; justify-content: center; height: 100vh; + padding-top: 36px; background: #1a1b2e; } @@ -137,10 +196,10 @@ body { /* ── Sidebar ── */ .sidebar { position: fixed; - top: 0; + top: 36px; left: 0; width: 264px; - height: 100vh; + height: calc(100vh - 36px); background: #1a1b2e; display: flex; flex-direction: column; @@ -148,7 +207,7 @@ body { overflow: hidden; } .sidebar__title-block { - margin: 48px 24px 8px; + margin: 32px 24px 8px; flex-shrink: 0; } .sidebar__title-block a { @@ -320,7 +379,10 @@ body { /* ── Content ── */ .content { margin-left: 264px; - padding: 48px 72px 96px; + padding-top: 84px; + padding-right: 72px; + padding-bottom: 96px; + padding-left: 72px; max-width: 1080px; min-height: 100vh; background: #fff; @@ -395,12 +457,131 @@ body { } .content ul li code, .content ol li code { font-weight: 700; } +/* ── Customize CSS button ── */ +.btn-customize-css { + position: fixed; + bottom: 60px; + left: 12px; + width: 240px; + z-index: 100; + background: transparent; + color: rgba(255,255,255,0.7); + border: 1px solid rgba(255,255,255,0.15); + border-radius: 8px; + padding: 8px 16px; + font-family: 'Poppins', sans-serif; + font-size: 13px; + font-weight: 500; + cursor: pointer; + text-align: left; + display: flex; + align-items: center; + gap: 8px; + transition: color 0.2s, border-color 0.2s, background 0.2s; +} +.btn-customize-css:hover { + color: #fff; + border-color: rgba(255,255,255,0.35); + background: rgba(255,255,255,0.06); +} +.btn-customize-css__icon { + font-size: 15px; + opacity: 0.8; +} + +/* ── CSS editor panel ── */ +.css-editor-panel { + position: fixed; + bottom: 120px; + left: 12px; + width: 240px; + background: #12132a; + border: 1px solid rgba(255,255,255,0.15); + border-radius: 10px; + z-index: 101; + display: flex; + flex-direction: column; + box-shadow: 0 8px 32px rgba(0,0,0,0.5); + overflow: hidden; +} +.css-editor-panel__header { + display: flex; + align-items: center; + justify-content: space-between; + padding: 10px 14px; + border-bottom: 1px solid rgba(255,255,255,0.08); + color: rgba(255,255,255,0.7); + font-size: 12px; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.06em; +} +.css-editor-panel__close { + background: transparent; + border: none; + color: rgba(255,255,255,0.4); + font-size: 13px; + cursor: pointer; + padding: 2px 4px; + border-radius: 4px; + font-family: inherit; + transition: color 0.15s, background 0.15s; +} +.css-editor-panel__close:hover { + color: #fff; + background: rgba(255,255,255,0.08); +} +.css-editor__textarea { + width: 100%; + height: 180px; + background: #0e0f24; + border: none; + color: rgba(255,255,255,0.8); + font-family: 'Roboto Mono', monospace; + font-size: 11px; + line-height: 1.6; + padding: 12px; + resize: none; + outline: none; +} +.css-editor__textarea::placeholder { + color: rgba(255,255,255,0.2); +} +.css-editor-panel__footer { + display: flex; + gap: 8px; + padding: 10px 12px; + border-top: 1px solid rgba(255,255,255,0.08); +} +.css-editor-panel__btn { + flex: 1; + padding: 6px 10px; + border: none; + border-radius: 6px; + font-family: 'Poppins', sans-serif; + font-size: 12px; + font-weight: 600; + cursor: pointer; + transition: background 0.15s; +} +.css-editor-panel__btn--primary { + background: #ff5577; + color: #fff; +} +.css-editor-panel__btn--primary:hover { background: #ff3355; } +.css-editor-panel__btn--secondary { + background: rgba(255,255,255,0.08); + color: rgba(255,255,255,0.7); +} +.css-editor-panel__btn--secondary:hover { background: rgba(255,255,255,0.14); } + /* ── Back button ── */ .btn-back { position: fixed; bottom: 24px; left: 0; width: 264px; + z-index: 98; background: transparent; color: rgba(255,255,255,0.45); border: none; @@ -421,8 +602,8 @@ body { left: -300px; transition: left 0.2s cubic-bezier(0.09, 0.46, 0.45, 0.94); } - .content { margin-left: 0; padding: 32px 24px 64px; } + .content { margin-left: 0; padding: 68px 24px 64px; } } @media screen and (max-width: 540px) { - .content { padding: 24px 16px 48px; } + .content { padding: 68px 16px 48px; } }