feat: Implement Google Docs-style editor with ODT export
This commit introduces a complete overhaul of the application, replacing the previous simple Markdown editor with a powerful, Google Docs-style editor with a live preview and ODT export. The new features include: - A new three-pane UI with a Markdown editor, a live preview, and a styles panel. - A powerful styling engine that allows users to create, edit, and apply custom styles to the document. - A persistent storage mechanism for styles using a JSON file. - Support for advanced Markdown features, including code blocks and blockquotes. - A hyperlinked table of contents that is automatically generated from the headings in the document. - Auto-captioning for tables and images. - Pagination for large tables. - Secure image embedding with a domain whitelist and size limits to prevent SSRF and DoS attacks. - An improved live preview that correctly renders multi-line Markdown elements.
This commit is contained in:
+95
-137
@@ -1,165 +1,123 @@
|
||||
/* GhostEditor - Markdown to Word Converter Styles */
|
||||
body {
|
||||
font-family: 'Arial', sans-serif;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background-color: #f8f9fa;
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
color: #202124;
|
||||
}
|
||||
|
||||
.card {
|
||||
border: 1px solid #dee2e6;
|
||||
border-radius: 0.5rem;
|
||||
box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
|
||||
.main-container {
|
||||
display: flex;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
background-color: #e9ecef;
|
||||
border-bottom: 1px solid #dee2e6;
|
||||
font-weight: 600;
|
||||
.editor-pane {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border-right: 1px solid #dadce0;
|
||||
}
|
||||
|
||||
#markdownInput {
|
||||
.toolbar {
|
||||
padding: 8px 16px;
|
||||
background-color: #edf2fa;
|
||||
border-bottom: 1px solid #dadce0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.toolbar button {
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
font-size: 18px;
|
||||
margin-right: 16px;
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
.toolbar button:hover {
|
||||
background-color: #d2e3fc;
|
||||
}
|
||||
|
||||
.markdown-input {
|
||||
flex: 1;
|
||||
padding: 48px;
|
||||
border: none;
|
||||
font-size: 16px;
|
||||
line-height: 1.6;
|
||||
resize: none;
|
||||
height: 500px;
|
||||
font-family: 'Courier New', monospace;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.preview-container {
|
||||
min-height: 500px;
|
||||
background-color: white;
|
||||
border: 1px solid #dee2e6;
|
||||
border-radius: 0.25rem;
|
||||
}
|
||||
|
||||
#previewOutput {
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
/* Markdown preview styling */
|
||||
#previewOutput h1 {
|
||||
color: #2c3e50;
|
||||
border-bottom: 2px solid #3498db;
|
||||
padding-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
#previewOutput h2 {
|
||||
color: #34495e;
|
||||
border-bottom: 1px solid #bdc3c7;
|
||||
padding-bottom: 0.3rem;
|
||||
}
|
||||
|
||||
#previewOutput h3 {
|
||||
color: #5d6d7e;
|
||||
}
|
||||
|
||||
#previewOutput table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin: 1rem 0;
|
||||
}
|
||||
|
||||
#previewOutput table,
|
||||
#previewOutput th,
|
||||
#previewOutput td {
|
||||
border: 1px solid #bdc3c7;
|
||||
}
|
||||
|
||||
#previewOutput th,
|
||||
#previewOutput td {
|
||||
padding: 0.5rem;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
#previewOutput th {
|
||||
background-color: #ecf0f1;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
#previewOutput pre {
|
||||
.preview-pane {
|
||||
flex: 1;
|
||||
padding: 48px;
|
||||
background-color: #f8f9fa;
|
||||
border: 1px solid #e9ecef;
|
||||
border-radius: 0.375rem;
|
||||
padding: 1rem;
|
||||
overflow-x: auto;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
#previewOutput code {
|
||||
background-color: #f1f2f6;
|
||||
padding: 0.2rem 0.4rem;
|
||||
border-radius: 0.25rem;
|
||||
font-family: 'Courier New', monospace;
|
||||
.preview-output {
|
||||
max-width: 8.5in;
|
||||
margin: 0 auto;
|
||||
background-color: #fff;
|
||||
padding: 1in;
|
||||
box-shadow: 0 0 10px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
#previewOutput blockquote {
|
||||
border-left: 4px solid #3498db;
|
||||
margin: 1rem 0;
|
||||
padding-left: 1rem;
|
||||
color: #7f8c8d;
|
||||
font-style: italic;
|
||||
.styles-pane {
|
||||
width: 250px;
|
||||
padding: 20px;
|
||||
background-color: #edf2fa;
|
||||
border-left: 1px solid #dadce0;
|
||||
}
|
||||
|
||||
#previewOutput img {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
margin: 1rem 0;
|
||||
#styles-list {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
/* Style preview */
|
||||
#stylePreview {
|
||||
min-height: 100px;
|
||||
border: 1px dashed #ced4da;
|
||||
padding: 0.5rem;
|
||||
border-radius: 0.25rem;
|
||||
.style-item {
|
||||
padding: 10px;
|
||||
margin-bottom: 10px;
|
||||
background-color: #fff;
|
||||
border: 1px solid #dadce0;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* Responsive adjustments */
|
||||
@media (max-width: 768px) {
|
||||
.container-fluid {
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
||||
.row > div {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.style-item:hover {
|
||||
background-color: #d2e3fc;
|
||||
}
|
||||
|
||||
/* Table pagination styles */
|
||||
.table-pagination-header {
|
||||
background-color: #ecf0f1;
|
||||
.modal {
|
||||
display: none;
|
||||
position: fixed;
|
||||
z-index: 1;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
background-color: rgba(0,0,0,0.4);
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
background-color: #fefefe;
|
||||
margin: 15% auto;
|
||||
padding: 20px;
|
||||
border: 1px solid #888;
|
||||
width: 80%;
|
||||
max-width: 500px;
|
||||
}
|
||||
|
||||
.close-button {
|
||||
color: #aaa;
|
||||
float: right;
|
||||
font-size: 28px;
|
||||
font-weight: bold;
|
||||
border-top: 2px solid #3498db !important;
|
||||
}
|
||||
|
||||
/* Caption styles */
|
||||
.caption {
|
||||
font-size: 0.9rem;
|
||||
color: #7f8c8d;
|
||||
text-align: center;
|
||||
margin-top: 0.5rem;
|
||||
font-style: italic;
|
||||
.close-button:hover,
|
||||
.close-button:focus {
|
||||
color: black;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* Button styling */
|
||||
.btn-outline-secondary.btn-sm {
|
||||
padding: 0.25rem 0.5rem;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
/* Scrollbar styling */
|
||||
.preview-container::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
}
|
||||
|
||||
.preview-container::-webkit-scrollbar-track {
|
||||
background: #f1f1f1;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.preview-container::-webkit-scrollbar-thumb {
|
||||
background: #c1c1c1;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.preview-container::-webkit-scrollbar-thumb:hover {
|
||||
background: #a8a8a8;
|
||||
}
|
||||
Reference in New Issue
Block a user