Compare commits
3 commits
e88c217b1e
...
cb5d056b51
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cb5d056b51 | ||
|
|
e42eeab437 | ||
|
|
f8ac1ec8fc |
5 changed files with 130 additions and 5 deletions
5
public/.gitignore
vendored
5
public/.gitignore
vendored
|
|
@ -48,3 +48,8 @@ Icon
|
|||
# ---------------
|
||||
|
||||
/site/config/.license
|
||||
|
||||
# Content
|
||||
# ---------------
|
||||
content
|
||||
/content/*
|
||||
|
|
|
|||
|
|
@ -22,6 +22,20 @@ Customcss:
|
|||
background: rgba(255, 255, 255, 1);
|
||||
}
|
||||
|
||||
p {
|
||||
font-family: DM Sans;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-size: 16px;
|
||||
text-align: start;
|
||||
color: rgb(0, 0, 0);
|
||||
background: rgba(113, 54, 255, 0.1);
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
|
||||
background: blue;
|
||||
}
|
||||
|
||||
----
|
||||
|
||||
Uuid: xi60pjkz5bp1nlwp
|
||||
|
|
@ -50,12 +50,21 @@
|
|||
></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Export Button -->
|
||||
<button class="export-button" @click="handleExport" type="button">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M9 16v-6H5l7-7 7 7h-4v6H9zm-4 4h14v-2H5v2z"/>
|
||||
</svg>
|
||||
<span>Exporter la feuille de style complète</span>
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, watch } from 'vue';
|
||||
import { useStylesheetStore } from '../stores/stylesheet';
|
||||
import { useNarrativeStore } from '../stores/narrative';
|
||||
import CssFileImport from './ui/CssFileImport.vue';
|
||||
import hljs from 'highlight.js/lib/core';
|
||||
import css from 'highlight.js/lib/languages/css';
|
||||
|
|
@ -64,6 +73,7 @@ import 'highlight.js/styles/atom-one-dark.css';
|
|||
hljs.registerLanguage('css', css);
|
||||
|
||||
const stylesheetStore = useStylesheetStore();
|
||||
const narrativeStore = useNarrativeStore();
|
||||
const isBaseCssExpanded = ref(false);
|
||||
const isCustomCssEditable = ref(false);
|
||||
let debounceTimer = null;
|
||||
|
|
@ -99,6 +109,65 @@ const handleCssImport = (cssContent) => {
|
|||
stylesheetStore.customCss = cssContent;
|
||||
};
|
||||
|
||||
const handleExport = () => {
|
||||
const now = new Date();
|
||||
const dateStr = now.toLocaleDateString('fr-FR', {
|
||||
day: '2-digit',
|
||||
month: '2-digit',
|
||||
year: 'numeric',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit'
|
||||
});
|
||||
|
||||
const narrativeTitle = narrativeStore.data?.title || 'Sans titre';
|
||||
const narrativeSlug = narrativeStore.data?.slug || 'narrative';
|
||||
|
||||
// Build complete CSS with comments
|
||||
let completeCSS = `/*
|
||||
* Feuille de style pour l'impression
|
||||
* Récit : ${narrativeTitle}
|
||||
* Téléchargé le : ${dateStr}
|
||||
*
|
||||
* Ce fichier contient le CSS de base et le CSS personnalisé
|
||||
* fusionnés pour une utilisation hors ligne.
|
||||
*/
|
||||
|
||||
`;
|
||||
|
||||
// Add base CSS
|
||||
if (stylesheetStore.baseCss) {
|
||||
completeCSS += `/* ========================================
|
||||
* CSS DE BASE
|
||||
* Styles par défaut de l'application
|
||||
* ======================================== */
|
||||
|
||||
${stylesheetStore.baseCss}
|
||||
|
||||
`;
|
||||
}
|
||||
|
||||
// Add custom CSS
|
||||
if (stylesheetStore.customCss) {
|
||||
completeCSS += `/* ========================================
|
||||
* CSS PERSONNALISÉ
|
||||
* Styles spécifiques à ce récit
|
||||
* ======================================== */
|
||||
|
||||
${stylesheetStore.customCss}`;
|
||||
}
|
||||
|
||||
// Create blob and download
|
||||
const blob = new Blob([completeCSS], { type: 'text/css' });
|
||||
const url = URL.createObjectURL(blob);
|
||||
const link = document.createElement('a');
|
||||
link.href = url;
|
||||
link.download = `${narrativeSlug}-style.print.css`;
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
URL.revokeObjectURL(url);
|
||||
};
|
||||
|
||||
// Watch editing mode and format when exiting
|
||||
watch(isCustomCssEditable, async (newValue, oldValue) => {
|
||||
stylesheetStore.isEditing = newValue;
|
||||
|
|
@ -234,6 +303,7 @@ h3 {
|
|||
font-family: 'Courier New', Courier, monospace;
|
||||
font-size: 0.875rem;
|
||||
line-height: 1.5;
|
||||
max-height: 500px;
|
||||
}
|
||||
|
||||
.readonly code {
|
||||
|
|
@ -244,6 +314,8 @@ textarea {
|
|||
width: 100%;
|
||||
flex: 1;
|
||||
min-height: 300px;
|
||||
max-height: 500px;
|
||||
overflow-y: auto;
|
||||
background: #1e1e1e;
|
||||
color: #abb2bf;
|
||||
border: none;
|
||||
|
|
@ -259,4 +331,33 @@ textarea::placeholder {
|
|||
color: #5c6370;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.export-button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 0.5rem;
|
||||
width: 100%;
|
||||
padding: 0.75rem 1rem;
|
||||
margin-top: 1rem;
|
||||
background: #2c313c;
|
||||
color: #abb2bf;
|
||||
border: 1px solid #3e4451;
|
||||
border-radius: 0.25rem;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.export-button:hover {
|
||||
background: #3e4451;
|
||||
border-color: #61afef;
|
||||
color: #61afef;
|
||||
}
|
||||
|
||||
.export-button svg {
|
||||
width: 1.25rem;
|
||||
height: 1.25rem;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -452,6 +452,11 @@ const updateMarginInnerUnit = (unit) => {
|
|||
};
|
||||
|
||||
// Watchers for body styles
|
||||
watch(font, (val) => {
|
||||
if (isUpdatingFromStore) return;
|
||||
updateStyle('body', 'font-family', `"${val}"`);
|
||||
});
|
||||
|
||||
watch(italic, (val) => {
|
||||
if (isUpdatingFromStore) return;
|
||||
updateStyle('body', 'font-style', val ? 'italic' : 'normal');
|
||||
|
|
|
|||
|
|
@ -14,13 +14,13 @@ export function useCssUpdater() {
|
|||
new RegExp(`(${property}:\\s*)[^;]+`, 'i'),
|
||||
`$1${value}`
|
||||
);
|
||||
store.content = store.content.replace(currentBlock, updatedBlock);
|
||||
store.replaceBlock(currentBlock, updatedBlock);
|
||||
} else {
|
||||
const updatedBlock = currentBlock.replace(
|
||||
/(\s*})$/,
|
||||
` ${property}: ${value};\n$1`
|
||||
);
|
||||
store.content = store.content.replace(currentBlock, updatedBlock);
|
||||
store.replaceBlock(currentBlock, updatedBlock);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -37,7 +37,7 @@ export function useCssUpdater() {
|
|||
);
|
||||
|
||||
if (updatedBlock !== currentBlock) {
|
||||
store.content = store.content.replace(currentBlock, updatedBlock);
|
||||
store.replaceBlock(currentBlock, updatedBlock);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -57,7 +57,7 @@ export function useCssUpdater() {
|
|||
}
|
||||
|
||||
if (updatedBlock !== currentBlock) {
|
||||
store.content = store.content.replace(currentBlock, updatedBlock);
|
||||
store.replaceBlock(currentBlock, updatedBlock);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -65,7 +65,7 @@ export function useCssUpdater() {
|
|||
* Create a new CSS rule for a selector
|
||||
*/
|
||||
const createRule = (selector) => {
|
||||
store.content += `\n\n${selector} {\n}\n`;
|
||||
store.customCss += `\n\n${selector} {\n}\n`;
|
||||
return `${selector} {\n}`;
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue