styling
This commit is contained in:
@@ -363,3 +363,5 @@ MigrationBackup/
|
|||||||
FodyWeavers.xsd
|
FodyWeavers.xsd
|
||||||
/Notemark/data/shares
|
/Notemark/data/shares
|
||||||
/Notemark/data/sessions/b5acffbe-706e-405e-b006-3415508a59a7.md
|
/Notemark/data/sessions/b5acffbe-706e-405e-b006-3415508a59a7.md
|
||||||
|
/Notemark/data/sessions
|
||||||
|
/Notemark/data/documents.json
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
@inject ISessionService SessionService
|
@inject ISessionService SessionService
|
||||||
@inject IDocumentService DocumentService
|
@inject IDocumentService DocumentService
|
||||||
@inject IMarkdownService MarkdownService
|
@inject IMarkdownService MarkdownService
|
||||||
|
@inject IAutoStyleService AutoStyleService
|
||||||
@inject IShareService ShareService
|
@inject IShareService ShareService
|
||||||
@inject IJSRuntime JSRuntime
|
@inject IJSRuntime JSRuntime
|
||||||
@inject IHttpContextAccessor HttpContextAccessor
|
@inject IHttpContextAccessor HttpContextAccessor
|
||||||
@@ -101,6 +102,55 @@
|
|||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
<!-- Document Style Dropdown in Top Bar -->
|
||||||
|
<div class="style-container">
|
||||||
|
<button class="btn-topbar btn-style-nav @(showStyleDropdown ? "active" : "")" @onclick="ToggleStyleDropdown" title="Document Typography & Style Preset (@GetDocStyleDisplayName(documentStyle))">
|
||||||
|
<span>🎨</span>
|
||||||
|
<span class="btn-text">@GetDocStyleDisplayName(documentStyle)</span>
|
||||||
|
<span class="btn-caret">▾</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
@if (showStyleDropdown)
|
||||||
|
{
|
||||||
|
<div class="dropdown-backdrop" @onclick="CloseStyleDropdown"></div>
|
||||||
|
<div class="style-dropdown">
|
||||||
|
<div class="dropdown-header-title">Document Style</div>
|
||||||
|
<button type="button" class="style-menu-item @(documentStyle == "modern" ? "selected" : "")" @onclick='() => SetDocumentStyle("modern")'>
|
||||||
|
<span class="style-item-icon">🌟</span>
|
||||||
|
<div class="style-item-text">
|
||||||
|
<span class="style-item-name">Modern</span>
|
||||||
|
<span class="style-item-desc">Clean sans-serif, modern cards & alerts</span>
|
||||||
|
</div>
|
||||||
|
@if (documentStyle == "modern") { <span class="style-check">✓</span> }
|
||||||
|
</button>
|
||||||
|
<button type="button" class="style-menu-item @(documentStyle == "academic" ? "selected" : "")" @onclick='() => SetDocumentStyle("academic")'>
|
||||||
|
<span class="style-item-icon">🎓</span>
|
||||||
|
<div class="style-item-text">
|
||||||
|
<span class="style-item-name">Academic</span>
|
||||||
|
<span class="style-item-desc">Formal serif, booktabs academic tables</span>
|
||||||
|
</div>
|
||||||
|
@if (documentStyle == "academic") { <span class="style-check">✓</span> }
|
||||||
|
</button>
|
||||||
|
<button type="button" class="style-menu-item @(documentStyle == "technical" ? "selected" : "")" @onclick='() => SetDocumentStyle("technical")'>
|
||||||
|
<span class="style-item-icon">💻</span>
|
||||||
|
<div class="style-item-text">
|
||||||
|
<span class="style-item-name">Technical</span>
|
||||||
|
<span class="style-item-desc">Monospace accents, compact dev spec</span>
|
||||||
|
</div>
|
||||||
|
@if (documentStyle == "technical") { <span class="style-check">✓</span> }
|
||||||
|
</button>
|
||||||
|
<button type="button" class="style-menu-item @(documentStyle == "executive" ? "selected" : "")" @onclick='() => SetDocumentStyle("executive")'>
|
||||||
|
<span class="style-item-icon">📊</span>
|
||||||
|
<div class="style-item-text">
|
||||||
|
<span class="style-item-name">Executive</span>
|
||||||
|
<span class="style-item-desc">Corporate navy headers, executive report</span>
|
||||||
|
</div>
|
||||||
|
@if (documentStyle == "executive") { <span class="style-check">✓</span> }
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Export / Actions Dropdown -->
|
<!-- Export / Actions Dropdown -->
|
||||||
<div class="export-container">
|
<div class="export-container">
|
||||||
<button class="btn-topbar btn-export" @onclick="ToggleExportDropdown" title="Export & Print Options">
|
<button class="btn-topbar btn-export" @onclick="ToggleExportDropdown" title="Export & Print Options">
|
||||||
@@ -180,6 +230,15 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Toast Notification for Auto Style and System status -->
|
||||||
|
@if (!string.IsNullOrEmpty(autoStyleToastMessage))
|
||||||
|
{
|
||||||
|
<div class="notemark-toast @autoStyleToastType" role="alert">
|
||||||
|
<span class="toast-icon">@(autoStyleToastType == "success" ? "✨" : (autoStyleToastType == "error" ? "⚠️" : "ℹ️"))</span>
|
||||||
|
<span class="toast-message">@autoStyleToastMessage</span>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
<!-- Format Toolbar with Undo / Redo -->
|
<!-- Format Toolbar with Undo / Redo -->
|
||||||
<div class="format-toolbar">
|
<div class="format-toolbar">
|
||||||
<button type="button" class="toolbar-btn" @onclick="Undo" disabled="@(!canUndo)" title="Undo (Ctrl+Z)">
|
<button type="button" class="toolbar-btn" @onclick="Undo" disabled="@(!canUndo)" title="Undo (Ctrl+Z)">
|
||||||
@@ -224,9 +283,31 @@
|
|||||||
<button type="button" class="toolbar-btn" @onclick='() => InsertFormatting("> [!NOTE]\n> ", "", "Helpful information here")' title="Note Callout">
|
<button type="button" class="toolbar-btn" @onclick='() => InsertFormatting("> [!NOTE]\n> ", "", "Helpful information here")' title="Note Callout">
|
||||||
💡 Note
|
💡 Note
|
||||||
</button>
|
</button>
|
||||||
<button type="button" class="toolbar-btn" @onclick='() => InsertFormatting("| Column 1 | Column 2 |\n|---|---|\n| Cell 1 | Cell 2 |", "", "")' title="Table">
|
<button type="button" class="toolbar-btn" @onclick='() => InsertFormatting("\n\n| Column 1 | Column 2 |\n| :--- | :--- |\n| Cell 1 | Cell 2 |\n\n", "", "")' title="Table">
|
||||||
📋 Table
|
📋 Table
|
||||||
</button>
|
</button>
|
||||||
|
<span class="toolbar-divider"></span>
|
||||||
|
<button type="button"
|
||||||
|
class="toolbar-btn btn-auto-style @(isAutoStyling ? "styling" : "") @(isAutoStyledSuccess ? "success" : "")"
|
||||||
|
@onclick="AutoStyleDocument"
|
||||||
|
disabled="@isAutoStyling"
|
||||||
|
title="Auto Style & Format Markdown (Shift+Alt+F)">
|
||||||
|
@if (isAutoStyling)
|
||||||
|
{
|
||||||
|
<span class="spinner-inline">⏳</span>
|
||||||
|
<span class="toolbar-text">Formatting...</span>
|
||||||
|
}
|
||||||
|
else if (isAutoStyledSuccess)
|
||||||
|
{
|
||||||
|
<span class="success-icon">✓</span>
|
||||||
|
<span class="toolbar-text">Formatted!</span>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<span>🪄</span>
|
||||||
|
<span class="toolbar-text">Auto Style</span>
|
||||||
|
}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Editor Layout -->
|
<!-- Editor Layout -->
|
||||||
@@ -257,7 +338,7 @@
|
|||||||
{
|
{
|
||||||
<div class="preview-pane" style="@GetPreviewPaneStyle()">
|
<div class="preview-pane" style="@GetPreviewPaneStyle()">
|
||||||
<div class="preview-header-bar">
|
<div class="preview-header-bar">
|
||||||
<span class="preview-label">Live Preview</span>
|
<span class="preview-label">Live Preview (@GetDocStyleDisplayName(documentStyle))</span>
|
||||||
<div class="preview-zoom-controls" title="Zoom: Scroll wheel over controls, or Ctrl + Scroll over preview">
|
<div class="preview-zoom-controls" title="Zoom: Scroll wheel over controls, or Ctrl + Scroll over preview">
|
||||||
<button type="button" class="btn-zoom" @onclick="ZoomOut" title="Zoom out (min 70%)" disabled="@(previewZoom <= 70)">
|
<button type="button" class="btn-zoom" @onclick="ZoomOut" title="Zoom out (min 70%)" disabled="@(previewZoom <= 70)">
|
||||||
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||||
@@ -275,7 +356,7 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="preview-content" style="@GetPreviewContentStyle()">
|
<div class="preview-content doc-style-@documentStyle" data-doc-style="@documentStyle" style="@GetPreviewContentStyle()">
|
||||||
@((MarkupString)renderedHtml)
|
@((MarkupString)renderedHtml)
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -303,6 +384,13 @@
|
|||||||
private bool isFlagged = false;
|
private bool isFlagged = false;
|
||||||
private int flaggedCount = 0;
|
private int flaggedCount = 0;
|
||||||
private List<string> currentTags = new();
|
private List<string> currentTags = new();
|
||||||
|
private string documentStyle = "modern";
|
||||||
|
private bool showStyleDropdown = false;
|
||||||
|
private bool isAutoStyling = false;
|
||||||
|
private bool isAutoStyledSuccess = false;
|
||||||
|
private string? autoStyleToastMessage = null;
|
||||||
|
private string autoStyleToastType = "success";
|
||||||
|
private Timer? toastDismissTimer;
|
||||||
private string markdownContent = "";
|
private string markdownContent = "";
|
||||||
private string renderedHtml = "";
|
private string renderedHtml = "";
|
||||||
private bool showPreview = true;
|
private bool showPreview = true;
|
||||||
@@ -367,12 +455,14 @@
|
|||||||
documentTitle = meta.Title;
|
documentTitle = meta.Title;
|
||||||
isFlagged = meta.IsFlagged;
|
isFlagged = meta.IsFlagged;
|
||||||
currentTags = meta.Tags;
|
currentTags = meta.Tags;
|
||||||
|
documentStyle = !string.IsNullOrWhiteSpace(meta.DocStyle) ? meta.DocStyle : "modern";
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
documentTitle = DocumentService.ExtractTitle(markdownContent);
|
documentTitle = DocumentService.ExtractTitle(markdownContent);
|
||||||
isFlagged = false;
|
isFlagged = false;
|
||||||
currentTags = DocumentService.ExtractTags(markdownContent);
|
currentTags = DocumentService.ExtractTags(markdownContent);
|
||||||
|
documentStyle = "modern";
|
||||||
}
|
}
|
||||||
|
|
||||||
undoStack.Clear();
|
undoStack.Clear();
|
||||||
@@ -537,7 +627,7 @@
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
await SessionService.SaveSessionContentAsync(sessionId, markdownContent);
|
await SessionService.SaveSessionContentAsync(sessionId, markdownContent);
|
||||||
await DocumentService.SaveDocumentAsync(sessionId, markdownContent, isFlagged, documentTitle, currentTags);
|
await DocumentService.SaveDocumentAsync(sessionId, markdownContent, isFlagged, documentTitle, currentTags, documentStyle);
|
||||||
hasUnsavedChanges = false;
|
hasUnsavedChanges = false;
|
||||||
saveStatus = "Saved";
|
saveStatus = "Saved";
|
||||||
await UpdateFlaggedCount();
|
await UpdateFlaggedCount();
|
||||||
@@ -599,6 +689,101 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task AutoStyleDocument()
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(markdownContent))
|
||||||
|
{
|
||||||
|
ShowToast("Document is empty", "info");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
isAutoStyling = true;
|
||||||
|
StateHasChanged();
|
||||||
|
await Task.Delay(250); // Visual cue for user that action is running
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var formatted = AutoStyleService.FormatMarkdown(markdownContent);
|
||||||
|
if (formatted != markdownContent)
|
||||||
|
{
|
||||||
|
undoStack.Add(markdownContent);
|
||||||
|
if (undoStack.Count > 50) undoStack.RemoveAt(0);
|
||||||
|
redoStack.Clear();
|
||||||
|
|
||||||
|
markdownContent = formatted;
|
||||||
|
hasUnsavedChanges = true;
|
||||||
|
UpdateStatistics();
|
||||||
|
if (showPreview)
|
||||||
|
{
|
||||||
|
renderedHtml = MarkdownService.RenderToHtml(markdownContent);
|
||||||
|
}
|
||||||
|
await SaveContent();
|
||||||
|
isAutoStyledSuccess = true;
|
||||||
|
ShowToast("Document styled & formatted! ✨", "success");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ShowToast("Document is already well formatted! ✓", "info");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
ShowToast($"Formatting error: {ex.Message}", "error");
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
isAutoStyling = false;
|
||||||
|
StateHasChanged();
|
||||||
|
|
||||||
|
_ = Task.Run(async () =>
|
||||||
|
{
|
||||||
|
await Task.Delay(2200);
|
||||||
|
isAutoStyledSuccess = false;
|
||||||
|
await InvokeAsync(StateHasChanged);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ShowToast(string message, string type = "success")
|
||||||
|
{
|
||||||
|
autoStyleToastMessage = message;
|
||||||
|
autoStyleToastType = type;
|
||||||
|
toastDismissTimer?.Dispose();
|
||||||
|
toastDismissTimer = new Timer(_ =>
|
||||||
|
{
|
||||||
|
autoStyleToastMessage = null;
|
||||||
|
InvokeAsync(StateHasChanged);
|
||||||
|
}, null, 3000, Timeout.Infinite);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ToggleStyleDropdown()
|
||||||
|
{
|
||||||
|
showStyleDropdown = !showStyleDropdown;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CloseStyleDropdown()
|
||||||
|
{
|
||||||
|
showStyleDropdown = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task SetDocumentStyle(string style)
|
||||||
|
{
|
||||||
|
documentStyle = style;
|
||||||
|
showStyleDropdown = false;
|
||||||
|
hasUnsavedChanges = true;
|
||||||
|
await SaveContent();
|
||||||
|
ShowToast($"Style set to {GetDocStyleDisplayName(style)}! 🎨", "success");
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetDocStyleDisplayName(string style) => style switch
|
||||||
|
{
|
||||||
|
"academic" => "Academic",
|
||||||
|
"technical" => "Technical",
|
||||||
|
"executive" => "Executive",
|
||||||
|
_ => "Modern"
|
||||||
|
};
|
||||||
|
|
||||||
private async Task OnKeyDown(KeyboardEventArgs e)
|
private async Task OnKeyDown(KeyboardEventArgs e)
|
||||||
{
|
{
|
||||||
if ((e.CtrlKey || e.MetaKey) && e.Key.Equals("s", StringComparison.OrdinalIgnoreCase))
|
if ((e.CtrlKey || e.MetaKey) && e.Key.Equals("s", StringComparison.OrdinalIgnoreCase))
|
||||||
@@ -617,6 +802,11 @@
|
|||||||
{
|
{
|
||||||
Redo();
|
Redo();
|
||||||
}
|
}
|
||||||
|
else if ((e.ShiftKey && e.AltKey && e.Key.Equals("f", StringComparison.OrdinalIgnoreCase)) ||
|
||||||
|
(e.CtrlKey && e.ShiftKey && e.Key.Equals("f", StringComparison.OrdinalIgnoreCase)))
|
||||||
|
{
|
||||||
|
await AutoStyleDocument();
|
||||||
|
}
|
||||||
else if ((e.CtrlKey || e.MetaKey) && e.Key.Equals("b", StringComparison.OrdinalIgnoreCase))
|
else if ((e.CtrlKey || e.MetaKey) && e.Key.Equals("b", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
await InsertFormatting("**", "**", "bold text");
|
await InsertFormatting("**", "**", "bold text");
|
||||||
@@ -656,6 +846,7 @@
|
|||||||
markdownContent = "# Untitled Note\n\nStart typing here... #ideas";
|
markdownContent = "# Untitled Note\n\nStart typing here... #ideas";
|
||||||
documentTitle = "Untitled Note";
|
documentTitle = "Untitled Note";
|
||||||
isFlagged = false;
|
isFlagged = false;
|
||||||
|
documentStyle = "modern";
|
||||||
hasUnsavedChanges = false;
|
hasUnsavedChanges = false;
|
||||||
saveStatus = "Saved";
|
saveStatus = "Saved";
|
||||||
|
|
||||||
@@ -665,7 +856,7 @@
|
|||||||
UpdateStatistics();
|
UpdateStatistics();
|
||||||
renderedHtml = MarkdownService.RenderToHtml(markdownContent);
|
renderedHtml = MarkdownService.RenderToHtml(markdownContent);
|
||||||
|
|
||||||
await DocumentService.SaveDocumentAsync(sessionId, markdownContent, isFlagged, documentTitle, currentTags);
|
await DocumentService.SaveDocumentAsync(sessionId, markdownContent, isFlagged, documentTitle, currentTags, documentStyle);
|
||||||
await SessionService.SaveSessionContentAsync(sessionId, markdownContent);
|
await SessionService.SaveSessionContentAsync(sessionId, markdownContent);
|
||||||
await UpdateFlaggedCount();
|
await UpdateFlaggedCount();
|
||||||
|
|
||||||
@@ -693,7 +884,7 @@
|
|||||||
showExportDropdown = false;
|
showExportDropdown = false;
|
||||||
var filename = $"{documentTitle.Replace(" ", "_").ToLowerInvariant()}.html";
|
var filename = $"{documentTitle.Replace(" ", "_").ToLowerInvariant()}.html";
|
||||||
var currentTheme = await JSRuntime.InvokeAsync<string>("notemarkTheme.getCurrentTheme");
|
var currentTheme = await JSRuntime.InvokeAsync<string>("notemarkTheme.getCurrentTheme");
|
||||||
await JSRuntime.InvokeVoidAsync("notemarkEditor.downloadHtml", filename, documentTitle, renderedHtml, currentTheme);
|
await JSRuntime.InvokeVoidAsync("notemarkEditor.downloadHtml", filename, documentTitle, renderedHtml, currentTheme, documentStyle);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task PrintDocument()
|
private async Task PrintDocument()
|
||||||
@@ -864,6 +1055,7 @@
|
|||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
|
toastDismissTimer?.Dispose();
|
||||||
autoSaveTimer?.Dispose();
|
autoSaveTimer?.Dispose();
|
||||||
dotNetRef?.Dispose();
|
dotNetRef?.Dispose();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -264,6 +264,52 @@
|
|||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.toolbar-text {
|
||||||
|
margin-left: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-auto-style {
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--accent);
|
||||||
|
background-color: var(--accent-light);
|
||||||
|
border: 1px solid var(--accent);
|
||||||
|
gap: 0.3rem;
|
||||||
|
padding: 0.3rem 0.65rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-auto-style:hover:not(:disabled) {
|
||||||
|
background-color: var(--accent);
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-auto-style.styling {
|
||||||
|
background-color: var(--accent);
|
||||||
|
color: #ffffff;
|
||||||
|
border-color: var(--accent);
|
||||||
|
cursor: wait;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spinner-inline {
|
||||||
|
display: inline-block;
|
||||||
|
animation: spinIcon 1s linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes spinIcon {
|
||||||
|
from { transform: rotate(0deg); }
|
||||||
|
to { transform: rotate(360deg); }
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-auto-style.success {
|
||||||
|
background-color: rgba(16, 185, 129, 0.15);
|
||||||
|
color: var(--success);
|
||||||
|
border-color: var(--success);
|
||||||
|
}
|
||||||
|
|
||||||
|
.success-icon {
|
||||||
|
font-weight: 800;
|
||||||
|
color: var(--success);
|
||||||
|
}
|
||||||
|
|
||||||
.toolbar-divider {
|
.toolbar-divider {
|
||||||
width: 1px;
|
width: 1px;
|
||||||
height: 18px;
|
height: 18px;
|
||||||
@@ -598,8 +644,7 @@
|
|||||||
/* Tables */
|
/* Tables */
|
||||||
.preview-content table {
|
.preview-content table {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
border-collapse: separate;
|
border-collapse: collapse;
|
||||||
border-spacing: 0;
|
|
||||||
margin: 1.5em 0;
|
margin: 1.5em 0;
|
||||||
border: 1px solid var(--border);
|
border: 1px solid var(--border);
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
@@ -610,24 +655,15 @@
|
|||||||
.preview-content th,
|
.preview-content th,
|
||||||
.preview-content td {
|
.preview-content td {
|
||||||
padding: 0.75rem 1rem;
|
padding: 0.75rem 1rem;
|
||||||
border-bottom: 1px solid var(--border);
|
border: 1px solid var(--border);
|
||||||
border-right: 1px solid var(--border);
|
|
||||||
text-align: left;
|
text-align: left;
|
||||||
}
|
}
|
||||||
|
|
||||||
.preview-content th:last-child,
|
|
||||||
.preview-content td:last-child {
|
|
||||||
border-right: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.preview-content tr:last-child td {
|
|
||||||
border-bottom: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.preview-content th {
|
.preview-content th {
|
||||||
background-color: var(--surface-hover);
|
background-color: var(--surface-hover);
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: var(--primary-text);
|
color: var(--primary-text);
|
||||||
|
border-bottom: 2px solid var(--border);
|
||||||
}
|
}
|
||||||
|
|
||||||
.preview-content tr:hover td {
|
.preview-content tr:hover td {
|
||||||
@@ -811,6 +847,149 @@
|
|||||||
background-color: var(--accent-hover);
|
background-color: var(--accent-hover);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Document Style Dropdown in Top Bar */
|
||||||
|
.style-container {
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-style-nav {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.35rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-caret {
|
||||||
|
font-size: 0.75rem;
|
||||||
|
opacity: 0.7;
|
||||||
|
margin-left: 0.15rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.style-dropdown {
|
||||||
|
position: absolute;
|
||||||
|
top: 100%;
|
||||||
|
right: 0;
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
background-color: var(--surface);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 10px;
|
||||||
|
box-shadow: var(--shadow-lg);
|
||||||
|
padding: 0.5rem;
|
||||||
|
min-width: 260px;
|
||||||
|
z-index: 1000;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.25rem;
|
||||||
|
animation: dropdownFadeIn 0.15s cubic-bezier(0.16, 1, 0.3, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-header-title {
|
||||||
|
font-size: 0.72rem;
|
||||||
|
font-weight: 700;
|
||||||
|
color: var(--secondary-text);
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.05em;
|
||||||
|
padding: 0.35rem 0.65rem 0.2rem 0.65rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.style-menu-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.6rem;
|
||||||
|
width: 100%;
|
||||||
|
padding: 0.5rem 0.65rem;
|
||||||
|
border: none;
|
||||||
|
background: none;
|
||||||
|
text-align: left;
|
||||||
|
border-radius: 6px;
|
||||||
|
cursor: pointer;
|
||||||
|
color: var(--primary-text);
|
||||||
|
transition: all 0.15s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.style-menu-item:hover {
|
||||||
|
background-color: var(--surface-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
.style-menu-item.selected {
|
||||||
|
background-color: var(--accent-light);
|
||||||
|
color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.style-item-icon {
|
||||||
|
font-size: 1.15rem;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.style-item-text {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.style-item-name {
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 0.84rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.style-item-desc {
|
||||||
|
font-size: 0.72rem;
|
||||||
|
color: var(--secondary-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
.style-check {
|
||||||
|
font-weight: 700;
|
||||||
|
color: var(--accent);
|
||||||
|
margin-left: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Toast Notification */
|
||||||
|
.notemark-toast {
|
||||||
|
position: fixed;
|
||||||
|
top: 60px;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.6rem;
|
||||||
|
padding: 0.65rem 1.25rem;
|
||||||
|
border-radius: 999px;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
font-weight: 600;
|
||||||
|
box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.2), 0 8px 10px -6px rgba(0, 0, 0, 0.1);
|
||||||
|
z-index: 2000;
|
||||||
|
animation: toastSlideDown 0.25s cubic-bezier(0.16, 1, 0.3, 1);
|
||||||
|
background-color: var(--surface);
|
||||||
|
color: var(--primary-text);
|
||||||
|
border: 1.5px solid var(--border);
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes toastSlideDown {
|
||||||
|
from { opacity: 0; transform: translate(-50%, -12px); }
|
||||||
|
to { opacity: 1; transform: translate(-50%, 0); }
|
||||||
|
}
|
||||||
|
|
||||||
|
.notemark-toast.success {
|
||||||
|
border-color: var(--success);
|
||||||
|
}
|
||||||
|
|
||||||
|
.notemark-toast.info {
|
||||||
|
border-color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.notemark-toast.error {
|
||||||
|
border-color: var(--error);
|
||||||
|
color: var(--error);
|
||||||
|
}
|
||||||
|
|
||||||
|
.toast-icon {
|
||||||
|
font-size: 1.1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toast-message {
|
||||||
|
line-height: 1.2;
|
||||||
|
}
|
||||||
|
|
||||||
/* Mobile Responsive */
|
/* Mobile Responsive */
|
||||||
@media screen and (max-width: 768px) {
|
@media screen and (max-width: 768px) {
|
||||||
.top-bar {
|
.top-bar {
|
||||||
@@ -862,6 +1041,461 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ========================================================= */
|
||||||
|
/* DOCUMENT PRESET STYLES & GITHUB ALERTS */
|
||||||
|
/* ========================================================= */
|
||||||
|
|
||||||
|
/* Callouts / GitHub Alerts */
|
||||||
|
.preview-content .markdown-alert,
|
||||||
|
.markdown-alert {
|
||||||
|
padding: 0.75rem 1rem;
|
||||||
|
margin: 1.25rem 0;
|
||||||
|
border-left: 4px solid;
|
||||||
|
border-radius: 0 6px 6px 0;
|
||||||
|
background-color: var(--surface-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-content .markdown-alert-title,
|
||||||
|
.markdown-alert-title {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-bottom: 0.35rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-content .markdown-alert-note, .markdown-alert-note { border-left-color: #3b82f6; background-color: rgba(59, 130, 246, 0.08); }
|
||||||
|
.preview-content .markdown-alert-tip, .markdown-alert-tip { border-left-color: #10b981; background-color: rgba(16, 185, 129, 0.08); }
|
||||||
|
.preview-content .markdown-alert-important, .markdown-alert-important { border-left-color: #8b5cf6; background-color: rgba(139, 92, 246, 0.08); }
|
||||||
|
.preview-content .markdown-alert-warning, .markdown-alert-warning { border-left-color: #f59e0b; background-color: rgba(245, 158, 11, 0.08); }
|
||||||
|
.preview-content .markdown-alert-caution, .markdown-alert-caution { border-left-color: #ef4444; background-color: rgba(239, 68, 68, 0.08); }
|
||||||
|
|
||||||
|
/* ========================================================= */
|
||||||
|
/* DISTINCT DOCUMENT PRESET STYLES */
|
||||||
|
/* ========================================================= */
|
||||||
|
|
||||||
|
/* === PRESET 1: MODERN (Clean SaaS / Notion Look) === */
|
||||||
|
.preview-content[data-doc-style="modern"],
|
||||||
|
.preview-content.doc-style-modern {
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
||||||
|
line-height: 1.7;
|
||||||
|
color: var(--primary-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-content[data-doc-style="modern"] h1,
|
||||||
|
.preview-content.doc-style-modern h1 {
|
||||||
|
font-size: 2.3rem;
|
||||||
|
font-weight: 800;
|
||||||
|
letter-spacing: -0.03em;
|
||||||
|
background: linear-gradient(135deg, var(--primary-text) 60%, var(--accent));
|
||||||
|
-webkit-background-clip: text;
|
||||||
|
-webkit-text-fill-color: transparent;
|
||||||
|
border-bottom: 2px solid var(--accent);
|
||||||
|
padding-bottom: 0.4em;
|
||||||
|
margin-top: 1.4em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-content[data-doc-style="modern"] h2,
|
||||||
|
.preview-content.doc-style-modern h2 {
|
||||||
|
font-size: 1.6rem;
|
||||||
|
font-weight: 700;
|
||||||
|
letter-spacing: -0.02em;
|
||||||
|
border-bottom: 1px solid var(--border);
|
||||||
|
padding-bottom: 0.3em;
|
||||||
|
margin-top: 1.3em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-content[data-doc-style="modern"] table,
|
||||||
|
.preview-content.doc-style-modern table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
border: 1.5px solid var(--border);
|
||||||
|
border-radius: 8px;
|
||||||
|
overflow: hidden;
|
||||||
|
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.05);
|
||||||
|
margin: 1.5em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-content[data-doc-style="modern"] th,
|
||||||
|
.preview-content.doc-style-modern th {
|
||||||
|
background-color: var(--surface-hover);
|
||||||
|
color: var(--primary-text);
|
||||||
|
font-weight: 600;
|
||||||
|
padding: 0.75rem 1rem;
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-bottom: 2px solid var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-content[data-doc-style="modern"] td,
|
||||||
|
.preview-content.doc-style-modern td {
|
||||||
|
padding: 0.7rem 1rem;
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-content[data-doc-style="modern"] tr:hover td,
|
||||||
|
.preview-content.doc-style-modern tr:hover td {
|
||||||
|
background-color: var(--surface-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-content[data-doc-style="modern"] blockquote,
|
||||||
|
.preview-content.doc-style-modern blockquote {
|
||||||
|
border-left: 4px solid var(--accent);
|
||||||
|
background: linear-gradient(90deg, rgba(79, 70, 229, 0.08) 0%, transparent 100%);
|
||||||
|
border-radius: 0 10px 10px 0;
|
||||||
|
padding: 0.75rem 1.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* === PRESET 2: ACADEMIC (LaTeX / Formal Research Paper) === */
|
||||||
|
.preview-content[data-doc-style="academic"],
|
||||||
|
.preview-content.doc-style-academic {
|
||||||
|
font-family: 'Merriweather', 'Georgia', 'Cambria', 'Times New Roman', serif;
|
||||||
|
line-height: 1.9;
|
||||||
|
font-size: 1.05rem;
|
||||||
|
background-color: #fdfdf9;
|
||||||
|
color: #1a1a1a;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: inset 0 0 40px rgba(0, 0, 0, 0.02);
|
||||||
|
padding: 2.5rem 3rem;
|
||||||
|
text-align: justify;
|
||||||
|
}
|
||||||
|
|
||||||
|
:root[data-theme="dark"] .preview-content[data-doc-style="academic"],
|
||||||
|
:root[data-theme="dark"] .preview-content.doc-style-academic {
|
||||||
|
background-color: #121110;
|
||||||
|
color: #e5e0d8;
|
||||||
|
box-shadow: inset 0 0 40px rgba(255, 255, 255, 0.01);
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-content[data-doc-style="academic"] h1,
|
||||||
|
.preview-content.doc-style-academic h1 {
|
||||||
|
font-family: 'Merriweather', 'Georgia', serif;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 2.1rem;
|
||||||
|
font-weight: 700;
|
||||||
|
letter-spacing: 0.02em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
border-top: 3px double currentColor;
|
||||||
|
border-bottom: 1px solid currentColor;
|
||||||
|
padding: 0.8rem 0;
|
||||||
|
margin: 1.8em 0 1.2em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-content[data-doc-style="academic"] h2,
|
||||||
|
.preview-content.doc-style-academic h2 {
|
||||||
|
font-family: 'Merriweather', 'Georgia', serif;
|
||||||
|
font-size: 1.45rem;
|
||||||
|
font-weight: 700;
|
||||||
|
font-variant: small-caps;
|
||||||
|
letter-spacing: 0.06em;
|
||||||
|
border-bottom: 1px solid currentColor;
|
||||||
|
padding-bottom: 0.25em;
|
||||||
|
margin-top: 1.6em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-content[data-doc-style="academic"] h3,
|
||||||
|
.preview-content.doc-style-academic h3 {
|
||||||
|
font-family: 'Merriweather', 'Georgia', serif;
|
||||||
|
font-size: 1.2rem;
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-top: 1.3em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Classical Academic Tables */
|
||||||
|
.preview-content[data-doc-style="academic"] table,
|
||||||
|
.preview-content.doc-style-academic table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
border: 1px solid rgba(128, 128, 128, 0.4);
|
||||||
|
border-top: 2.5px solid currentColor;
|
||||||
|
border-bottom: 2.5px solid currentColor;
|
||||||
|
margin: 2em auto;
|
||||||
|
font-size: 0.95rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-content[data-doc-style="academic"] th,
|
||||||
|
.preview-content.doc-style-academic th {
|
||||||
|
border: 1px solid rgba(128, 128, 128, 0.35);
|
||||||
|
border-bottom: 2px solid currentColor;
|
||||||
|
font-variant: small-caps;
|
||||||
|
font-size: 0.88rem;
|
||||||
|
letter-spacing: 0.08em;
|
||||||
|
background: rgba(0, 0, 0, 0.03);
|
||||||
|
padding: 0.65rem 1rem;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-content[data-doc-style="academic"] td,
|
||||||
|
.preview-content.doc-style-academic td {
|
||||||
|
border: 1px solid rgba(128, 128, 128, 0.25);
|
||||||
|
padding: 0.6rem 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-content[data-doc-style="academic"] blockquote,
|
||||||
|
.preview-content.doc-style-academic blockquote {
|
||||||
|
font-style: italic;
|
||||||
|
border-left: none;
|
||||||
|
background: transparent;
|
||||||
|
padding: 0.8rem 2.5rem;
|
||||||
|
margin: 1.8em 0;
|
||||||
|
position: relative;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-content[data-doc-style="academic"] blockquote::before {
|
||||||
|
content: "“";
|
||||||
|
font-size: 3.5rem;
|
||||||
|
line-height: 0;
|
||||||
|
position: absolute;
|
||||||
|
left: 0.5rem;
|
||||||
|
top: 1.5rem;
|
||||||
|
opacity: 0.35;
|
||||||
|
font-family: Georgia, serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* === PRESET 3: TECHNICAL (Cyber / Terminal / Dev Spec) === */
|
||||||
|
.preview-content[data-doc-style="technical"],
|
||||||
|
.preview-content.doc-style-technical {
|
||||||
|
font-family: 'JetBrains Mono', 'Fira Code', 'Cascadia Code', 'SF Mono', Consolas, monospace;
|
||||||
|
font-size: 0.92rem;
|
||||||
|
line-height: 1.65;
|
||||||
|
background-color: #0d1117;
|
||||||
|
color: #c9d1d9;
|
||||||
|
border: 1px solid #30363d;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 2rem 2.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-content[data-doc-style="technical"] h1,
|
||||||
|
.preview-content.doc-style-technical h1 {
|
||||||
|
font-family: 'JetBrains Mono', monospace;
|
||||||
|
color: #58a6ff;
|
||||||
|
font-size: 1.8rem;
|
||||||
|
font-weight: 700;
|
||||||
|
border-bottom: 2px solid #238636;
|
||||||
|
padding-bottom: 0.4em;
|
||||||
|
letter-spacing: -0.02em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-content[data-doc-style="technical"] h1::before {
|
||||||
|
content: "#!/usr/bin/spec >> ";
|
||||||
|
color: #7ee787;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
display: block;
|
||||||
|
font-weight: 400;
|
||||||
|
margin-bottom: 0.3rem;
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-content[data-doc-style="technical"] h2,
|
||||||
|
.preview-content.doc-style-technical h2 {
|
||||||
|
font-family: 'JetBrains Mono', monospace;
|
||||||
|
color: #79c0ff;
|
||||||
|
font-size: 1.35rem;
|
||||||
|
border-bottom: 1px dashed #30363d;
|
||||||
|
padding-bottom: 0.3em;
|
||||||
|
margin-top: 1.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-content[data-doc-style="technical"] h2::before {
|
||||||
|
content: "## ";
|
||||||
|
color: #7ee787;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-content[data-doc-style="technical"] h3,
|
||||||
|
.preview-content.doc-style-technical h3 {
|
||||||
|
font-family: 'JetBrains Mono', monospace;
|
||||||
|
color: #d2a8ff;
|
||||||
|
font-size: 1.15rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-content[data-doc-style="technical"] h3::before {
|
||||||
|
content: "fn ";
|
||||||
|
color: #ff7b72;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Terminal Matrix Grid Table */
|
||||||
|
.preview-content[data-doc-style="technical"] table,
|
||||||
|
.preview-content.doc-style-technical table {
|
||||||
|
border: 1px solid #30363d;
|
||||||
|
background-color: #161b22;
|
||||||
|
font-family: 'JetBrains Mono', monospace;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-content[data-doc-style="technical"] th,
|
||||||
|
.preview-content.doc-style-technical th {
|
||||||
|
background-color: #21262d;
|
||||||
|
color: #7ee787;
|
||||||
|
border: 1px solid #30363d;
|
||||||
|
padding: 0.5rem 0.85rem;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-content[data-doc-style="technical"] td,
|
||||||
|
.preview-content.doc-style-technical td {
|
||||||
|
border: 1px solid #30363d;
|
||||||
|
padding: 0.5rem 0.85rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-content[data-doc-style="technical"] pre,
|
||||||
|
.preview-content.doc-style-technical pre {
|
||||||
|
background-color: #030712 !important;
|
||||||
|
border: 1px solid #06b6d4 !important;
|
||||||
|
box-shadow: 0 0 15px rgba(6, 182, 212, 0.12);
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-content[data-doc-style="technical"] blockquote,
|
||||||
|
.preview-content.doc-style-technical blockquote {
|
||||||
|
border-left: 4px solid #238636;
|
||||||
|
background-color: rgba(35, 134, 54, 0.1);
|
||||||
|
color: #7ee787;
|
||||||
|
border-radius: 0 4px 4px 0;
|
||||||
|
padding: 0.6rem 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* === PRESET 4: EXECUTIVE (Corporate Brief / Wall Street / McKinsey) === */
|
||||||
|
.preview-content[data-doc-style="executive"],
|
||||||
|
.preview-content.doc-style-executive {
|
||||||
|
font-family: 'Plus Jakarta Sans', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
||||||
|
line-height: 1.75;
|
||||||
|
color: #0f172a;
|
||||||
|
background-color: #ffffff;
|
||||||
|
border-top: 5px solid #0f2744;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 10px 25px -5px rgba(15, 39, 68, 0.08);
|
||||||
|
padding: 2.5rem 3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
:root[data-theme="dark"] .preview-content[data-doc-style="executive"],
|
||||||
|
:root[data-theme="dark"] .preview-content.doc-style-executive {
|
||||||
|
color: #f1f5f9;
|
||||||
|
background-color: #0f172a;
|
||||||
|
border-top: 5px solid #38bdf8;
|
||||||
|
box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-content[data-doc-style="executive"] h1,
|
||||||
|
.preview-content.doc-style-executive h1 {
|
||||||
|
font-size: 2.3rem;
|
||||||
|
font-weight: 800;
|
||||||
|
color: #0f2744;
|
||||||
|
letter-spacing: -0.02em;
|
||||||
|
border-bottom: 3px solid #d97706;
|
||||||
|
padding-bottom: 0.4em;
|
||||||
|
margin-top: 1.4em;
|
||||||
|
}
|
||||||
|
|
||||||
|
:root[data-theme="dark"] .preview-content[data-doc-style="executive"] h1,
|
||||||
|
:root[data-theme="dark"] .preview-content.doc-style-executive h1 {
|
||||||
|
color: #38bdf8;
|
||||||
|
border-bottom-color: #fbbf24;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-content[data-doc-style="executive"] h1::before {
|
||||||
|
content: "EXECUTIVE BRIEFING // CONFIDENTIAL";
|
||||||
|
display: block;
|
||||||
|
font-size: 0.72rem;
|
||||||
|
font-weight: 800;
|
||||||
|
letter-spacing: 0.15em;
|
||||||
|
color: #d97706;
|
||||||
|
margin-bottom: 0.4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-content[data-doc-style="executive"] h2,
|
||||||
|
.preview-content.doc-style-executive h2 {
|
||||||
|
font-size: 1.35rem;
|
||||||
|
font-weight: 800;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.08em;
|
||||||
|
color: #0f2744;
|
||||||
|
border-left: 4px solid #d97706;
|
||||||
|
padding-left: 0.75rem;
|
||||||
|
margin-top: 1.6em;
|
||||||
|
}
|
||||||
|
|
||||||
|
:root[data-theme="dark"] .preview-content[data-doc-style="executive"] h2,
|
||||||
|
:root[data-theme="dark"] .preview-content.doc-style-executive h2 {
|
||||||
|
color: #38bdf8;
|
||||||
|
border-left-color: #fbbf24;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-content[data-doc-style="executive"] table,
|
||||||
|
.preview-content.doc-style-executive table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
border: 1.5px solid #cbd5e1;
|
||||||
|
box-shadow: 0 4px 6px -1px rgba(15, 39, 68, 0.06);
|
||||||
|
border-radius: 6px;
|
||||||
|
overflow: hidden;
|
||||||
|
margin: 1.5em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
:root[data-theme="dark"] .preview-content[data-doc-style="executive"] table,
|
||||||
|
:root[data-theme="dark"] .preview-content.doc-style-executive table {
|
||||||
|
border-color: #334155;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-content[data-doc-style="executive"] th,
|
||||||
|
.preview-content.doc-style-executive th {
|
||||||
|
background-color: #0f2744;
|
||||||
|
color: #ffffff;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
font-weight: 700;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.08em;
|
||||||
|
padding: 0.75rem 1rem;
|
||||||
|
border: 1px solid #1e3a5f;
|
||||||
|
}
|
||||||
|
|
||||||
|
:root[data-theme="dark"] .preview-content[data-doc-style="executive"] th,
|
||||||
|
:root[data-theme="dark"] .preview-content.doc-style-executive th {
|
||||||
|
background-color: #1e293b;
|
||||||
|
color: #38bdf8;
|
||||||
|
border: 1px solid #334155;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-content[data-doc-style="executive"] td,
|
||||||
|
.preview-content.doc-style-executive td {
|
||||||
|
padding: 0.65rem 1rem;
|
||||||
|
border: 1px solid #cbd5e1;
|
||||||
|
}
|
||||||
|
|
||||||
|
:root[data-theme="dark"] .preview-content[data-doc-style="executive"] td,
|
||||||
|
:root[data-theme="dark"] .preview-content.doc-style-executive td {
|
||||||
|
border: 1px solid #334155;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-content[data-doc-style="executive"] tr:nth-child(even) td {
|
||||||
|
background-color: rgba(15, 39, 68, 0.03);
|
||||||
|
}
|
||||||
|
|
||||||
|
:root[data-theme="dark"] .preview-content[data-doc-style="executive"] tr:nth-child(even) td {
|
||||||
|
background-color: rgba(255, 255, 255, 0.03);
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-content[data-doc-style="executive"] blockquote,
|
||||||
|
.preview-content.doc-style-executive blockquote {
|
||||||
|
border-left: 4px solid #0f2744;
|
||||||
|
background-color: rgba(15, 39, 68, 0.05);
|
||||||
|
border-radius: 0 8px 8px 0;
|
||||||
|
font-weight: 500;
|
||||||
|
padding: 1rem 1.4rem;
|
||||||
|
border-right: 1px solid rgba(15, 39, 68, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
:root[data-theme="dark"] .preview-content[data-doc-style="executive"] blockquote,
|
||||||
|
:root[data-theme="dark"] .preview-content.doc-style-executive blockquote {
|
||||||
|
border-left-color: #38bdf8;
|
||||||
|
background-color: rgba(56, 189, 248, 0.08);
|
||||||
|
border-right-color: rgba(56, 189, 248, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
/* ========================================================= */
|
/* ========================================================= */
|
||||||
/* PRINT / PDF EXPORT MEDIA QUERY STYLES */
|
/* PRINT / PDF EXPORT MEDIA QUERY STYLES */
|
||||||
/* ========================================================= */
|
/* ========================================================= */
|
||||||
@@ -879,6 +1513,7 @@
|
|||||||
.editor-pane,
|
.editor-pane,
|
||||||
.notes-drawer,
|
.notes-drawer,
|
||||||
.drawer-backdrop,
|
.drawer-backdrop,
|
||||||
|
.preview-header-bar,
|
||||||
.code-copy-btn {
|
.code-copy-btn {
|
||||||
display: none !important;
|
display: none !important;
|
||||||
}
|
}
|
||||||
@@ -897,6 +1532,7 @@
|
|||||||
border: none !important;
|
border: none !important;
|
||||||
background: none !important;
|
background: none !important;
|
||||||
display: block !important;
|
display: block !important;
|
||||||
|
width: 100% !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.preview-content {
|
.preview-content {
|
||||||
@@ -905,6 +1541,11 @@
|
|||||||
overflow: visible !important;
|
overflow: visible !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.preview-content h1,
|
||||||
|
.preview-content h2 {
|
||||||
|
page-break-after: avoid;
|
||||||
|
}
|
||||||
|
|
||||||
.preview-content pre {
|
.preview-content pre {
|
||||||
border: 1px solid #ddd !important;
|
border: 1px solid #ddd !important;
|
||||||
background-color: #f8f9fa !important;
|
background-color: #f8f9fa !important;
|
||||||
@@ -914,4 +1555,8 @@
|
|||||||
.preview-content table {
|
.preview-content table {
|
||||||
page-break-inside: avoid;
|
page-break-inside: avoid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.preview-content .markdown-alert {
|
||||||
|
page-break-inside: avoid;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
@using Notemark.Services
|
@using Notemark.Services
|
||||||
@inject IShareService ShareService
|
@inject IShareService ShareService
|
||||||
@inject ISessionService SessionService
|
@inject ISessionService SessionService
|
||||||
|
@inject IDocumentService DocumentService
|
||||||
@inject IMarkdownService MarkdownService
|
@inject IMarkdownService MarkdownService
|
||||||
@inject NavigationManager Navigation
|
@inject NavigationManager Navigation
|
||||||
@inject IJSRuntime JSRuntime
|
@inject IJSRuntime JSRuntime
|
||||||
@@ -70,7 +71,7 @@ else
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<div class="preview-content" style="@GetPreviewContentStyle()">
|
<div class="preview-content doc-style-@documentStyle" data-doc-style="@documentStyle" style="@GetPreviewContentStyle()">
|
||||||
@((MarkupString)renderedHtml)
|
@((MarkupString)renderedHtml)
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
@@ -86,6 +87,7 @@ else
|
|||||||
private ShareToken? shareToken = null;
|
private ShareToken? shareToken = null;
|
||||||
private string markdownContent = "";
|
private string markdownContent = "";
|
||||||
private string renderedHtml = "";
|
private string renderedHtml = "";
|
||||||
|
private string documentStyle = "modern";
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
protected override async Task OnInitializedAsync()
|
||||||
{
|
{
|
||||||
@@ -120,8 +122,10 @@ else
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load the document content
|
// Load the document content & metadata
|
||||||
markdownContent = await SessionService.LoadSessionContentAsync(shareToken.SessionId);
|
markdownContent = await SessionService.LoadSessionContentAsync(shareToken.SessionId);
|
||||||
|
var meta = await DocumentService.GetDocumentAsync(shareToken.SessionId);
|
||||||
|
documentStyle = meta?.DocStyle ?? "modern";
|
||||||
|
|
||||||
// Render to HTML
|
// Render to HTML
|
||||||
renderedHtml = MarkdownService.RenderToHtml(markdownContent);
|
renderedHtml = MarkdownService.RenderToHtml(markdownContent);
|
||||||
|
|||||||
@@ -327,34 +327,26 @@
|
|||||||
|
|
||||||
.preview-content table {
|
.preview-content table {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
border-collapse: separate;
|
border-collapse: collapse;
|
||||||
border-spacing: 0;
|
|
||||||
margin: 1.5em 0;
|
margin: 1.5em 0;
|
||||||
border: 1px solid var(--border);
|
border: 1px solid var(--border);
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
font-size: 0.9375rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.preview-content th,
|
.preview-content th,
|
||||||
.preview-content td {
|
.preview-content td {
|
||||||
padding: 0.75rem 1rem;
|
padding: 0.75rem 1rem;
|
||||||
border-bottom: 1px solid var(--border);
|
border: 1px solid var(--border);
|
||||||
border-right: 1px solid var(--border);
|
|
||||||
text-align: left;
|
text-align: left;
|
||||||
}
|
}
|
||||||
|
|
||||||
.preview-content th:last-child,
|
|
||||||
.preview-content td:last-child {
|
|
||||||
border-right: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.preview-content tr:last-child td {
|
|
||||||
border-bottom: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.preview-content th {
|
.preview-content th {
|
||||||
background-color: var(--surface-hover);
|
background-color: var(--surface-hover);
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
|
color: var(--primary-text);
|
||||||
|
border-bottom: 2px solid var(--border);
|
||||||
}
|
}
|
||||||
|
|
||||||
.preview-content tr:hover td {
|
.preview-content tr:hover td {
|
||||||
@@ -385,6 +377,459 @@
|
|||||||
margin: 1em 0;
|
margin: 1em 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ========================================================= */
|
||||||
|
/* DOCUMENT PRESET STYLES & GITHUB ALERTS */
|
||||||
|
/* ========================================================= */
|
||||||
|
|
||||||
|
/* Callouts / GitHub Alerts */
|
||||||
|
.preview-content .markdown-alert {
|
||||||
|
padding: 0.75rem 1rem;
|
||||||
|
margin: 1.25rem 0;
|
||||||
|
border-left: 4px solid;
|
||||||
|
border-radius: 0 6px 6px 0;
|
||||||
|
background-color: var(--surface-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-content .markdown-alert-title {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-bottom: 0.35rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-content .markdown-alert-note { border-left-color: #3b82f6; background-color: rgba(59, 130, 246, 0.08); }
|
||||||
|
.preview-content .markdown-alert-tip { border-left-color: #10b981; background-color: rgba(16, 185, 129, 0.08); }
|
||||||
|
.preview-content .markdown-alert-important { border-left-color: #8b5cf6; background-color: rgba(139, 92, 246, 0.08); }
|
||||||
|
.preview-content .markdown-alert-warning { border-left-color: #f59e0b; background-color: rgba(245, 158, 11, 0.08); }
|
||||||
|
.preview-content .markdown-alert-caution { border-left-color: #ef4444; background-color: rgba(239, 68, 68, 0.08); }
|
||||||
|
|
||||||
|
/* ========================================================= */
|
||||||
|
/* DISTINCT DOCUMENT PRESET STYLES */
|
||||||
|
/* ========================================================= */
|
||||||
|
|
||||||
|
/* === PRESET 1: MODERN (Clean SaaS / Notion Look) === */
|
||||||
|
.preview-content[data-doc-style="modern"],
|
||||||
|
.preview-content.doc-style-modern {
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
||||||
|
line-height: 1.7;
|
||||||
|
color: var(--primary-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-content[data-doc-style="modern"] h1,
|
||||||
|
.preview-content.doc-style-modern h1 {
|
||||||
|
font-size: 2.3rem;
|
||||||
|
font-weight: 800;
|
||||||
|
letter-spacing: -0.03em;
|
||||||
|
background: linear-gradient(135deg, var(--primary-text) 60%, var(--accent));
|
||||||
|
-webkit-background-clip: text;
|
||||||
|
-webkit-text-fill-color: transparent;
|
||||||
|
border-bottom: 2px solid var(--accent);
|
||||||
|
padding-bottom: 0.4em;
|
||||||
|
margin-top: 1.4em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-content[data-doc-style="modern"] h2,
|
||||||
|
.preview-content.doc-style-modern h2 {
|
||||||
|
font-size: 1.6rem;
|
||||||
|
font-weight: 700;
|
||||||
|
letter-spacing: -0.02em;
|
||||||
|
border-bottom: 1px solid var(--border);
|
||||||
|
padding-bottom: 0.3em;
|
||||||
|
margin-top: 1.3em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-content[data-doc-style="modern"] table,
|
||||||
|
.preview-content.doc-style-modern table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
border: 1.5px solid var(--border);
|
||||||
|
border-radius: 8px;
|
||||||
|
overflow: hidden;
|
||||||
|
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.05);
|
||||||
|
margin: 1.5em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-content[data-doc-style="modern"] th,
|
||||||
|
.preview-content.doc-style-modern th {
|
||||||
|
background-color: var(--surface-hover);
|
||||||
|
color: var(--primary-text);
|
||||||
|
font-weight: 600;
|
||||||
|
padding: 0.75rem 1rem;
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-bottom: 2px solid var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-content[data-doc-style="modern"] td,
|
||||||
|
.preview-content.doc-style-modern td {
|
||||||
|
padding: 0.7rem 1rem;
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-content[data-doc-style="modern"] tr:hover td,
|
||||||
|
.preview-content.doc-style-modern tr:hover td {
|
||||||
|
background-color: var(--surface-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-content[data-doc-style="modern"] blockquote,
|
||||||
|
.preview-content.doc-style-modern blockquote {
|
||||||
|
border-left: 4px solid var(--accent);
|
||||||
|
background: linear-gradient(90deg, rgba(79, 70, 229, 0.08) 0%, transparent 100%);
|
||||||
|
border-radius: 0 10px 10px 0;
|
||||||
|
padding: 0.75rem 1.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* === PRESET 2: ACADEMIC (LaTeX / Formal Research Paper) === */
|
||||||
|
.preview-content[data-doc-style="academic"],
|
||||||
|
.preview-content.doc-style-academic {
|
||||||
|
font-family: 'Merriweather', 'Georgia', 'Cambria', 'Times New Roman', serif;
|
||||||
|
line-height: 1.9;
|
||||||
|
font-size: 1.05rem;
|
||||||
|
background-color: #fdfdf9;
|
||||||
|
color: #1a1a1a;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: inset 0 0 40px rgba(0, 0, 0, 0.02);
|
||||||
|
padding: 2.5rem 3rem;
|
||||||
|
text-align: justify;
|
||||||
|
}
|
||||||
|
|
||||||
|
:root[data-theme="dark"] .preview-content[data-doc-style="academic"],
|
||||||
|
:root[data-theme="dark"] .preview-content.doc-style-academic {
|
||||||
|
background-color: #121110;
|
||||||
|
color: #e5e0d8;
|
||||||
|
box-shadow: inset 0 0 40px rgba(255, 255, 255, 0.01);
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-content[data-doc-style="academic"] h1,
|
||||||
|
.preview-content.doc-style-academic h1 {
|
||||||
|
font-family: 'Merriweather', 'Georgia', serif;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 2.1rem;
|
||||||
|
font-weight: 700;
|
||||||
|
letter-spacing: 0.02em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
border-top: 3px double currentColor;
|
||||||
|
border-bottom: 1px solid currentColor;
|
||||||
|
padding: 0.8rem 0;
|
||||||
|
margin: 1.8em 0 1.2em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-content[data-doc-style="academic"] h2,
|
||||||
|
.preview-content.doc-style-academic h2 {
|
||||||
|
font-family: 'Merriweather', 'Georgia', serif;
|
||||||
|
font-size: 1.45rem;
|
||||||
|
font-weight: 700;
|
||||||
|
font-variant: small-caps;
|
||||||
|
letter-spacing: 0.06em;
|
||||||
|
border-bottom: 1px solid currentColor;
|
||||||
|
padding-bottom: 0.25em;
|
||||||
|
margin-top: 1.6em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-content[data-doc-style="academic"] h3,
|
||||||
|
.preview-content.doc-style-academic h3 {
|
||||||
|
font-family: 'Merriweather', 'Georgia', serif;
|
||||||
|
font-size: 1.2rem;
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-top: 1.3em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Classical Academic Tables */
|
||||||
|
.preview-content[data-doc-style="academic"] table,
|
||||||
|
.preview-content.doc-style-academic table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
border: 1px solid rgba(128, 128, 128, 0.4);
|
||||||
|
border-top: 2.5px solid currentColor;
|
||||||
|
border-bottom: 2.5px solid currentColor;
|
||||||
|
margin: 2em auto;
|
||||||
|
font-size: 0.95rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-content[data-doc-style="academic"] th,
|
||||||
|
.preview-content.doc-style-academic th {
|
||||||
|
border: 1px solid rgba(128, 128, 128, 0.35);
|
||||||
|
border-bottom: 2px solid currentColor;
|
||||||
|
font-variant: small-caps;
|
||||||
|
font-size: 0.88rem;
|
||||||
|
letter-spacing: 0.08em;
|
||||||
|
background: rgba(0, 0, 0, 0.03);
|
||||||
|
padding: 0.65rem 1rem;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-content[data-doc-style="academic"] td,
|
||||||
|
.preview-content.doc-style-academic td {
|
||||||
|
border: 1px solid rgba(128, 128, 128, 0.25);
|
||||||
|
padding: 0.6rem 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-content[data-doc-style="academic"] blockquote,
|
||||||
|
.preview-content.doc-style-academic blockquote {
|
||||||
|
font-style: italic;
|
||||||
|
border-left: none;
|
||||||
|
background: transparent;
|
||||||
|
padding: 0.8rem 2.5rem;
|
||||||
|
margin: 1.8em 0;
|
||||||
|
position: relative;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-content[data-doc-style="academic"] blockquote::before {
|
||||||
|
content: "“";
|
||||||
|
font-size: 3.5rem;
|
||||||
|
line-height: 0;
|
||||||
|
position: absolute;
|
||||||
|
left: 0.5rem;
|
||||||
|
top: 1.5rem;
|
||||||
|
opacity: 0.35;
|
||||||
|
font-family: Georgia, serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* === PRESET 3: TECHNICAL (Cyber / Terminal / Dev Spec) === */
|
||||||
|
.preview-content[data-doc-style="technical"],
|
||||||
|
.preview-content.doc-style-technical {
|
||||||
|
font-family: 'JetBrains Mono', 'Fira Code', 'Cascadia Code', 'SF Mono', Consolas, monospace;
|
||||||
|
font-size: 0.92rem;
|
||||||
|
line-height: 1.65;
|
||||||
|
background-color: #0d1117;
|
||||||
|
color: #c9d1d9;
|
||||||
|
border: 1px solid #30363d;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 2rem 2.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-content[data-doc-style="technical"] h1,
|
||||||
|
.preview-content.doc-style-technical h1 {
|
||||||
|
font-family: 'JetBrains Mono', monospace;
|
||||||
|
color: #58a6ff;
|
||||||
|
font-size: 1.8rem;
|
||||||
|
font-weight: 700;
|
||||||
|
border-bottom: 2px solid #238636;
|
||||||
|
padding-bottom: 0.4em;
|
||||||
|
letter-spacing: -0.02em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-content[data-doc-style="technical"] h1::before {
|
||||||
|
content: "#!/usr/bin/spec >> ";
|
||||||
|
color: #7ee787;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
display: block;
|
||||||
|
font-weight: 400;
|
||||||
|
margin-bottom: 0.3rem;
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-content[data-doc-style="technical"] h2,
|
||||||
|
.preview-content.doc-style-technical h2 {
|
||||||
|
font-family: 'JetBrains Mono', monospace;
|
||||||
|
color: #79c0ff;
|
||||||
|
font-size: 1.35rem;
|
||||||
|
border-bottom: 1px dashed #30363d;
|
||||||
|
padding-bottom: 0.3em;
|
||||||
|
margin-top: 1.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-content[data-doc-style="technical"] h2::before {
|
||||||
|
content: "## ";
|
||||||
|
color: #7ee787;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-content[data-doc-style="technical"] h3,
|
||||||
|
.preview-content.doc-style-technical h3 {
|
||||||
|
font-family: 'JetBrains Mono', monospace;
|
||||||
|
color: #d2a8ff;
|
||||||
|
font-size: 1.15rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-content[data-doc-style="technical"] h3::before {
|
||||||
|
content: "fn ";
|
||||||
|
color: #ff7b72;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Terminal Matrix Grid Table */
|
||||||
|
.preview-content[data-doc-style="technical"] table,
|
||||||
|
.preview-content.doc-style-technical table {
|
||||||
|
border: 1px solid #30363d;
|
||||||
|
background-color: #161b22;
|
||||||
|
font-family: 'JetBrains Mono', monospace;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-content[data-doc-style="technical"] th,
|
||||||
|
.preview-content.doc-style-technical th {
|
||||||
|
background-color: #21262d;
|
||||||
|
color: #7ee787;
|
||||||
|
border: 1px solid #30363d;
|
||||||
|
padding: 0.5rem 0.85rem;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-content[data-doc-style="technical"] td,
|
||||||
|
.preview-content.doc-style-technical td {
|
||||||
|
border: 1px solid #30363d;
|
||||||
|
padding: 0.5rem 0.85rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-content[data-doc-style="technical"] pre,
|
||||||
|
.preview-content.doc-style-technical pre {
|
||||||
|
background-color: #030712 !important;
|
||||||
|
border: 1px solid #06b6d4 !important;
|
||||||
|
box-shadow: 0 0 15px rgba(6, 182, 212, 0.12);
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-content[data-doc-style="technical"] blockquote,
|
||||||
|
.preview-content.doc-style-technical blockquote {
|
||||||
|
border-left: 4px solid #238636;
|
||||||
|
background-color: rgba(35, 134, 54, 0.1);
|
||||||
|
color: #7ee787;
|
||||||
|
border-radius: 0 4px 4px 0;
|
||||||
|
padding: 0.6rem 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* === PRESET 4: EXECUTIVE (Corporate Brief / Wall Street / McKinsey) === */
|
||||||
|
.preview-content[data-doc-style="executive"],
|
||||||
|
.preview-content.doc-style-executive {
|
||||||
|
font-family: 'Plus Jakarta Sans', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
||||||
|
line-height: 1.75;
|
||||||
|
color: #0f172a;
|
||||||
|
background-color: #ffffff;
|
||||||
|
border-top: 5px solid #0f2744;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 10px 25px -5px rgba(15, 39, 68, 0.08);
|
||||||
|
padding: 2.5rem 3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
:root[data-theme="dark"] .preview-content[data-doc-style="executive"],
|
||||||
|
:root[data-theme="dark"] .preview-content.doc-style-executive {
|
||||||
|
color: #f1f5f9;
|
||||||
|
background-color: #0f172a;
|
||||||
|
border-top: 5px solid #38bdf8;
|
||||||
|
box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-content[data-doc-style="executive"] h1,
|
||||||
|
.preview-content.doc-style-executive h1 {
|
||||||
|
font-size: 2.3rem;
|
||||||
|
font-weight: 800;
|
||||||
|
color: #0f2744;
|
||||||
|
letter-spacing: -0.02em;
|
||||||
|
border-bottom: 3px solid #d97706;
|
||||||
|
padding-bottom: 0.4em;
|
||||||
|
margin-top: 1.4em;
|
||||||
|
}
|
||||||
|
|
||||||
|
:root[data-theme="dark"] .preview-content[data-doc-style="executive"] h1,
|
||||||
|
:root[data-theme="dark"] .preview-content.doc-style-executive h1 {
|
||||||
|
color: #38bdf8;
|
||||||
|
border-bottom-color: #fbbf24;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-content[data-doc-style="executive"] h1::before {
|
||||||
|
content: "EXECUTIVE BRIEFING // CONFIDENTIAL";
|
||||||
|
display: block;
|
||||||
|
font-size: 0.72rem;
|
||||||
|
font-weight: 800;
|
||||||
|
letter-spacing: 0.15em;
|
||||||
|
color: #d97706;
|
||||||
|
margin-bottom: 0.4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-content[data-doc-style="executive"] h2,
|
||||||
|
.preview-content.doc-style-executive h2 {
|
||||||
|
font-size: 1.35rem;
|
||||||
|
font-weight: 800;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.08em;
|
||||||
|
color: #0f2744;
|
||||||
|
border-left: 4px solid #d97706;
|
||||||
|
padding-left: 0.75rem;
|
||||||
|
margin-top: 1.6em;
|
||||||
|
}
|
||||||
|
|
||||||
|
:root[data-theme="dark"] .preview-content[data-doc-style="executive"] h2,
|
||||||
|
:root[data-theme="dark"] .preview-content.doc-style-executive h2 {
|
||||||
|
color: #38bdf8;
|
||||||
|
border-left-color: #fbbf24;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-content[data-doc-style="executive"] table,
|
||||||
|
.preview-content.doc-style-executive table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
border: 1.5px solid #cbd5e1;
|
||||||
|
box-shadow: 0 4px 6px -1px rgba(15, 39, 68, 0.06);
|
||||||
|
border-radius: 6px;
|
||||||
|
overflow: hidden;
|
||||||
|
margin: 1.5em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
:root[data-theme="dark"] .preview-content[data-doc-style="executive"] table,
|
||||||
|
:root[data-theme="dark"] .preview-content.doc-style-executive table {
|
||||||
|
border-color: #334155;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-content[data-doc-style="executive"] th,
|
||||||
|
.preview-content.doc-style-executive th {
|
||||||
|
background-color: #0f2744;
|
||||||
|
color: #ffffff;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
font-weight: 700;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.08em;
|
||||||
|
padding: 0.75rem 1rem;
|
||||||
|
border: 1px solid #1e3a5f;
|
||||||
|
}
|
||||||
|
|
||||||
|
:root[data-theme="dark"] .preview-content[data-doc-style="executive"] th,
|
||||||
|
:root[data-theme="dark"] .preview-content.doc-style-executive th {
|
||||||
|
background-color: #1e293b;
|
||||||
|
color: #38bdf8;
|
||||||
|
border: 1px solid #334155;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-content[data-doc-style="executive"] td,
|
||||||
|
.preview-content.doc-style-executive td {
|
||||||
|
padding: 0.65rem 1rem;
|
||||||
|
border: 1px solid #cbd5e1;
|
||||||
|
}
|
||||||
|
|
||||||
|
:root[data-theme="dark"] .preview-content[data-doc-style="executive"] td,
|
||||||
|
:root[data-theme="dark"] .preview-content.doc-style-executive td {
|
||||||
|
border: 1px solid #334155;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-content[data-doc-style="executive"] tr:nth-child(even) td {
|
||||||
|
background-color: rgba(15, 39, 68, 0.03);
|
||||||
|
}
|
||||||
|
|
||||||
|
:root[data-theme="dark"] .preview-content[data-doc-style="executive"] tr:nth-child(even) td {
|
||||||
|
background-color: rgba(255, 255, 255, 0.03);
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-content[data-doc-style="executive"] blockquote,
|
||||||
|
.preview-content.doc-style-executive blockquote {
|
||||||
|
border-left: 4px solid #0f2744;
|
||||||
|
background-color: rgba(15, 39, 68, 0.05);
|
||||||
|
border-radius: 0 8px 8px 0;
|
||||||
|
font-weight: 500;
|
||||||
|
padding: 1rem 1.4rem;
|
||||||
|
border-right: 1px solid rgba(15, 39, 68, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
:root[data-theme="dark"] .preview-content[data-doc-style="executive"] blockquote,
|
||||||
|
:root[data-theme="dark"] .preview-content.doc-style-executive blockquote {
|
||||||
|
border-left-color: #38bdf8;
|
||||||
|
background-color: rgba(56, 189, 248, 0.08);
|
||||||
|
border-right-color: rgba(56, 189, 248, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
.shared-header {
|
.shared-header {
|
||||||
padding: 0.75rem 1rem;
|
padding: 0.75rem 1rem;
|
||||||
|
|||||||
@@ -9,4 +9,5 @@ public record DocumentMetadata
|
|||||||
public DateTime CreatedAt { get; init; } = DateTime.UtcNow;
|
public DateTime CreatedAt { get; init; } = DateTime.UtcNow;
|
||||||
public DateTime LastModifiedAt { get; init; } = DateTime.UtcNow;
|
public DateTime LastModifiedAt { get; init; } = DateTime.UtcNow;
|
||||||
public string Snippet { get; init; } = "";
|
public string Snippet { get; init; } = "";
|
||||||
|
public string DocStyle { get; init; } = "modern";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ builder.Services.AddHttpContextAccessor();
|
|||||||
|
|
||||||
// Register Notemark services
|
// Register Notemark services
|
||||||
builder.Services.AddSingleton<IMarkdownService, MarkdownService>();
|
builder.Services.AddSingleton<IMarkdownService, MarkdownService>();
|
||||||
|
builder.Services.AddSingleton<IAutoStyleService, AutoStyleService>();
|
||||||
builder.Services.AddSingleton<IDocumentService, DocumentService>();
|
builder.Services.AddSingleton<IDocumentService, DocumentService>();
|
||||||
builder.Services.AddScoped<ISessionService, SessionService>();
|
builder.Services.AddScoped<ISessionService, SessionService>();
|
||||||
builder.Services.AddScoped<IShareService, ShareService>();
|
builder.Services.AddScoped<IShareService, ShareService>();
|
||||||
|
|||||||
@@ -0,0 +1,473 @@
|
|||||||
|
using System.Text;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
|
namespace Notemark.Services;
|
||||||
|
|
||||||
|
public class AutoStyleService : IAutoStyleService
|
||||||
|
{
|
||||||
|
private static readonly Regex HeadingRegex = new(@"^(#{1,6})([^\s#].*)$", RegexOptions.Compiled);
|
||||||
|
private static readonly Regex HeadingTrailingHashRegex = new(@"^(#{1,6}\s+.*?)\s+#+\s*$", RegexOptions.Compiled);
|
||||||
|
private static readonly Regex BulletListRegex = new(@"^(\s*)[*+]( +.*)$", RegexOptions.Compiled);
|
||||||
|
private static readonly Regex TaskListEmptyRegex = new(@"^(\s*-\s*)\[\s*\]\s*(.*)$", RegexOptions.Compiled);
|
||||||
|
private static readonly Regex TaskListCheckedRegex = new(@"^(\s*-\s*)\[[xX]\]\s*(.*)$", RegexOptions.Compiled);
|
||||||
|
|
||||||
|
// Callout triggers for English and Slovak
|
||||||
|
private static readonly Regex WarningCalloutRegex = new(@"^(?:>\s*)?(?:Pozor|Varovanie|Warning|Caution):\s*(.*)$", RegexOptions.IgnoreCase | RegexOptions.Compiled);
|
||||||
|
private static readonly Regex ImportantCalloutRegex = new(@"^(?:>\s*)?(?:Dôležité|Dolezite|Important):\s*(.*)$", RegexOptions.IgnoreCase | RegexOptions.Compiled);
|
||||||
|
private static readonly Regex TipCalloutRegex = new(@"^(?:>\s*)?(?:Tip):\s*(.*)$", RegexOptions.IgnoreCase | RegexOptions.Compiled);
|
||||||
|
private static readonly Regex NoteCalloutRegex = new(@"^(?:>\s*)?(?:Poznámka|Poznamka|Note|Info|Informácia|Informacia):\s*(.*)$", RegexOptions.IgnoreCase | RegexOptions.Compiled);
|
||||||
|
private static readonly Regex CautionCalloutRegex = new(@"^(?:>\s*)?(?:Kritické|Kriticke|Chyba|Danger|Error):\s*(.*)$", RegexOptions.IgnoreCase | RegexOptions.Compiled);
|
||||||
|
|
||||||
|
public string FormatMarkdown(string markdown)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(markdown))
|
||||||
|
return string.Empty;
|
||||||
|
|
||||||
|
var rawLines = markdown.Replace("\r\n", "\n").Replace("\r", "\n").Split('\n');
|
||||||
|
var processedLines = new List<string>();
|
||||||
|
|
||||||
|
bool inCodeBlock = false;
|
||||||
|
string codeBlockFence = "";
|
||||||
|
bool inFrontmatter = false;
|
||||||
|
|
||||||
|
int lineIndex = 0;
|
||||||
|
while (lineIndex < rawLines.Length)
|
||||||
|
{
|
||||||
|
var line = rawLines[lineIndex];
|
||||||
|
|
||||||
|
// 1. Handle Frontmatter (YAML at document start)
|
||||||
|
if (lineIndex == 0 && line.Trim() == "---")
|
||||||
|
{
|
||||||
|
inFrontmatter = true;
|
||||||
|
processedLines.Add(line);
|
||||||
|
lineIndex++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (inFrontmatter)
|
||||||
|
{
|
||||||
|
processedLines.Add(line);
|
||||||
|
if (line.Trim() == "---")
|
||||||
|
{
|
||||||
|
inFrontmatter = false;
|
||||||
|
}
|
||||||
|
lineIndex++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Handle Code Blocks
|
||||||
|
var trimmedLine = line.Trim();
|
||||||
|
if (trimmedLine.StartsWith("```") || trimmedLine.StartsWith("~~~"))
|
||||||
|
{
|
||||||
|
var fence = trimmedLine.Length >= 3 ? trimmedLine[..3] : "```";
|
||||||
|
if (!inCodeBlock)
|
||||||
|
{
|
||||||
|
inCodeBlock = true;
|
||||||
|
codeBlockFence = fence;
|
||||||
|
processedLines.Add(line.TrimEnd());
|
||||||
|
}
|
||||||
|
else if (trimmedLine.StartsWith(codeBlockFence))
|
||||||
|
{
|
||||||
|
inCodeBlock = false;
|
||||||
|
codeBlockFence = "";
|
||||||
|
processedLines.Add(line.TrimEnd());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
processedLines.Add(line);
|
||||||
|
}
|
||||||
|
lineIndex++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inCodeBlock)
|
||||||
|
{
|
||||||
|
// Preserve code block contents exactly as-is
|
||||||
|
processedLines.Add(line);
|
||||||
|
lineIndex++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Detect and Format Markdown Tables
|
||||||
|
if (IsTableStart(rawLines, lineIndex))
|
||||||
|
{
|
||||||
|
if (processedLines.Count > 0 && !string.IsNullOrWhiteSpace(processedLines[^1]))
|
||||||
|
{
|
||||||
|
processedLines.Add(string.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
var (formattedTableLines, nextIndex) = FormatTableBlock(rawLines, lineIndex);
|
||||||
|
processedLines.AddRange(formattedTableLines);
|
||||||
|
|
||||||
|
if (nextIndex < rawLines.Length && !string.IsNullOrWhiteSpace(rawLines[nextIndex]))
|
||||||
|
{
|
||||||
|
processedLines.Add(string.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
lineIndex = nextIndex;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. Format Regular Markdown Line
|
||||||
|
var formattedLine = FormatSingleLine(line);
|
||||||
|
|
||||||
|
// 5. Smart Callouts Conversion
|
||||||
|
var calloutLines = TryConvertSmartCallout(formattedLine);
|
||||||
|
if (calloutLines != null)
|
||||||
|
{
|
||||||
|
processedLines.AddRange(calloutLines);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
processedLines.Add(formattedLine);
|
||||||
|
}
|
||||||
|
|
||||||
|
lineIndex++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 6. Clean up consecutive blank lines & ensure single final newline
|
||||||
|
return NormalizeBlankLines(processedLines);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string FormatSingleLine(string line)
|
||||||
|
{
|
||||||
|
var trimmedEnd = line.TrimEnd();
|
||||||
|
|
||||||
|
// Fix Heading missing space: e.g. #Heading -> # Heading
|
||||||
|
var headingMatch = HeadingRegex.Match(trimmedEnd);
|
||||||
|
if (headingMatch.Success)
|
||||||
|
{
|
||||||
|
trimmedEnd = $"{headingMatch.Groups[1].Value} {headingMatch.Groups[2].Value.Trim()}";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove trailing hash decorations in headings: ## Heading ## -> ## Heading
|
||||||
|
var trailingHashMatch = HeadingTrailingHashRegex.Match(trimmedEnd);
|
||||||
|
if (trailingHashMatch.Success)
|
||||||
|
{
|
||||||
|
trimmedEnd = trailingHashMatch.Groups[1].Value.TrimEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fix bullet list style: * item or + item -> - item
|
||||||
|
var bulletMatch = BulletListRegex.Match(trimmedEnd);
|
||||||
|
if (bulletMatch.Success)
|
||||||
|
{
|
||||||
|
trimmedEnd = $"{bulletMatch.Groups[1].Value}-{bulletMatch.Groups[2].Value}";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fix Task lists spacing
|
||||||
|
var taskEmptyMatch = TaskListEmptyRegex.Match(trimmedEnd);
|
||||||
|
if (taskEmptyMatch.Success)
|
||||||
|
{
|
||||||
|
trimmedEnd = $"{taskEmptyMatch.Groups[1].Value}[ ] {taskEmptyMatch.Groups[2].Value.TrimStart()}";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var taskCheckedMatch = TaskListCheckedRegex.Match(trimmedEnd);
|
||||||
|
if (taskCheckedMatch.Success)
|
||||||
|
{
|
||||||
|
trimmedEnd = $"{taskCheckedMatch.Groups[1].Value}[x] {taskCheckedMatch.Groups[2].Value.TrimStart()}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return trimmedEnd;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<string>? TryConvertSmartCallout(string line)
|
||||||
|
{
|
||||||
|
var trimmed = line.Trim();
|
||||||
|
if (string.IsNullOrWhiteSpace(trimmed)) return null;
|
||||||
|
|
||||||
|
// Skip if already in GitHub alert syntax: > [!NOTE], > [!WARNING], etc.
|
||||||
|
if (trimmed.StartsWith("> [!") || trimmed.StartsWith("[!")) return null;
|
||||||
|
|
||||||
|
if (WarningCalloutRegex.IsMatch(trimmed))
|
||||||
|
{
|
||||||
|
var text = WarningCalloutRegex.Match(trimmed).Groups[1].Value.Trim();
|
||||||
|
return new List<string> { "> [!WARNING]", string.IsNullOrEmpty(text) ? "> " : $"> {text}" };
|
||||||
|
}
|
||||||
|
if (ImportantCalloutRegex.IsMatch(trimmed))
|
||||||
|
{
|
||||||
|
var text = ImportantCalloutRegex.Match(trimmed).Groups[1].Value.Trim();
|
||||||
|
return new List<string> { "> [!IMPORTANT]", string.IsNullOrEmpty(text) ? "> " : $"> {text}" };
|
||||||
|
}
|
||||||
|
if (TipCalloutRegex.IsMatch(trimmed))
|
||||||
|
{
|
||||||
|
var text = TipCalloutRegex.Match(trimmed).Groups[1].Value.Trim();
|
||||||
|
return new List<string> { "> [!TIP]", string.IsNullOrEmpty(text) ? "> " : $"> {text}" };
|
||||||
|
}
|
||||||
|
if (NoteCalloutRegex.IsMatch(trimmed))
|
||||||
|
{
|
||||||
|
var text = NoteCalloutRegex.Match(trimmed).Groups[1].Value.Trim();
|
||||||
|
return new List<string> { "> [!NOTE]", string.IsNullOrEmpty(text) ? "> " : $"> {text}" };
|
||||||
|
}
|
||||||
|
if (CautionCalloutRegex.IsMatch(trimmed))
|
||||||
|
{
|
||||||
|
var text = CautionCalloutRegex.Match(trimmed).Groups[1].Value.Trim();
|
||||||
|
return new List<string> { "> [!CAUTION]", string.IsNullOrEmpty(text) ? "> " : $"> {text}" };
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsTableStart(string[] lines, int currentIndex)
|
||||||
|
{
|
||||||
|
if (currentIndex + 1 >= lines.Length) return false;
|
||||||
|
|
||||||
|
var current = lines[currentIndex].Trim();
|
||||||
|
var next = lines[currentIndex + 1].Trim();
|
||||||
|
|
||||||
|
if (!current.Contains('|') || !next.Contains('|')) return false;
|
||||||
|
|
||||||
|
// Next line must be table separator: containing only |, -, :, and spaces
|
||||||
|
var separatorClean = next.Replace("|", "").Replace("-", "").Replace(":", "").Trim();
|
||||||
|
return separatorClean.Length == 0 && next.Contains('-');
|
||||||
|
}
|
||||||
|
|
||||||
|
private static (List<string> FormattedLines, int NextIndex) FormatTableBlock(string[] lines, int startIndex)
|
||||||
|
{
|
||||||
|
var tableRawLines = new List<string>();
|
||||||
|
int currentIndex = startIndex;
|
||||||
|
|
||||||
|
while (currentIndex < lines.Length)
|
||||||
|
{
|
||||||
|
var line = lines[currentIndex];
|
||||||
|
var trimmed = line.Trim();
|
||||||
|
if (string.IsNullOrWhiteSpace(trimmed) || !trimmed.Contains('|'))
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
tableRawLines.Add(trimmed);
|
||||||
|
currentIndex++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tableRawLines.Count < 2)
|
||||||
|
{
|
||||||
|
return (tableRawLines, currentIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse cells for all rows
|
||||||
|
var parsedRows = new List<List<string>>();
|
||||||
|
foreach (var r in tableRawLines)
|
||||||
|
{
|
||||||
|
var cells = SplitTableCells(r);
|
||||||
|
parsedRows.Add(cells);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine max column count
|
||||||
|
int colCount = parsedRows.Max(r => r.Count);
|
||||||
|
if (colCount == 0) return (tableRawLines, currentIndex);
|
||||||
|
|
||||||
|
// Normalize all rows to colCount
|
||||||
|
foreach (var row in parsedRows)
|
||||||
|
{
|
||||||
|
while (row.Count < colCount)
|
||||||
|
{
|
||||||
|
row.Add("");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trim trailing columns if they are empty in every non-separator row
|
||||||
|
while (colCount > 1)
|
||||||
|
{
|
||||||
|
bool allEmptyInLastCol = true;
|
||||||
|
for (int r = 0; r < parsedRows.Count; r++)
|
||||||
|
{
|
||||||
|
if (r == 1) continue; // skip separator
|
||||||
|
if (!string.IsNullOrWhiteSpace(parsedRows[r][colCount - 1]))
|
||||||
|
{
|
||||||
|
allEmptyInLastCol = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (allEmptyInLastCol)
|
||||||
|
{
|
||||||
|
colCount--;
|
||||||
|
foreach (var row in parsedRows)
|
||||||
|
{
|
||||||
|
if (row.Count > colCount) row.RemoveAt(colCount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure header cells in row 0 are not blank
|
||||||
|
for (int c = 0; c < colCount; c++)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(parsedRows[0][c]))
|
||||||
|
{
|
||||||
|
parsedRows[0][c] = $"Column {c + 1}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse alignments from separator (row 1)
|
||||||
|
var alignments = new List<ColumnAlign>();
|
||||||
|
var separatorRow = parsedRows[1];
|
||||||
|
for (int c = 0; c < colCount; c++)
|
||||||
|
{
|
||||||
|
var cell = c < separatorRow.Count ? separatorRow[c].Trim() : "";
|
||||||
|
bool left = cell.StartsWith(':');
|
||||||
|
bool right = cell.EndsWith(':');
|
||||||
|
|
||||||
|
if (left && right)
|
||||||
|
alignments.Add(ColumnAlign.Center);
|
||||||
|
else if (right)
|
||||||
|
alignments.Add(ColumnAlign.Right);
|
||||||
|
else
|
||||||
|
alignments.Add(ColumnAlign.Left);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter out any duplicate separator rows inside data rows (r >= 2)
|
||||||
|
var dataRows = new List<List<string>>();
|
||||||
|
for (int r = 2; r < parsedRows.Count; r++)
|
||||||
|
{
|
||||||
|
var row = parsedRows[r];
|
||||||
|
bool isDashesOnly = row.All(c => {
|
||||||
|
var clean = c.Replace("-", "").Replace(":", "").Trim();
|
||||||
|
return clean.Length == 0 && c.Contains('-');
|
||||||
|
});
|
||||||
|
if (!isDashesOnly)
|
||||||
|
{
|
||||||
|
dataRows.Add(row);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine column widths
|
||||||
|
var colWidths = new int[colCount];
|
||||||
|
for (int c = 0; c < colCount; c++)
|
||||||
|
{
|
||||||
|
int maxWidth = 3; // minimum 3 chars (e.g. ---)
|
||||||
|
maxWidth = Math.Max(maxWidth, parsedRows[0][c].Trim().Length);
|
||||||
|
for (int r = 0; r < dataRows.Count; r++)
|
||||||
|
{
|
||||||
|
maxWidth = Math.Max(maxWidth, dataRows[r][c].Trim().Length);
|
||||||
|
}
|
||||||
|
colWidths[c] = maxWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reconstruct formatted table
|
||||||
|
var result = new List<string>();
|
||||||
|
|
||||||
|
// 1. Header row
|
||||||
|
result.Add(FormatTableRow(parsedRows[0], colWidths, alignments));
|
||||||
|
|
||||||
|
// 2. Separator row
|
||||||
|
var sepSb = new StringBuilder();
|
||||||
|
sepSb.Append('|');
|
||||||
|
for (int c = 0; c < colCount; c++)
|
||||||
|
{
|
||||||
|
var width = colWidths[c];
|
||||||
|
var align = alignments[c];
|
||||||
|
sepSb.Append(' ');
|
||||||
|
if (align == ColumnAlign.Center)
|
||||||
|
{
|
||||||
|
sepSb.Append(':');
|
||||||
|
sepSb.Append(new string('-', Math.Max(1, width - 2)));
|
||||||
|
sepSb.Append(':');
|
||||||
|
}
|
||||||
|
else if (align == ColumnAlign.Right)
|
||||||
|
{
|
||||||
|
sepSb.Append(new string('-', Math.Max(2, width - 1)));
|
||||||
|
sepSb.Append(':');
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sepSb.Append(':');
|
||||||
|
sepSb.Append(new string('-', Math.Max(2, width - 1)));
|
||||||
|
}
|
||||||
|
sepSb.Append(" |");
|
||||||
|
}
|
||||||
|
result.Add(sepSb.ToString());
|
||||||
|
|
||||||
|
// 3. Data rows
|
||||||
|
for (int r = 0; r < dataRows.Count; r++)
|
||||||
|
{
|
||||||
|
result.Add(FormatTableRow(dataRows[r], colWidths, alignments));
|
||||||
|
}
|
||||||
|
|
||||||
|
return (result, currentIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<string> SplitTableCells(string row)
|
||||||
|
{
|
||||||
|
var trimmed = row.Trim();
|
||||||
|
if (trimmed.StartsWith('|')) trimmed = trimmed[1..];
|
||||||
|
if (trimmed.EndsWith('|')) trimmed = trimmed[..^1];
|
||||||
|
|
||||||
|
var raw = trimmed.Split('|');
|
||||||
|
return raw.Select(s => s.Trim()).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string FormatTableRow(List<string> cells, int[] colWidths, List<ColumnAlign> alignments)
|
||||||
|
{
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
sb.Append('|');
|
||||||
|
for (int c = 0; c < colWidths.Length; c++)
|
||||||
|
{
|
||||||
|
var text = c < cells.Count ? cells[c].Trim() : "";
|
||||||
|
var width = colWidths[c];
|
||||||
|
var align = c < alignments.Count ? alignments[c] : ColumnAlign.Left;
|
||||||
|
|
||||||
|
sb.Append(' ');
|
||||||
|
if (align == ColumnAlign.Right)
|
||||||
|
{
|
||||||
|
sb.Append(text.PadLeft(width));
|
||||||
|
}
|
||||||
|
else if (align == ColumnAlign.Center)
|
||||||
|
{
|
||||||
|
int totalPad = width - text.Length;
|
||||||
|
int padLeft = totalPad / 2;
|
||||||
|
int padRight = totalPad - padLeft;
|
||||||
|
sb.Append(new string(' ', Math.Max(0, padLeft)));
|
||||||
|
sb.Append(text);
|
||||||
|
sb.Append(new string(' ', Math.Max(0, padRight)));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sb.Append(text.PadRight(width));
|
||||||
|
}
|
||||||
|
sb.Append(" |");
|
||||||
|
}
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string NormalizeBlankLines(List<string> lines)
|
||||||
|
{
|
||||||
|
var output = new List<string>();
|
||||||
|
bool prevWasBlank = false;
|
||||||
|
|
||||||
|
foreach (var line in lines)
|
||||||
|
{
|
||||||
|
var isBlank = string.IsNullOrWhiteSpace(line);
|
||||||
|
if (isBlank)
|
||||||
|
{
|
||||||
|
if (!prevWasBlank && output.Count > 0)
|
||||||
|
{
|
||||||
|
output.Add(string.Empty);
|
||||||
|
prevWasBlank = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
output.Add(line);
|
||||||
|
prevWasBlank = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trim trailing blank lines
|
||||||
|
while (output.Count > 0 && string.IsNullOrWhiteSpace(output[^1]))
|
||||||
|
{
|
||||||
|
output.RemoveAt(output.Count - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return string.Join("\n", output) + "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum ColumnAlign
|
||||||
|
{
|
||||||
|
Left,
|
||||||
|
Center,
|
||||||
|
Right
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -107,7 +107,7 @@ public partial class DocumentService : IDocumentService
|
|||||||
return docs.FirstOrDefault(d => d.Id.Equals(id, StringComparison.OrdinalIgnoreCase));
|
return docs.FirstOrDefault(d => d.Id.Equals(id, StringComparison.OrdinalIgnoreCase));
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<DocumentMetadata> SaveDocumentAsync(string id, string content, bool? isFlagged = null, string? customTitle = null, List<string>? tags = null)
|
public async Task<DocumentMetadata> SaveDocumentAsync(string id, string content, bool? isFlagged = null, string? customTitle = null, List<string>? tags = null, string? docStyle = null)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(id))
|
if (string.IsNullOrEmpty(id))
|
||||||
throw new ArgumentException("Document ID cannot be empty", nameof(id));
|
throw new ArgumentException("Document ID cannot be empty", nameof(id));
|
||||||
@@ -132,6 +132,7 @@ public partial class DocumentService : IDocumentService
|
|||||||
Snippet = snippet,
|
Snippet = snippet,
|
||||||
IsFlagged = isFlagged ?? existing.IsFlagged,
|
IsFlagged = isFlagged ?? existing.IsFlagged,
|
||||||
Tags = extractedTags,
|
Tags = extractedTags,
|
||||||
|
DocStyle = docStyle ?? existing.DocStyle ?? "modern",
|
||||||
LastModifiedAt = DateTime.UtcNow
|
LastModifiedAt = DateTime.UtcNow
|
||||||
};
|
};
|
||||||
docs[existingIndex] = doc;
|
docs[existingIndex] = doc;
|
||||||
@@ -145,6 +146,7 @@ public partial class DocumentService : IDocumentService
|
|||||||
Snippet = snippet,
|
Snippet = snippet,
|
||||||
IsFlagged = isFlagged ?? false,
|
IsFlagged = isFlagged ?? false,
|
||||||
Tags = extractedTags,
|
Tags = extractedTags,
|
||||||
|
DocStyle = docStyle ?? "modern",
|
||||||
CreatedAt = DateTime.UtcNow,
|
CreatedAt = DateTime.UtcNow,
|
||||||
LastModifiedAt = DateTime.UtcNow
|
LastModifiedAt = DateTime.UtcNow
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
namespace Notemark.Services;
|
||||||
|
|
||||||
|
public interface IAutoStyleService
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Formats and cleans markdown content (aligns tables, fixes headings, cleans whitespace, converts smart callouts).
|
||||||
|
/// </summary>
|
||||||
|
string FormatMarkdown(string markdown);
|
||||||
|
}
|
||||||
@@ -11,7 +11,7 @@ public interface IDocumentService
|
|||||||
Task<List<DocumentMetadata>> GetDocumentsByTagAsync(string tag);
|
Task<List<DocumentMetadata>> GetDocumentsByTagAsync(string tag);
|
||||||
Task<DocumentMetadata?> GetMostRecentDocumentAsync();
|
Task<DocumentMetadata?> GetMostRecentDocumentAsync();
|
||||||
Task<DocumentMetadata?> GetDocumentAsync(string id);
|
Task<DocumentMetadata?> GetDocumentAsync(string id);
|
||||||
Task<DocumentMetadata> SaveDocumentAsync(string id, string content, bool? isFlagged = null, string? customTitle = null, List<string>? tags = null);
|
Task<DocumentMetadata> SaveDocumentAsync(string id, string content, bool? isFlagged = null, string? customTitle = null, List<string>? tags = null, string? docStyle = null);
|
||||||
Task<bool> ToggleFlagAsync(string id);
|
Task<bool> ToggleFlagAsync(string id);
|
||||||
Task DeleteDocumentAsync(string id);
|
Task DeleteDocumentAsync(string id);
|
||||||
string ExtractTitle(string content);
|
string ExtractTitle(string content);
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ public class MarkdownService : IMarkdownService
|
|||||||
// Configure Markdig pipeline with GitHub-style features
|
// Configure Markdig pipeline with GitHub-style features
|
||||||
_pipeline = new MarkdownPipelineBuilder()
|
_pipeline = new MarkdownPipelineBuilder()
|
||||||
.UseAdvancedExtensions() // Tables, footnotes, task lists, etc.
|
.UseAdvancedExtensions() // Tables, footnotes, task lists, etc.
|
||||||
|
.UseAlertBlocks() // GitHub-style alerts (> [!NOTE], > [!WARNING], etc.)
|
||||||
.UseSoftlineBreakAsHardlineBreak() // GitHub-style line breaks
|
.UseSoftlineBreakAsHardlineBreak() // GitHub-style line breaks
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
@@ -29,10 +30,127 @@ public class MarkdownService : IMarkdownService
|
|||||||
if (string.IsNullOrEmpty(markdown))
|
if (string.IsNullOrEmpty(markdown))
|
||||||
return string.Empty;
|
return string.Empty;
|
||||||
|
|
||||||
|
// Preprocess markdown to ensure tables are correctly surrounded by blank lines for CommonMark / Markdig parser
|
||||||
|
var preprocessed = EnsureTableSpacing(markdown);
|
||||||
|
|
||||||
// Convert markdown to HTML
|
// Convert markdown to HTML
|
||||||
var html = Markdown.ToHtml(markdown, _pipeline);
|
var html = Markdown.ToHtml(preprocessed, _pipeline);
|
||||||
|
|
||||||
// Sanitize HTML to prevent XSS attacks
|
// Sanitize HTML to prevent XSS attacks
|
||||||
return _sanitizer.Sanitize(html);
|
return _sanitizer.Sanitize(html);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static string EnsureTableSpacing(string markdown)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(markdown)) return markdown;
|
||||||
|
|
||||||
|
var lines = markdown.Replace("\r\n", "\n").Replace("\r", "\n").Split('\n');
|
||||||
|
var result = new List<string>(lines.Length + 8);
|
||||||
|
bool inCodeBlock = false;
|
||||||
|
string codeFence = "";
|
||||||
|
bool inFrontmatter = false;
|
||||||
|
bool inTable = false;
|
||||||
|
|
||||||
|
for (int i = 0; i < lines.Length; i++)
|
||||||
|
{
|
||||||
|
var line = lines[i];
|
||||||
|
var trimmed = line.Trim();
|
||||||
|
|
||||||
|
// 1. Frontmatter
|
||||||
|
if (i == 0 && trimmed == "---")
|
||||||
|
{
|
||||||
|
inFrontmatter = true;
|
||||||
|
result.Add(line);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (inFrontmatter)
|
||||||
|
{
|
||||||
|
result.Add(line);
|
||||||
|
if (trimmed == "---") inFrontmatter = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Code blocks
|
||||||
|
if (trimmed.StartsWith("```") || trimmed.StartsWith("~~~"))
|
||||||
|
{
|
||||||
|
var fence = trimmed.Length >= 3 ? trimmed[..3] : "```";
|
||||||
|
if (!inCodeBlock)
|
||||||
|
{
|
||||||
|
inCodeBlock = true;
|
||||||
|
codeFence = fence;
|
||||||
|
}
|
||||||
|
else if (trimmed.StartsWith(codeFence))
|
||||||
|
{
|
||||||
|
inCodeBlock = false;
|
||||||
|
codeFence = "";
|
||||||
|
}
|
||||||
|
result.Add(line);
|
||||||
|
inTable = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inCodeBlock)
|
||||||
|
{
|
||||||
|
result.Add(line);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Table detection
|
||||||
|
if (!inTable && IsTableStartLine(lines, i))
|
||||||
|
{
|
||||||
|
// Ensure blank line before table if previous line is not blank
|
||||||
|
if (result.Count > 0 && !string.IsNullOrWhiteSpace(result[^1]))
|
||||||
|
{
|
||||||
|
result.Add(string.Empty);
|
||||||
|
}
|
||||||
|
inTable = true;
|
||||||
|
result.Add(line);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inTable)
|
||||||
|
{
|
||||||
|
if (IsTableRowLine(line))
|
||||||
|
{
|
||||||
|
result.Add(line);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Table ended
|
||||||
|
inTable = false;
|
||||||
|
if (!string.IsNullOrWhiteSpace(line))
|
||||||
|
{
|
||||||
|
// Ensure blank line after table
|
||||||
|
result.Add(string.Empty);
|
||||||
|
}
|
||||||
|
result.Add(line);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result.Add(line);
|
||||||
|
}
|
||||||
|
|
||||||
|
return string.Join("\n", result);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsTableStartLine(string[] lines, int index)
|
||||||
|
{
|
||||||
|
if (index + 1 >= lines.Length) return false;
|
||||||
|
var current = lines[index].Trim();
|
||||||
|
var next = lines[index + 1].Trim();
|
||||||
|
|
||||||
|
if (!current.Contains('|') || !next.Contains('|')) return false;
|
||||||
|
|
||||||
|
// Next line must be a table delimiter line containing only |, -, :, and whitespace
|
||||||
|
var cleanDelim = next.Replace("|", "").Replace("-", "").Replace(":", "").Trim();
|
||||||
|
return cleanDelim.Length == 0 && next.Contains('-');
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsTableRowLine(string line)
|
||||||
|
{
|
||||||
|
var trimmed = line.Trim();
|
||||||
|
return !string.IsNullOrWhiteSpace(trimmed) && trimmed.Contains('|');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
[
|
|
||||||
{
|
|
||||||
"id": "2ba5f046-c8d8-4e55-acf4-5e654dee5afc",
|
|
||||||
"title": "Kopanie",
|
|
||||||
"isFlagged": true,
|
|
||||||
"tags": [],
|
|
||||||
"createdAt": "2026-09-22T09:08:10.0528137Z",
|
|
||||||
"lastModifiedAt": "2026-09-22T09:45:38.5280677Z",
|
|
||||||
"snippet": "Kopanie Moj otec mi v\u017Edy hovoril, \u017Ee do pivnice sa nechod\u00ED. Nie preto, \u017Ee by tam bolo nebezpe\u010Dn\u00E9 \u2014 aspo\u0148 tak to tvrdil \u2014..."
|
|
||||||
}
|
|
||||||
]
|
|
||||||
@@ -124,3 +124,134 @@ a:hover {
|
|||||||
.blazor-error-boundary::after {
|
.blazor-error-boundary::after {
|
||||||
content: "An error has occurred.";
|
content: "An error has occurred.";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ========================================================= */
|
||||||
|
/* GLOBAL MARKDOWN PREVIEW STYLING */
|
||||||
|
/* ========================================================= */
|
||||||
|
|
||||||
|
/* Base Tables */
|
||||||
|
.preview-content table {
|
||||||
|
width: 100% !important;
|
||||||
|
border-collapse: collapse !important;
|
||||||
|
margin: 1.5em 0 !important;
|
||||||
|
border: 1.5px solid var(--border) !important;
|
||||||
|
border-radius: 8px !important;
|
||||||
|
overflow: hidden !important;
|
||||||
|
font-size: 0.9375rem !important;
|
||||||
|
display: table !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-content th,
|
||||||
|
.preview-content td {
|
||||||
|
padding: 0.75rem 1rem !important;
|
||||||
|
border: 1px solid var(--border) !important;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-content th {
|
||||||
|
background-color: var(--surface-hover) !important;
|
||||||
|
font-weight: 600 !important;
|
||||||
|
color: var(--primary-text) !important;
|
||||||
|
border-bottom: 2px solid var(--border) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-content tr:hover td {
|
||||||
|
background-color: var(--surface-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Modern Preset Tables */
|
||||||
|
.preview-content[data-doc-style="modern"] table,
|
||||||
|
.preview-content.doc-style-modern table {
|
||||||
|
border: 1.5px solid var(--border) !important;
|
||||||
|
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.05) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-content[data-doc-style="modern"] th,
|
||||||
|
.preview-content.doc-style-modern th {
|
||||||
|
background-color: var(--surface-hover) !important;
|
||||||
|
color: var(--primary-text) !important;
|
||||||
|
border: 1px solid var(--border) !important;
|
||||||
|
border-bottom: 2px solid var(--accent) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-content[data-doc-style="modern"] td,
|
||||||
|
.preview-content.doc-style-modern td {
|
||||||
|
border: 1px solid var(--border) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Academic Preset Tables */
|
||||||
|
.preview-content[data-doc-style="academic"] table,
|
||||||
|
.preview-content.doc-style-academic table {
|
||||||
|
border: 1px solid rgba(128, 128, 128, 0.4) !important;
|
||||||
|
border-top: 2.5px solid currentColor !important;
|
||||||
|
border-bottom: 2.5px solid currentColor !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-content[data-doc-style="academic"] th,
|
||||||
|
.preview-content.doc-style-academic th {
|
||||||
|
border: 1px solid rgba(128, 128, 128, 0.35) !important;
|
||||||
|
border-bottom: 2px solid currentColor !important;
|
||||||
|
background: rgba(0, 0, 0, 0.03) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-content[data-doc-style="academic"] td,
|
||||||
|
.preview-content.doc-style-academic td {
|
||||||
|
border: 1px solid rgba(128, 128, 128, 0.25) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Technical Preset Tables */
|
||||||
|
.preview-content[data-doc-style="technical"] table,
|
||||||
|
.preview-content.doc-style-technical table {
|
||||||
|
border: 1.5px solid #30363d !important;
|
||||||
|
background-color: #161b22 !important;
|
||||||
|
font-family: 'JetBrains Mono', monospace !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-content[data-doc-style="technical"] th,
|
||||||
|
.preview-content.doc-style-technical th {
|
||||||
|
background-color: #21262d !important;
|
||||||
|
color: #7ee787 !important;
|
||||||
|
border: 1px solid #30363d !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-content[data-doc-style="technical"] td,
|
||||||
|
.preview-content.doc-style-technical td {
|
||||||
|
border: 1px solid #30363d !important;
|
||||||
|
color: #c9d1d9 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Executive Preset Tables */
|
||||||
|
.preview-content[data-doc-style="executive"] table,
|
||||||
|
.preview-content.doc-style-executive table {
|
||||||
|
border: 1.5px solid #cbd5e1 !important;
|
||||||
|
box-shadow: 0 4px 6px -1px rgba(15, 39, 68, 0.06) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:root[data-theme="dark"] .preview-content[data-doc-style="executive"] table,
|
||||||
|
:root[data-theme="dark"] .preview-content.doc-style-executive table {
|
||||||
|
border-color: #334155 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-content[data-doc-style="executive"] th,
|
||||||
|
.preview-content.doc-style-executive th {
|
||||||
|
background-color: #0f2744 !important;
|
||||||
|
color: #ffffff !important;
|
||||||
|
border: 1px solid #1e3a5f !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:root[data-theme="dark"] .preview-content[data-doc-style="executive"] th,
|
||||||
|
:root[data-theme="dark"] .preview-content.doc-style-executive th {
|
||||||
|
background-color: #1e293b !important;
|
||||||
|
color: #38bdf8 !important;
|
||||||
|
border: 1px solid #334155 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-content[data-doc-style="executive"] td,
|
||||||
|
.preview-content.doc-style-executive td {
|
||||||
|
border: 1px solid #cbd5e1 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:root[data-theme="dark"] .preview-content[data-doc-style="executive"] td,
|
||||||
|
:root[data-theme="dark"] .preview-content.doc-style-executive td {
|
||||||
|
border: 1px solid #334155 !important;
|
||||||
|
}
|
||||||
+367
-14
@@ -256,29 +256,382 @@ window.notemarkEditor = {
|
|||||||
}, { passive: false });
|
}, { passive: false });
|
||||||
},
|
},
|
||||||
|
|
||||||
// Download standalone styled HTML file
|
// Download standalone styled HTML file with chosen document style & theme
|
||||||
downloadHtml: function(filename, title, htmlBody, theme) {
|
downloadHtml: function(filename, title, htmlBody, theme, docStyle) {
|
||||||
|
const style = docStyle || 'modern';
|
||||||
const fullHtml = `<!DOCTYPE html>
|
const fullHtml = `<!DOCTYPE html>
|
||||||
<html lang="en" data-theme="${theme || 'light'}">
|
<html lang="en" data-theme="${theme || 'light'}" data-doc-style="${style}">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>${title || 'Notemark Document'}</title>
|
<title>${title || 'Notemark Document'}</title>
|
||||||
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||||
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:ital,wght@0,400;0,600;0,700;1,400&family=Merriweather:ital,wght@0,400;0,700;1,400;1,700&family=Plus+Jakarta+Sans:ital,wght@0,400;0,600;0,700;0,800;1,400&display=swap" rel="stylesheet">
|
||||||
<style>
|
<style>
|
||||||
:root { --bg: #ffffff; --primary-text: #111827; --accent: #4f46e5; --border: #e5e7eb; --code-bg: #f1f5f9; --surface-hover: #f3f4f6; }
|
:root {
|
||||||
:root[data-theme="dark"] { --bg: #0b0f19; --primary-text: #f9fafb; --accent: #6366f1; --border: #1f2937; --code-bg: #1e293b; --surface-hover: #1f2937; }
|
--bg: #ffffff;
|
||||||
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif; line-height: 1.7; background-color: var(--bg); color: var(--primary-text); max-width: 860px; margin: 2rem auto; padding: 0 1.5rem; }
|
--primary-text: #111827;
|
||||||
|
--secondary-text: #4b5563;
|
||||||
|
--accent: #4f46e5;
|
||||||
|
--border: #e5e7eb;
|
||||||
|
--border-subtle: #f3f4f6;
|
||||||
|
--code-bg: #f8fafc;
|
||||||
|
--surface-hover: #f3f4f6;
|
||||||
|
--callout-bg: rgba(79, 70, 229, 0.08);
|
||||||
|
}
|
||||||
|
:root[data-theme="dark"] {
|
||||||
|
--bg: #0b0f19;
|
||||||
|
--primary-text: #f9fafb;
|
||||||
|
--secondary-text: #9ca3af;
|
||||||
|
--accent: #6366f1;
|
||||||
|
--border: #1f2937;
|
||||||
|
--border-subtle: #111827;
|
||||||
|
--code-bg: #111827;
|
||||||
|
--surface-hover: #1f2937;
|
||||||
|
--callout-bg: rgba(99, 102, 241, 0.12);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Base Body Typography */
|
||||||
|
body {
|
||||||
|
background-color: var(--bg);
|
||||||
|
color: var(--primary-text);
|
||||||
|
max-width: 880px;
|
||||||
|
margin: 2.5rem auto;
|
||||||
|
padding: 0 2rem;
|
||||||
|
line-height: 1.7;
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Headings */
|
||||||
h1, h2, h3, h4, h5, h6 { color: var(--primary-text); font-weight: 700; }
|
h1, h2, h3, h4, h5, h6 { color: var(--primary-text); font-weight: 700; }
|
||||||
h1 { font-size: 2.2rem; border-bottom: 1px solid var(--border); padding-bottom: 0.3em; }
|
h1 { font-size: 2.2rem; border-bottom: 1px solid var(--border); padding-bottom: 0.4em; margin-top: 1.5em; }
|
||||||
h2 { font-size: 1.6rem; border-bottom: 1px solid var(--border); padding-bottom: 0.3em; }
|
h2 { font-size: 1.6rem; border-bottom: 1px solid var(--border); padding-bottom: 0.3em; margin-top: 1.4em; }
|
||||||
code { background-color: var(--code-bg); color: var(--accent); padding: 0.2em 0.4em; border-radius: 4px; font-family: monospace; }
|
h3 { font-size: 1.25rem; margin-top: 1.2em; }
|
||||||
pre { background-color: var(--code-bg); padding: 1.25rem; border-radius: 8px; overflow-x: auto; border: 1px solid var(--border); }
|
|
||||||
pre code { background: none; color: inherit; padding: 0; }
|
/* Code & Pre */
|
||||||
blockquote { border-left: 4px solid var(--accent); padding: 0.5rem 1rem; margin: 1em 0; background: rgba(79, 70, 229, 0.08); }
|
code { background-color: var(--code-bg); color: var(--accent); padding: 0.2em 0.4em; border-radius: 4px; font-family: 'JetBrains Mono', Consolas, monospace; font-size: 0.9em; }
|
||||||
|
pre { background-color: var(--code-bg); padding: 1.25rem; border-radius: 8px; overflow-x: auto; border: 1px solid var(--border); line-height: 1.5; }
|
||||||
|
pre code { background: none; color: inherit; padding: 0; font-size: 0.88rem; }
|
||||||
|
|
||||||
|
/* Quotes & Callouts */
|
||||||
|
blockquote { border-left: 4px solid var(--accent); padding: 0.6rem 1.2rem; margin: 1.2em 0; background: var(--callout-bg); border-radius: 0 6px 6px 0; }
|
||||||
|
|
||||||
|
/* GitHub Alerts */
|
||||||
|
.markdown-alert { padding: 0.75rem 1rem; margin: 1.25rem 0; border-left: 4px solid; border-radius: 0 6px 6px 0; background: var(--callout-bg); }
|
||||||
|
.markdown-alert-title { display: flex; align-items: center; gap: 0.5rem; font-weight: 600; margin-bottom: 0.35rem; }
|
||||||
|
.markdown-alert-note { border-left-color: #3b82f6; background-color: rgba(59, 130, 246, 0.08); }
|
||||||
|
.markdown-alert-tip { border-left-color: #10b981; background-color: rgba(16, 185, 129, 0.08); }
|
||||||
|
.markdown-alert-important { border-left-color: #8b5cf6; background-color: rgba(139, 92, 246, 0.08); }
|
||||||
|
.markdown-alert-warning { border-left-color: #f59e0b; background-color: rgba(245, 158, 11, 0.08); }
|
||||||
|
.markdown-alert-caution { border-left-color: #ef4444; background-color: rgba(239, 68, 68, 0.08); }
|
||||||
|
|
||||||
|
/* Tables */
|
||||||
table { width: 100%; border-collapse: collapse; margin: 1.5em 0; }
|
table { width: 100%; border-collapse: collapse; margin: 1.5em 0; }
|
||||||
th, td { border: 1px solid var(--border); padding: 0.6rem 0.9rem; text-align: left; }
|
th, td { border: 1px solid var(--border); padding: 0.65rem 0.9rem; text-align: left; }
|
||||||
th { background-color: var(--surface-hover); }
|
th { background-color: var(--surface-hover); font-weight: 600; }
|
||||||
img { max-width: 100%; border-radius: 8px; }
|
img { max-width: 100%; border-radius: 8px; }
|
||||||
|
|
||||||
|
/* === PRESET 1: MODERN (Clean SaaS / Notion) === */
|
||||||
|
[data-doc-style="modern"] body {
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
||||||
|
line-height: 1.7;
|
||||||
|
}
|
||||||
|
[data-doc-style="modern"] h1 {
|
||||||
|
font-size: 2.3rem;
|
||||||
|
font-weight: 800;
|
||||||
|
letter-spacing: -0.03em;
|
||||||
|
background: linear-gradient(135deg, var(--primary-text) 60%, var(--accent));
|
||||||
|
-webkit-background-clip: text;
|
||||||
|
-webkit-text-fill-color: transparent;
|
||||||
|
border-bottom: 2px solid var(--accent);
|
||||||
|
padding-bottom: 0.4em;
|
||||||
|
}
|
||||||
|
[data-doc-style="modern"] h2 {
|
||||||
|
font-size: 1.6rem;
|
||||||
|
font-weight: 700;
|
||||||
|
border-bottom: 1px solid var(--border);
|
||||||
|
}
|
||||||
|
[data-doc-style="modern"] table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
border: 1.5px solid var(--border);
|
||||||
|
border-radius: 8px;
|
||||||
|
overflow: hidden;
|
||||||
|
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.05);
|
||||||
|
margin: 1.5em 0;
|
||||||
|
}
|
||||||
|
[data-doc-style="modern"] th {
|
||||||
|
background-color: var(--surface-hover);
|
||||||
|
color: var(--primary-text);
|
||||||
|
font-weight: 600;
|
||||||
|
padding: 0.75rem 1rem;
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-bottom: 2px solid var(--accent);
|
||||||
|
}
|
||||||
|
[data-doc-style="modern"] td {
|
||||||
|
padding: 0.7rem 1rem;
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
}
|
||||||
|
[data-doc-style="modern"] blockquote {
|
||||||
|
border-left: 4px solid var(--accent);
|
||||||
|
background: linear-gradient(90deg, rgba(79, 70, 229, 0.08) 0%, transparent 100%);
|
||||||
|
border-radius: 0 10px 10px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* === PRESET 2: ACADEMIC (LaTeX / Research Paper) === */
|
||||||
|
[data-doc-style="academic"] body {
|
||||||
|
font-family: 'Merriweather', 'Georgia', 'Cambria', 'Times New Roman', serif;
|
||||||
|
line-height: 1.9;
|
||||||
|
font-size: 1.05rem;
|
||||||
|
background-color: #fdfdf9;
|
||||||
|
color: #1a1a1a;
|
||||||
|
padding: 3rem 4rem;
|
||||||
|
text-align: justify;
|
||||||
|
}
|
||||||
|
:root[data-theme="dark"][data-doc-style="academic"] body {
|
||||||
|
background-color: #121110;
|
||||||
|
color: #e5e0d8;
|
||||||
|
}
|
||||||
|
[data-doc-style="academic"] h1 {
|
||||||
|
font-family: 'Merriweather', 'Georgia', serif;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 2.1rem;
|
||||||
|
font-weight: 700;
|
||||||
|
letter-spacing: 0.02em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
border-top: 3px double currentColor;
|
||||||
|
border-bottom: 1px solid currentColor;
|
||||||
|
padding: 0.8rem 0;
|
||||||
|
margin: 1.8em 0 1.2em 0;
|
||||||
|
}
|
||||||
|
[data-doc-style="academic"] h2 {
|
||||||
|
font-family: 'Merriweather', 'Georgia', serif;
|
||||||
|
font-size: 1.45rem;
|
||||||
|
font-weight: 700;
|
||||||
|
font-variant: small-caps;
|
||||||
|
letter-spacing: 0.06em;
|
||||||
|
border-bottom: 1px solid currentColor;
|
||||||
|
padding-bottom: 0.25em;
|
||||||
|
}
|
||||||
|
[data-doc-style="academic"] h3 {
|
||||||
|
font-family: 'Merriweather', 'Georgia', serif;
|
||||||
|
font-size: 1.2rem;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
[data-doc-style="academic"] table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
border: 1px solid rgba(128, 128, 128, 0.4);
|
||||||
|
border-top: 2.5px solid currentColor;
|
||||||
|
border-bottom: 2.5px solid currentColor;
|
||||||
|
margin: 2em auto;
|
||||||
|
}
|
||||||
|
[data-doc-style="academic"] th {
|
||||||
|
border: 1px solid rgba(128, 128, 128, 0.35);
|
||||||
|
border-bottom: 2px solid currentColor;
|
||||||
|
font-variant: small-caps;
|
||||||
|
letter-spacing: 0.08em;
|
||||||
|
background: rgba(0, 0, 0, 0.03);
|
||||||
|
text-align: center;
|
||||||
|
padding: 0.65rem 1rem;
|
||||||
|
}
|
||||||
|
[data-doc-style="academic"] td {
|
||||||
|
border: 1px solid rgba(128, 128, 128, 0.25);
|
||||||
|
padding: 0.6rem 1rem;
|
||||||
|
}
|
||||||
|
[data-doc-style="academic"] blockquote {
|
||||||
|
font-style: italic;
|
||||||
|
border-left: none;
|
||||||
|
background: transparent;
|
||||||
|
padding: 0.8rem 2.5rem;
|
||||||
|
position: relative;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
[data-doc-style="academic"] blockquote::before {
|
||||||
|
content: "“";
|
||||||
|
font-size: 3.5rem;
|
||||||
|
line-height: 0;
|
||||||
|
position: absolute;
|
||||||
|
left: 0.5rem;
|
||||||
|
top: 1.5rem;
|
||||||
|
opacity: 0.35;
|
||||||
|
font-family: Georgia, serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* === PRESET 3: TECHNICAL (Cyber / Terminal / Dev Spec) === */
|
||||||
|
[data-doc-style="technical"] body {
|
||||||
|
font-family: 'JetBrains Mono', 'Fira Code', 'Cascadia Code', 'SF Mono', Consolas, monospace;
|
||||||
|
font-size: 0.92rem;
|
||||||
|
line-height: 1.65;
|
||||||
|
background-color: #0d1117;
|
||||||
|
color: #c9d1d9;
|
||||||
|
padding: 2.5rem;
|
||||||
|
}
|
||||||
|
[data-doc-style="technical"] h1 {
|
||||||
|
font-family: 'JetBrains Mono', monospace;
|
||||||
|
color: #58a6ff;
|
||||||
|
font-size: 1.8rem;
|
||||||
|
font-weight: 700;
|
||||||
|
border-bottom: 2px solid #238636;
|
||||||
|
padding-bottom: 0.4em;
|
||||||
|
}
|
||||||
|
[data-doc-style="technical"] h1::before {
|
||||||
|
content: "#!/usr/bin/spec >> ";
|
||||||
|
color: #7ee787;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
display: block;
|
||||||
|
font-weight: 400;
|
||||||
|
margin-bottom: 0.3rem;
|
||||||
|
}
|
||||||
|
[data-doc-style="technical"] h2 {
|
||||||
|
font-family: 'JetBrains Mono', monospace;
|
||||||
|
color: #79c0ff;
|
||||||
|
font-size: 1.35rem;
|
||||||
|
border-bottom: 1px dashed #30363d;
|
||||||
|
padding-bottom: 0.3em;
|
||||||
|
}
|
||||||
|
[data-doc-style="technical"] h2::before {
|
||||||
|
content: "## ";
|
||||||
|
color: #7ee787;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
[data-doc-style="technical"] h3 {
|
||||||
|
font-family: 'JetBrains Mono', monospace;
|
||||||
|
color: #d2a8ff;
|
||||||
|
font-size: 1.15rem;
|
||||||
|
}
|
||||||
|
[data-doc-style="technical"] h3::before {
|
||||||
|
content: "fn ";
|
||||||
|
color: #ff7b72;
|
||||||
|
}
|
||||||
|
[data-doc-style="technical"] table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
border: 1.5px solid #30363d;
|
||||||
|
background-color: #161b22;
|
||||||
|
font-family: 'JetBrains Mono', monospace;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
margin: 1.5em 0;
|
||||||
|
}
|
||||||
|
[data-doc-style="technical"] th {
|
||||||
|
background-color: #21262d;
|
||||||
|
color: #7ee787;
|
||||||
|
border: 1px solid #30363d;
|
||||||
|
padding: 0.6rem 0.85rem;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
[data-doc-style="technical"] td {
|
||||||
|
border: 1px solid #30363d;
|
||||||
|
padding: 0.55rem 0.85rem;
|
||||||
|
color: #c9d1d9;
|
||||||
|
}
|
||||||
|
[data-doc-style="technical"] pre {
|
||||||
|
background-color: #030712 !important;
|
||||||
|
border: 1px solid #06b6d4 !important;
|
||||||
|
box-shadow: 0 0 15px rgba(6, 182, 212, 0.12);
|
||||||
|
}
|
||||||
|
[data-doc-style="technical"] blockquote {
|
||||||
|
border-left: 4px solid #238636;
|
||||||
|
background-color: rgba(35, 134, 54, 0.1);
|
||||||
|
color: #7ee787;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* === PRESET 4: EXECUTIVE (Corporate Brief / McKinsey) === */
|
||||||
|
[data-doc-style="executive"] body {
|
||||||
|
font-family: 'Plus Jakarta Sans', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
||||||
|
line-height: 1.75;
|
||||||
|
color: #0f172a;
|
||||||
|
background-color: #ffffff;
|
||||||
|
border-top: 6px solid #0f2744;
|
||||||
|
padding: 2.5rem 3.5rem;
|
||||||
|
}
|
||||||
|
:root[data-theme="dark"][data-doc-style="executive"] body {
|
||||||
|
color: #f1f5f9;
|
||||||
|
background-color: #0f172a;
|
||||||
|
border-top: 6px solid #38bdf8;
|
||||||
|
}
|
||||||
|
[data-doc-style="executive"] h1 {
|
||||||
|
font-size: 2.3rem;
|
||||||
|
font-weight: 800;
|
||||||
|
color: #0f2744;
|
||||||
|
letter-spacing: -0.02em;
|
||||||
|
border-bottom: 3px solid #d97706;
|
||||||
|
padding-bottom: 0.4em;
|
||||||
|
}
|
||||||
|
:root[data-theme="dark"][data-doc-style="executive"] h1 {
|
||||||
|
color: #38bdf8;
|
||||||
|
border-bottom-color: #fbbf24;
|
||||||
|
}
|
||||||
|
[data-doc-style="executive"] h1::before {
|
||||||
|
content: "EXECUTIVE BRIEFING // CONFIDENTIAL";
|
||||||
|
display: block;
|
||||||
|
font-size: 0.72rem;
|
||||||
|
font-weight: 800;
|
||||||
|
letter-spacing: 0.15em;
|
||||||
|
color: #d97706;
|
||||||
|
margin-bottom: 0.4rem;
|
||||||
|
}
|
||||||
|
[data-doc-style="executive"] h2 {
|
||||||
|
font-size: 1.35rem;
|
||||||
|
font-weight: 800;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.08em;
|
||||||
|
color: #0f2744;
|
||||||
|
border-left: 4px solid #d97706;
|
||||||
|
padding-left: 0.75rem;
|
||||||
|
}
|
||||||
|
:root[data-theme="dark"][data-doc-style="executive"] h2 {
|
||||||
|
color: #38bdf8;
|
||||||
|
border-left-color: #fbbf24;
|
||||||
|
}
|
||||||
|
[data-doc-style="executive"] table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
border: 1.5px solid #cbd5e1;
|
||||||
|
box-shadow: 0 4px 6px -1px rgba(15, 39, 68, 0.06);
|
||||||
|
border-radius: 6px;
|
||||||
|
overflow: hidden;
|
||||||
|
margin: 1.5em 0;
|
||||||
|
}
|
||||||
|
:root[data-theme="dark"][data-doc-style="executive"] table {
|
||||||
|
border-color: #334155;
|
||||||
|
}
|
||||||
|
[data-doc-style="executive"] th {
|
||||||
|
background-color: #0f2744;
|
||||||
|
color: #ffffff;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
font-weight: 700;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.08em;
|
||||||
|
padding: 0.75rem 1rem;
|
||||||
|
border: 1px solid #1e3a5f;
|
||||||
|
}
|
||||||
|
:root[data-theme="dark"][data-doc-style="executive"] th {
|
||||||
|
background-color: #1e293b;
|
||||||
|
color: #38bdf8;
|
||||||
|
border: 1px solid #334155;
|
||||||
|
}
|
||||||
|
[data-doc-style="executive"] td {
|
||||||
|
padding: 0.65rem 1rem;
|
||||||
|
border: 1px solid #cbd5e1;
|
||||||
|
}
|
||||||
|
:root[data-theme="dark"][data-doc-style="executive"] td {
|
||||||
|
border: 1px solid #334155;
|
||||||
|
}
|
||||||
|
[data-doc-style="executive"] tr:nth-child(even) td {
|
||||||
|
background-color: rgba(15, 39, 68, 0.03);
|
||||||
|
}
|
||||||
|
:root[data-theme="dark"][data-doc-style="executive"] tr:nth-child(even) td {
|
||||||
|
background-color: rgba(255, 255, 255, 0.03);
|
||||||
|
}
|
||||||
|
[data-doc-style="executive"] blockquote {
|
||||||
|
border-left: 4px solid #0f2744;
|
||||||
|
background-color: rgba(15, 39, 68, 0.05);
|
||||||
|
border-radius: 0 8px 8px 0;
|
||||||
|
font-weight: 500;
|
||||||
|
padding: 1rem 1.4rem;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|||||||
Reference in New Issue
Block a user