Compare commits

2 Commits

5 changed files with 299 additions and 12 deletions
+6
View File
@@ -7,6 +7,12 @@
"core:event:default", "core:event:default",
"core:window:default", "core:window:default",
"core:window:allow-set-title", "core:window:allow-set-title",
"core:window:allow-start-dragging",
"core:window:allow-minimize",
"core:window:allow-maximize",
"core:window:allow-unmaximize",
"core:window:allow-is-maximized",
"core:window:allow-close",
"core:app:default", "core:app:default",
"core:webview:default", "core:webview:default",
"fs:read-all", "fs:read-all",
+2 -1
View File
@@ -13,7 +13,8 @@
"label": "main", "label": "main",
"title": "Pena — Markdown Viewer", "title": "Pena — Markdown Viewer",
"width": 1280, "width": 1280,
"height": 800 "height": 800,
"decorations": false
} }
], ],
"security": { "security": {
+25
View File
@@ -11,6 +11,15 @@
</head> </head>
<body> <body>
<div id="titlebar">
<span id="titlebar-title">Pena — Markdown Viewer</span>
<div class="titlebar-controls">
<button id="titlebar-minimize" title="Réduire"></button>
<button id="titlebar-maximize" title="Agrandir"></button>
<button id="titlebar-close" title="Fermer"></button>
</div>
</div>
<div id="view-home"> <div id="view-home">
<h1 class="home-title">Pena</h1> <h1 class="home-title">Pena</h1>
<div class="home-buttons"> <div class="home-buttons">
@@ -30,7 +39,23 @@
<div id="content" class="content"> <div id="content" class="content">
<!-- contenu généré par JS --> <!-- contenu généré par JS -->
</div> </div>
<button id="btn-customize-css" class="btn-customize-css">
<span class="btn-customize-css__icon"></span>
Personnaliser le CSS
</button>
<button id="btn-back" class="btn-back">← Accueil</button> <button id="btn-back" class="btn-back">← Accueil</button>
<div id="css-editor-panel" class="css-editor-panel hidden">
<div class="css-editor-panel__header">
<span>CSS personnalisé</span>
<button id="btn-css-close" class="css-editor-panel__close"></button>
</div>
<textarea id="css-editor" class="css-editor__textarea" placeholder="/* Entrez votre CSS personnalisé ici */&#10;/* Exemple : .content { font-size: 18px; } */"></textarea>
<div class="css-editor-panel__footer">
<button id="btn-css-reset" class="css-editor-panel__btn css-editor-panel__btn--secondary">Réinitialiser</button>
<button id="btn-css-apply" class="css-editor-panel__btn css-editor-panel__btn--primary">Appliquer</button>
</div>
</div>
</div> </div>
<script src="main.js" type="module"></script> <script src="main.js" type="module"></script>
+77 -3
View File
@@ -1,6 +1,21 @@
let currentPath = null; let currentPath = null;
let currentMode = null; // 'file' | 'dir' 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 viewHome = document.getElementById('view-home');
const viewReader = document.getElementById('view-reader'); const viewReader = document.getElementById('view-reader');
const sidebar = document.querySelector('#sidebar'); const sidebar = document.querySelector('#sidebar');
@@ -165,9 +180,10 @@ async function loadPage(filePath) {
const basename = filePath.split('/').pop().split('\\').pop(); const basename = filePath.split('/').pop().split('\\').pop();
const title = basename.replace(/\.md$/i, ''); 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 => { sidebar.querySelectorAll('a').forEach(a => {
a.classList.toggle('active', a.dataset.path === filePath); a.classList.toggle('active', a.dataset.path === filePath);
}); });
@@ -191,10 +207,19 @@ async function openPath(path, mode) {
saveRecent(path, mode); saveRecent(path, mode);
if (mode === 'file') { if (mode === 'file') {
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.innerHTML = '';
sidebar.classList.add('hidden'); sidebar.classList.add('hidden');
content.style.marginLeft = '0'; content.style.marginLeft = '0';
showReader(); }
await loadPage(path); await loadPage(path);
return; return;
} }
@@ -243,5 +268,54 @@ document.getElementById('btn-back').addEventListener('click', () => {
currentPath = null; currentPath = null;
currentMode = null; currentMode = null;
sidebar.innerHTML = ''; sidebar.innerHTML = '';
win.setTitle('Pena — Markdown Viewer');
titlebarTitle.textContent = 'Pena — Markdown Viewer';
showHome(); 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);
});
+187 -6
View File
@@ -11,6 +11,64 @@ body {
overflow-x: hidden; 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 ── */ /* ── Utility ── */
.hidden { display: none !important; } .hidden { display: none !important; }
@@ -21,6 +79,7 @@ body {
align-items: center; align-items: center;
justify-content: center; justify-content: center;
height: 100vh; height: 100vh;
padding-top: 36px;
background: #1a1b2e; background: #1a1b2e;
} }
@@ -137,10 +196,10 @@ body {
/* ── Sidebar ── */ /* ── Sidebar ── */
.sidebar { .sidebar {
position: fixed; position: fixed;
top: 0; top: 36px;
left: 0; left: 0;
width: 264px; width: 264px;
height: 100vh; height: calc(100vh - 36px);
background: #1a1b2e; background: #1a1b2e;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@@ -148,7 +207,7 @@ body {
overflow: hidden; overflow: hidden;
} }
.sidebar__title-block { .sidebar__title-block {
margin: 48px 24px 8px; margin: 32px 24px 8px;
flex-shrink: 0; flex-shrink: 0;
} }
.sidebar__title-block a { .sidebar__title-block a {
@@ -320,7 +379,10 @@ body {
/* ── Content ── */ /* ── Content ── */
.content { .content {
margin-left: 264px; margin-left: 264px;
padding: 48px 72px 96px; padding-top: 84px;
padding-right: 72px;
padding-bottom: 96px;
padding-left: 72px;
max-width: 1080px; max-width: 1080px;
min-height: 100vh; min-height: 100vh;
background: #fff; background: #fff;
@@ -395,12 +457,131 @@ body {
} }
.content ul li code, .content ol li code { font-weight: 700; } .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 ── */ /* ── Back button ── */
.btn-back { .btn-back {
position: fixed; position: fixed;
bottom: 24px; bottom: 24px;
left: 0; left: 0;
width: 264px; width: 264px;
z-index: 98;
background: transparent; background: transparent;
color: rgba(255,255,255,0.45); color: rgba(255,255,255,0.45);
border: none; border: none;
@@ -421,8 +602,8 @@ body {
left: -300px; left: -300px;
transition: left 0.2s cubic-bezier(0.09, 0.46, 0.45, 0.94); 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) { @media screen and (max-width: 540px) {
.content { padding: 24px 16px 48px; } .content { padding: 68px 16px 48px; }
} }