🔧 Améliorations navigation et historique
- Scroll précis vers sections table des matières avec surbrillance - Historique Ctrl+Z/Y fonctionnel pour toutes les actions IA - Mode édition automatique lors du chargement de fichiers - Support CSS GitHub Preview pour visualisation Markdown 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
a539adb6d1
commit
b85e6d3a25
454
assets/css/github-preview.css
Normal file
454
assets/css/github-preview.css
Normal file
@ -0,0 +1,454 @@
|
|||||||
|
/* CSS pour la prévisualisation du journal - Style GitHub authentique */
|
||||||
|
|
||||||
|
.markdown-preview {
|
||||||
|
/* Variables spécifiques GitHub */
|
||||||
|
--github-text-color: #1f2328;
|
||||||
|
--github-text-light: #656d76;
|
||||||
|
--github-bg-color: #ffffff;
|
||||||
|
--github-border-color: #d0d7de;
|
||||||
|
--github-border-muted: #d8dee4;
|
||||||
|
--github-accent-emphasis: #0969da;
|
||||||
|
--github-accent-fg: #0969da;
|
||||||
|
--github-neutral-muted: #afb8c1;
|
||||||
|
--github-canvas-subtle: #f6f8fa;
|
||||||
|
--github-danger-fg: #cf222e;
|
||||||
|
--github-success-fg: #1a7f37;
|
||||||
|
--github-attention-fg: #9a6700;
|
||||||
|
--github-severe-fg: #bc4c00;
|
||||||
|
--github-done-fg: #8250df;
|
||||||
|
|
||||||
|
/* Override pour thème sombre */
|
||||||
|
color: var(--github-text-color);
|
||||||
|
background: var(--github-bg-color);
|
||||||
|
border: 1px solid var(--github-border-color);
|
||||||
|
border-radius: 6px;
|
||||||
|
|
||||||
|
/* GitHub typography */
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji";
|
||||||
|
font-size: 16px;
|
||||||
|
line-height: 1.5;
|
||||||
|
word-wrap: break-word;
|
||||||
|
|
||||||
|
/* Layout comme GitHub */
|
||||||
|
max-width: none;
|
||||||
|
width: 100%;
|
||||||
|
padding: 48px;
|
||||||
|
margin: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Mode sombre GitHub */
|
||||||
|
body.dark-theme .markdown-preview {
|
||||||
|
--github-text-color: #e6edf3;
|
||||||
|
--github-text-light: #7d8590;
|
||||||
|
--github-bg-color: #0d1117;
|
||||||
|
--github-border-color: #30363d;
|
||||||
|
--github-border-muted: #21262d;
|
||||||
|
--github-accent-emphasis: #1f6feb;
|
||||||
|
--github-accent-fg: #58a6ff;
|
||||||
|
--github-neutral-muted: #6e7681;
|
||||||
|
--github-canvas-subtle: #161b22;
|
||||||
|
--github-danger-fg: #f85149;
|
||||||
|
--github-success-fg: #3fb950;
|
||||||
|
--github-attention-fg: #d29922;
|
||||||
|
--github-severe-fg: #db6d28;
|
||||||
|
--github-done-fg: #a5a3ff;
|
||||||
|
|
||||||
|
background: var(--github-bg-color);
|
||||||
|
color: var(--github-text-color);
|
||||||
|
border-color: var(--github-border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Titres style GitHub */
|
||||||
|
.markdown-preview h1,
|
||||||
|
.markdown-preview h2,
|
||||||
|
.markdown-preview h3,
|
||||||
|
.markdown-preview h4,
|
||||||
|
.markdown-preview h5,
|
||||||
|
.markdown-preview h6 {
|
||||||
|
margin-top: 24px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 1.25;
|
||||||
|
color: var(--github-text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-preview h1 {
|
||||||
|
font-size: 2em;
|
||||||
|
border-bottom: 1px solid var(--github-border-muted);
|
||||||
|
padding-bottom: 0.3em;
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-preview h2 {
|
||||||
|
font-size: 1.5em;
|
||||||
|
border-bottom: 1px solid var(--github-border-muted);
|
||||||
|
padding-bottom: 0.3em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-preview h3 {
|
||||||
|
font-size: 1.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-preview h4 {
|
||||||
|
font-size: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-preview h5 {
|
||||||
|
font-size: 0.875em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-preview h6 {
|
||||||
|
font-size: 0.85em;
|
||||||
|
color: var(--github-text-light);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Paragraphes */
|
||||||
|
.markdown-preview p {
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Listes style GitHub */
|
||||||
|
.markdown-preview ul,
|
||||||
|
.markdown-preview ol {
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
padding-left: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-preview li {
|
||||||
|
word-wrap: break-all;
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-preview li > p {
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-preview li + li {
|
||||||
|
margin-top: 0.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Listes de tâches */
|
||||||
|
.markdown-preview .task-list-item {
|
||||||
|
list-style-type: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-preview .task-list-item input[type="checkbox"] {
|
||||||
|
margin: 0 0.2em 0.25em -1.4em;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Citations */
|
||||||
|
.markdown-preview blockquote {
|
||||||
|
margin: 0 0 16px 0;
|
||||||
|
padding: 0 1em;
|
||||||
|
color: var(--github-text-light);
|
||||||
|
border-left: 0.25em solid var(--github-border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-preview blockquote > :first-child {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-preview blockquote > :last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Code inline */
|
||||||
|
.markdown-preview code {
|
||||||
|
padding: 0.2em 0.4em;
|
||||||
|
margin: 0;
|
||||||
|
font-size: 85%;
|
||||||
|
white-space: break-spaces;
|
||||||
|
background-color: var(--github-neutral-muted);
|
||||||
|
border-radius: 6px;
|
||||||
|
font-family: ui-monospace, SFMono-Regular, "SF Mono", Consolas, "Liberation Mono", Menlo, monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.dark-theme .markdown-preview code {
|
||||||
|
background-color: rgba(110, 118, 129, 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Blocs de code */
|
||||||
|
.markdown-preview pre {
|
||||||
|
padding: 16px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
background-color: var(--github-canvas-subtle);
|
||||||
|
border-radius: 6px;
|
||||||
|
overflow: auto;
|
||||||
|
font-size: 85%;
|
||||||
|
line-height: 1.45;
|
||||||
|
word-wrap: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.dark-theme .markdown-preview pre {
|
||||||
|
background-color: var(--github-canvas-subtle);
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-preview pre code {
|
||||||
|
display: inline;
|
||||||
|
max-width: auto;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
overflow: visible;
|
||||||
|
line-height: inherit;
|
||||||
|
word-wrap: normal;
|
||||||
|
background-color: transparent;
|
||||||
|
border: 0;
|
||||||
|
white-space: pre;
|
||||||
|
word-break: normal;
|
||||||
|
word-spacing: normal;
|
||||||
|
tab-size: 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tables style GitHub */
|
||||||
|
.markdown-preview table {
|
||||||
|
border-spacing: 0;
|
||||||
|
border-collapse: collapse;
|
||||||
|
display: table;
|
||||||
|
width: 100%;
|
||||||
|
overflow: auto;
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
border: 1px solid var(--github-border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-preview table th {
|
||||||
|
font-weight: 600;
|
||||||
|
background-color: var(--github-canvas-subtle);
|
||||||
|
border: 1px solid var(--github-border-color);
|
||||||
|
padding: 6px 13px;
|
||||||
|
text-align: left;
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.dark-theme .markdown-preview table th {
|
||||||
|
background-color: var(--github-canvas-subtle);
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-preview table td {
|
||||||
|
border: 1px solid var(--github-border-color);
|
||||||
|
padding: 6px 13px;
|
||||||
|
text-align: left;
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-preview table tr {
|
||||||
|
background-color: var(--github-bg-color);
|
||||||
|
border-top: 1px solid var(--github-border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-preview table tr:nth-child(even) {
|
||||||
|
background-color: var(--github-canvas-subtle);
|
||||||
|
}
|
||||||
|
|
||||||
|
body.dark-theme .markdown-preview table tr:nth-child(even) {
|
||||||
|
background-color: var(--github-canvas-subtle);
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-preview table thead tr {
|
||||||
|
background-color: var(--github-canvas-subtle);
|
||||||
|
}
|
||||||
|
|
||||||
|
body.dark-theme .markdown-preview table thead tr {
|
||||||
|
background-color: var(--github-canvas-subtle);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Liens style GitHub */
|
||||||
|
.markdown-preview a {
|
||||||
|
color: var(--github-accent-fg);
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-preview a:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-preview a:visited {
|
||||||
|
color: var(--github-done-fg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Images */
|
||||||
|
.markdown-preview img {
|
||||||
|
max-width: 100%;
|
||||||
|
box-sizing: content-box;
|
||||||
|
background-color: var(--github-bg-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-preview img[align=right] {
|
||||||
|
padding-left: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-preview img[align=left] {
|
||||||
|
padding-right: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Séparateurs horizontaux */
|
||||||
|
.markdown-preview hr {
|
||||||
|
height: 0.25em;
|
||||||
|
padding: 0;
|
||||||
|
margin: 24px 0;
|
||||||
|
background-color: var(--github-border-color);
|
||||||
|
border: 0;
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Emphasis */
|
||||||
|
.markdown-preview strong {
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-preview em {
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Strikethrough */
|
||||||
|
.markdown-preview del {
|
||||||
|
text-decoration: line-through;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Keyboard keys */
|
||||||
|
.markdown-preview kbd {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 3px 5px;
|
||||||
|
font-size: 11px;
|
||||||
|
line-height: 10px;
|
||||||
|
color: var(--github-text-light);
|
||||||
|
vertical-align: middle;
|
||||||
|
background-color: var(--github-canvas-subtle);
|
||||||
|
border: solid 1px var(--github-neutral-muted);
|
||||||
|
border-bottom-color: var(--github-neutral-muted);
|
||||||
|
border-radius: 6px;
|
||||||
|
box-shadow: inset 0 -1px 0 var(--github-neutral-muted);
|
||||||
|
font-family: ui-monospace, SFMono-Regular, "SF Mono", Consolas, "Liberation Mono", Menlo, monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Support pour les alertes GitHub (Notes, Tips, etc.) */
|
||||||
|
.markdown-preview .markdown-alert {
|
||||||
|
padding: 0.5rem 1em;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
color: inherit;
|
||||||
|
border-left: 0.25em solid var(--github-border-color);
|
||||||
|
background-color: var(--github-canvas-subtle);
|
||||||
|
border-radius: 0 6px 6px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-preview .markdown-alert > :first-child {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-preview .markdown-alert > :last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-preview .markdown-alert .markdown-alert-title {
|
||||||
|
display: flex;
|
||||||
|
font-weight: 500;
|
||||||
|
align-items: center;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-preview .markdown-alert.markdown-alert-note {
|
||||||
|
border-left-color: var(--github-accent-emphasis);
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-preview .markdown-alert.markdown-alert-tip {
|
||||||
|
border-left-color: var(--github-success-fg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-preview .markdown-alert.markdown-alert-important {
|
||||||
|
border-left-color: var(--github-done-fg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-preview .markdown-alert.markdown-alert-warning {
|
||||||
|
border-left-color: var(--github-attention-fg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-preview .markdown-alert.markdown-alert-caution {
|
||||||
|
border-left-color: var(--github-danger-fg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Support pour Mermaid avec style GitHub */
|
||||||
|
.markdown-preview .mermaid {
|
||||||
|
text-align: center;
|
||||||
|
margin: 16px 0;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-preview .mermaid svg {
|
||||||
|
max-width: 100%;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Support pour les footnotes */
|
||||||
|
.markdown-preview .footnotes {
|
||||||
|
font-size: 0.875em;
|
||||||
|
color: var(--github-text-light);
|
||||||
|
border-top: 1px solid var(--github-border-color);
|
||||||
|
margin-top: 24px;
|
||||||
|
padding-top: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-preview .footnotes ol {
|
||||||
|
padding-left: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-preview .footnotes li {
|
||||||
|
margin: 0.25rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Amélioration de l'espacement pour les éléments imbriqués */
|
||||||
|
.markdown-preview li p {
|
||||||
|
margin-bottom: 0;
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-preview li blockquote {
|
||||||
|
margin: 8px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-preview li ul,
|
||||||
|
.markdown-preview li ol {
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-preview ul ul,
|
||||||
|
.markdown-preview ul ol,
|
||||||
|
.markdown-preview ol ol,
|
||||||
|
.markdown-preview ol ul {
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Scrollbars GitHub-like (webkit only) */
|
||||||
|
.markdown-preview::-webkit-scrollbar {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-preview::-webkit-scrollbar-corner,
|
||||||
|
.markdown-preview::-webkit-scrollbar-track {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-preview::-webkit-scrollbar-thumb {
|
||||||
|
background-color: var(--github-neutral-muted);
|
||||||
|
border-radius: 8px;
|
||||||
|
border: 4px solid transparent;
|
||||||
|
background-clip: content-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-preview::-webkit-scrollbar-thumb:hover {
|
||||||
|
background-color: var(--github-text-light);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Responsive pour mobile */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.markdown-preview {
|
||||||
|
padding: 24px 16px;
|
||||||
|
}
|
||||||
|
}
|
293
assets/js/app.js
293
assets/js/app.js
@ -125,32 +125,42 @@ class ConceptionAssistant {
|
|||||||
this.showNotification('Retour en haut', 'success');
|
this.showNotification('Retour en haut', 'success');
|
||||||
}
|
}
|
||||||
|
|
||||||
saveState() {
|
saveState(immediate = false) {
|
||||||
|
// Si immediate est true, sauvegarder immédiatement sans debounce
|
||||||
|
if (immediate) {
|
||||||
|
this.performSaveState();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Éviter de sauvegarder trop souvent (debounce)
|
// Éviter de sauvegarder trop souvent (debounce)
|
||||||
if (this.saveStateTimer) {
|
if (this.saveStateTimer) {
|
||||||
clearTimeout(this.saveStateTimer);
|
clearTimeout(this.saveStateTimer);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.saveStateTimer = setTimeout(() => {
|
this.saveStateTimer = setTimeout(() => {
|
||||||
const currentContent = this.editor.innerText;
|
this.performSaveState();
|
||||||
|
|
||||||
// Ne pas sauvegarder si le contenu n'a pas changé
|
|
||||||
if (this.undoStack.length > 0 && this.undoStack[this.undoStack.length - 1] === currentContent) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.undoStack.push(currentContent);
|
|
||||||
|
|
||||||
// Limiter la pile d'annulation à 50 éléments
|
|
||||||
if (this.undoStack.length > 50) {
|
|
||||||
this.undoStack.shift();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Vider la pile de refaire car on a fait une nouvelle action
|
|
||||||
this.redoStack = [];
|
|
||||||
}, 1000); // Sauvegarder après 1 seconde d'inactivité
|
}, 1000); // Sauvegarder après 1 seconde d'inactivité
|
||||||
}
|
}
|
||||||
|
|
||||||
|
performSaveState() {
|
||||||
|
const currentContent = this.editor.innerText;
|
||||||
|
|
||||||
|
// Ne pas sauvegarder si le contenu n'a pas changé
|
||||||
|
if (this.undoStack.length > 0 && this.undoStack[this.undoStack.length - 1] === currentContent) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.undoStack.push(currentContent);
|
||||||
|
|
||||||
|
// Limiter la pile d'annulation à 50 éléments
|
||||||
|
if (this.undoStack.length > 50) {
|
||||||
|
this.undoStack.shift();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vider la pile de refaire car on a fait une nouvelle action
|
||||||
|
this.redoStack = [];
|
||||||
|
}
|
||||||
|
|
||||||
undo() {
|
undo() {
|
||||||
if (this.undoStack.length > 1) {
|
if (this.undoStack.length > 1) {
|
||||||
const currentContent = this.undoStack.pop();
|
const currentContent = this.undoStack.pop();
|
||||||
@ -262,6 +272,14 @@ class ConceptionAssistant {
|
|||||||
this.currentJournalId = id;
|
this.currentJournalId = id;
|
||||||
this.editor.innerText = journal.markdownContent;
|
this.editor.innerText = journal.markdownContent;
|
||||||
this.generateTOC();
|
this.generateTOC();
|
||||||
|
|
||||||
|
// Réinitialiser l'historique pour le nouveau journal
|
||||||
|
this.undoStack = [journal.markdownContent];
|
||||||
|
this.redoStack = [];
|
||||||
|
|
||||||
|
// S'assurer que l'éditeur est en mode édition
|
||||||
|
this.ensureEditMode();
|
||||||
|
|
||||||
this.showNotification('Journal chargé', 'success');
|
this.showNotification('Journal chargé', 'success');
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -336,6 +354,14 @@ class ConceptionAssistant {
|
|||||||
this.editor.innerText = '';
|
this.editor.innerText = '';
|
||||||
this.generateTOC();
|
this.generateTOC();
|
||||||
this.clearFeedback();
|
this.clearFeedback();
|
||||||
|
|
||||||
|
// Réinitialiser l'historique pour le nouveau journal
|
||||||
|
this.undoStack = [''];
|
||||||
|
this.redoStack = [];
|
||||||
|
|
||||||
|
// S'assurer que l'éditeur est en mode édition
|
||||||
|
this.ensureEditMode();
|
||||||
|
|
||||||
this.showNotification('Nouveau journal créé', 'success');
|
this.showNotification('Nouveau journal créé', 'success');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -441,64 +467,134 @@ class ConceptionAssistant {
|
|||||||
}
|
}
|
||||||
|
|
||||||
scrollToHeading(title) {
|
scrollToHeading(title) {
|
||||||
const content = this.editor.innerText;
|
try {
|
||||||
const lines = content.split('\n');
|
// Méthode plus simple et robuste : chercher le texte directement dans l'éditeur
|
||||||
|
const content = this.editor.innerText;
|
||||||
|
const lines = content.split('\n');
|
||||||
|
|
||||||
// Chercher la ligne qui correspond au titre
|
// Trouver l'index de la ligne correspondant au titre
|
||||||
for (let i = 0; i < lines.length; i++) {
|
let targetLineIndex = -1;
|
||||||
const line = lines[i].trim();
|
for (let i = 0; i < lines.length; i++) {
|
||||||
if (line.startsWith('#') && line.replace(/^#+\s*/, '') === title) {
|
const line = lines[i].trim();
|
||||||
try {
|
if (line.startsWith('#') && line.replace(/^#+\s*/, '') === title) {
|
||||||
// Approche plus simple : utiliser un élément temporaire avec une position spécifique
|
targetLineIndex = i;
|
||||||
const tempElement = document.createElement('div');
|
break;
|
||||||
tempElement.style.position = 'absolute';
|
|
||||||
tempElement.style.visibility = 'hidden';
|
|
||||||
tempElement.style.height = '1px';
|
|
||||||
|
|
||||||
// Calculer la position approximative en fonction de la ligne
|
|
||||||
const editorRect = this.editor.getBoundingClientRect();
|
|
||||||
const computedStyle = window.getComputedStyle(this.editor);
|
|
||||||
const lineHeight = parseInt(computedStyle.lineHeight) || 20;
|
|
||||||
|
|
||||||
// Insérer l'élément temporaire dans l'éditeur
|
|
||||||
this.editor.appendChild(tempElement);
|
|
||||||
|
|
||||||
// Calculer la position de scroll basée sur le numéro de ligne
|
|
||||||
const targetPosition = i * lineHeight;
|
|
||||||
|
|
||||||
// Scroller vers la position calculée
|
|
||||||
this.editor.scrollTo({
|
|
||||||
top: targetPosition,
|
|
||||||
behavior: 'smooth'
|
|
||||||
});
|
|
||||||
|
|
||||||
// Si l'éditeur n'a pas de scroll, utiliser le parent
|
|
||||||
if (this.editor.scrollHeight <= this.editor.clientHeight) {
|
|
||||||
const scrollContainer = this.editor.parentElement || document.documentElement;
|
|
||||||
const editorTop = this.editor.offsetTop;
|
|
||||||
|
|
||||||
scrollContainer.scrollTo({
|
|
||||||
top: editorTop + targetPosition - 100, // -100 pour un padding
|
|
||||||
behavior: 'smooth'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Nettoyer l'élément temporaire
|
|
||||||
setTimeout(() => {
|
|
||||||
if (tempElement.parentNode) {
|
|
||||||
tempElement.parentNode.removeChild(tempElement);
|
|
||||||
}
|
|
||||||
}, 100);
|
|
||||||
|
|
||||||
this.showNotification('Navigation vers la section', 'success');
|
|
||||||
return;
|
|
||||||
} catch (error) {
|
|
||||||
console.log('Erreur de scroll:', error);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
this.showNotification('Section non trouvée', 'warning');
|
if (targetLineIndex === -1) {
|
||||||
|
this.showNotification('Section non trouvée', 'warning');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculer la position approximative de la ligne
|
||||||
|
const editorStyles = window.getComputedStyle(this.editor);
|
||||||
|
const lineHeight = parseFloat(editorStyles.lineHeight) || 20;
|
||||||
|
const paddingTop = parseFloat(editorStyles.paddingTop) || 0;
|
||||||
|
|
||||||
|
// Calculer la position de scroll basée sur le numéro de ligne
|
||||||
|
const targetScrollPosition = (targetLineIndex * lineHeight) + paddingTop;
|
||||||
|
|
||||||
|
// Déterminer si l'éditeur ou la fenêtre doit être scrollée
|
||||||
|
const editorRect = this.editor.getBoundingClientRect();
|
||||||
|
const editorHasScroll = this.editor.scrollHeight > this.editor.clientHeight;
|
||||||
|
|
||||||
|
if (editorHasScroll) {
|
||||||
|
// Scroller dans l'éditeur
|
||||||
|
this.editor.scrollTo({
|
||||||
|
top: Math.max(0, targetScrollPosition - 60),
|
||||||
|
behavior: 'smooth'
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// Scroller la page entière
|
||||||
|
const editorTop = this.editor.offsetTop;
|
||||||
|
const windowScrollTarget = editorTop + targetScrollPosition - 100;
|
||||||
|
|
||||||
|
window.scrollTo({
|
||||||
|
top: Math.max(0, windowScrollTarget),
|
||||||
|
behavior: 'smooth'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Optionnel : mettre en surbrillance temporairement le titre
|
||||||
|
this.highlightHeading(title);
|
||||||
|
|
||||||
|
this.showNotification(`Navigation vers: ${title}`, 'success');
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Erreur de scroll:', error);
|
||||||
|
this.showNotification('Erreur lors de la navigation', 'error');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
highlightHeading(title) {
|
||||||
|
// Fonction pour mettre en surbrillance temporairement le titre trouvé
|
||||||
|
try {
|
||||||
|
const content = this.editor.innerText;
|
||||||
|
const lines = content.split('\n');
|
||||||
|
|
||||||
|
for (let i = 0; i < lines.length; i++) {
|
||||||
|
const line = lines[i].trim();
|
||||||
|
if (line.startsWith('#') && line.replace(/^#+\s*/, '') === title) {
|
||||||
|
// Créer un range pour sélectionner la ligne
|
||||||
|
const selection = window.getSelection();
|
||||||
|
const range = document.createRange();
|
||||||
|
|
||||||
|
// Trouver le nœud texte et la position
|
||||||
|
const walker = document.createTreeWalker(
|
||||||
|
this.editor,
|
||||||
|
NodeFilter.SHOW_TEXT,
|
||||||
|
null,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
|
||||||
|
let currentLine = 0;
|
||||||
|
let textNode = walker.nextNode();
|
||||||
|
|
||||||
|
while (textNode && currentLine < i) {
|
||||||
|
const nodeText = textNode.textContent;
|
||||||
|
const newLines = (nodeText.match(/\n/g) || []).length;
|
||||||
|
currentLine += newLines;
|
||||||
|
|
||||||
|
if (currentLine < i) {
|
||||||
|
textNode = walker.nextNode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (textNode) {
|
||||||
|
// Trouver le début de la ligne dans ce nœud
|
||||||
|
const nodeText = textNode.textContent;
|
||||||
|
const linesInNode = nodeText.split('\n');
|
||||||
|
const targetLineInNode = i - (currentLine - linesInNode.length + 1);
|
||||||
|
|
||||||
|
if (targetLineInNode >= 0 && targetLineInNode < linesInNode.length) {
|
||||||
|
let startPos = 0;
|
||||||
|
for (let j = 0; j < targetLineInNode; j++) {
|
||||||
|
startPos += linesInNode[j].length + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const endPos = startPos + linesInNode[targetLineInNode].length;
|
||||||
|
|
||||||
|
range.setStart(textNode, startPos);
|
||||||
|
range.setEnd(textNode, endPos);
|
||||||
|
|
||||||
|
// Sélectionner temporairement
|
||||||
|
selection.removeAllRanges();
|
||||||
|
selection.addRange(range);
|
||||||
|
|
||||||
|
// Retirer la sélection après un court délai
|
||||||
|
setTimeout(() => {
|
||||||
|
selection.removeAllRanges();
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
// Ignorer les erreurs de surbrillance, ce n'est pas critique
|
||||||
|
console.log('Erreur surbrillance:', error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
exportMarkdown() {
|
exportMarkdown() {
|
||||||
@ -529,9 +625,18 @@ class ConceptionAssistant {
|
|||||||
|
|
||||||
const reader = new FileReader();
|
const reader = new FileReader();
|
||||||
reader.onload = (e) => {
|
reader.onload = (e) => {
|
||||||
this.editor.innerText = e.target.result;
|
const importedContent = e.target.result;
|
||||||
|
this.editor.innerText = importedContent;
|
||||||
this.generateTOC();
|
this.generateTOC();
|
||||||
this.currentJournalId = null; // Nouveau journal
|
this.currentJournalId = null; // Nouveau journal
|
||||||
|
|
||||||
|
// Réinitialiser l'historique pour le fichier importé
|
||||||
|
this.undoStack = [importedContent];
|
||||||
|
this.redoStack = [];
|
||||||
|
|
||||||
|
// S'assurer que l'éditeur est en mode édition
|
||||||
|
this.ensureEditMode();
|
||||||
|
|
||||||
this.showNotification('Fichier Markdown importé', 'success');
|
this.showNotification('Fichier Markdown importé', 'success');
|
||||||
};
|
};
|
||||||
reader.readAsText(file);
|
reader.readAsText(file);
|
||||||
@ -633,6 +738,9 @@ class ConceptionAssistant {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case 'liberty':
|
case 'liberty':
|
||||||
|
// Sauvegarder l'état avant les modifications de l'IA
|
||||||
|
this.saveState(true);
|
||||||
|
|
||||||
const count = document.getElementById('liberty-repeat-count')?.value || 3;
|
const count = document.getElementById('liberty-repeat-count')?.value || 3;
|
||||||
// Mode liberté utilise toujours le document complet
|
// Mode liberté utilise toujours le document complet
|
||||||
result = await this.callAI('/api/ai/liberty-mode', { content: fullContent, iterations: count, focus: 'conception' });
|
result = await this.callAI('/api/ai/liberty-mode', { content: fullContent, iterations: count, focus: 'conception' });
|
||||||
@ -641,7 +749,8 @@ class ConceptionAssistant {
|
|||||||
if (result.finalContent) {
|
if (result.finalContent) {
|
||||||
this.editor.innerText = result.finalContent;
|
this.editor.innerText = result.finalContent;
|
||||||
this.generateTOC();
|
this.generateTOC();
|
||||||
this.saveState();
|
// Sauvegarder l'état après les modifications de l'IA
|
||||||
|
this.saveState(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
let libertyHTML = `<strong>Mode Liberté Intelligent (${result.iterations} itérations)</strong><br><br>`;
|
let libertyHTML = `<strong>Mode Liberté Intelligent (${result.iterations} itérations)</strong><br><br>`;
|
||||||
@ -775,6 +884,9 @@ class ConceptionAssistant {
|
|||||||
if (!this.lastRephraseData) return;
|
if (!this.lastRephraseData) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
// Sauvegarder l'état avant la reformulation pour permettre l'undo
|
||||||
|
this.saveState(true);
|
||||||
|
|
||||||
// Remplacer le texte dans l'éditeur
|
// Remplacer le texte dans l'éditeur
|
||||||
const range = this.lastRephraseData.selection.getRangeAt(0);
|
const range = this.lastRephraseData.selection.getRangeAt(0);
|
||||||
range.deleteContents();
|
range.deleteContents();
|
||||||
@ -784,6 +896,9 @@ class ConceptionAssistant {
|
|||||||
window.getSelection().removeAllRanges();
|
window.getSelection().removeAllRanges();
|
||||||
this.generateTOC();
|
this.generateTOC();
|
||||||
|
|
||||||
|
// Sauvegarder l'état après la reformulation
|
||||||
|
this.saveState(true);
|
||||||
|
|
||||||
// Afficher un message de succès
|
// Afficher un message de succès
|
||||||
this.showNotification('Reformulation appliquée avec succès', 'success');
|
this.showNotification('Reformulation appliquée avec succès', 'success');
|
||||||
this.clearFeedback();
|
this.clearFeedback();
|
||||||
@ -806,6 +921,31 @@ class ConceptionAssistant {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ensureEditMode() {
|
||||||
|
// Si on est en mode preview, forcer le retour en mode édition
|
||||||
|
if (this.isPreviewMode) {
|
||||||
|
const previewBtn = document.getElementById('preview-toggle');
|
||||||
|
|
||||||
|
// Revenir en mode édition sans utiliser originalContent car on veut le nouveau contenu
|
||||||
|
this.editor.contentEditable = true;
|
||||||
|
this.editor.style.background = '';
|
||||||
|
this.editor.style.border = '';
|
||||||
|
this.editor.style.borderRadius = '';
|
||||||
|
|
||||||
|
// Changer le bouton
|
||||||
|
if (previewBtn) {
|
||||||
|
previewBtn.innerHTML = 'Visualiser';
|
||||||
|
previewBtn.classList.remove('secondary');
|
||||||
|
previewBtn.classList.add('primary');
|
||||||
|
}
|
||||||
|
|
||||||
|
this.isPreviewMode = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// S'assurer que l'éditeur est toujours éditable
|
||||||
|
this.editor.contentEditable = true;
|
||||||
|
}
|
||||||
|
|
||||||
showNotification(message, type = 'success') {
|
showNotification(message, type = 'success') {
|
||||||
const notification = document.createElement('div');
|
const notification = document.createElement('div');
|
||||||
notification.className = `notification ${type}`;
|
notification.className = `notification ${type}`;
|
||||||
@ -1059,6 +1199,13 @@ function initializeTemplateForm() {
|
|||||||
app.generateTOC();
|
app.generateTOC();
|
||||||
app.currentJournalId = null; // Nouveau journal
|
app.currentJournalId = null; // Nouveau journal
|
||||||
|
|
||||||
|
// Réinitialiser l'historique pour le nouveau template
|
||||||
|
app.undoStack = [result.data.content];
|
||||||
|
app.redoStack = [];
|
||||||
|
|
||||||
|
// S'assurer que l'éditeur est en mode édition
|
||||||
|
app.ensureEditMode();
|
||||||
|
|
||||||
app.showNotification(`Template ${domain}/${level} chargé avec succès`, 'success');
|
app.showNotification(`Template ${domain}/${level} chargé avec succès`, 'success');
|
||||||
closeAllPanels();
|
closeAllPanels();
|
||||||
} else {
|
} else {
|
||||||
|
@ -11,6 +11,7 @@ function getHead(){
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>Conception-Assistant</title>
|
<title>Conception-Assistant</title>
|
||||||
<link rel="stylesheet" href="/assets/css/style.css">
|
<link rel="stylesheet" href="/assets/css/style.css">
|
||||||
|
<link rel="stylesheet" href="/assets/css/github-preview.css">
|
||||||
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>📝</text></svg>">
|
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>📝</text></svg>">
|
||||||
<!-- Marked.js pour le rendu Markdown compatible GitHub -->
|
<!-- Marked.js pour le rendu Markdown compatible GitHub -->
|
||||||
<script src="https://cdn.jsdelivr.net/npm/marked@9.1.6/marked.min.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/marked@9.1.6/marked.min.js"></script>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user