From 07d7eb764794af4c8313565881529a031c17a934 Mon Sep 17 00:00:00 2001 From: Michal Vrba Date: Fri, 30 Jan 2026 16:37:30 +0100 Subject: [PATCH] Ignore MigrationBackup/ directory in .gitignore Added MigrationBackup/ to .gitignore to prevent tracking of backup files and subdirectories within this folder. This helps keep the repository clean from backup-related artifacts. --- .gitignore | 3 +- Notemark/Components/Pages/Editor.razor | 148 +++++++++++---------- Notemark/Components/Pages/Editor.razor.css | 146 ++++++++++++++------ 3 files changed, 186 insertions(+), 111 deletions(-) diff --git a/.gitignore b/.gitignore index 9491a2f..4da40ba 100644 --- a/.gitignore +++ b/.gitignore @@ -360,4 +360,5 @@ MigrationBackup/ .ionide/ # Fody - auto-generated XML schema -FodyWeavers.xsd \ No newline at end of file +FodyWeavers.xsd +/Notemark/data/shares diff --git a/Notemark/Components/Pages/Editor.razor b/Notemark/Components/Pages/Editor.razor index 92b967a..9f8be34 100644 --- a/Notemark/Components/Pages/Editor.razor +++ b/Notemark/Components/Pages/Editor.razor @@ -24,7 +24,38 @@
- +
@@ -53,42 +84,7 @@ - @if (showShareModal) - { - - } + @* Removed modal - now using dropdown *@ @code { @@ -98,8 +94,8 @@ private bool showPreview = false; private string saveStatus = "Saved"; - // Share modal state - private bool showShareModal = false; + // Share dropdown state + private bool showShareDropdown = false; private bool isGeneratingLinks = false; private string readonlyLink = ""; private string editableLink = ""; @@ -192,50 +188,66 @@ } } - private async Task ShowShareModal() + private async Task ToggleShareDropdown() { - showShareModal = true; - isGeneratingLinks = true; - readonlyLink = ""; - editableLink = ""; - - await InvokeAsync(StateHasChanged); - - try + if (!showShareDropdown) { - // Generate share tokens - var readonlyToken = await ShareService.CreateShareTokenAsync(sessionId, "readonly"); - var editableToken = await ShareService.CreateShareTokenAsync(sessionId, "editable"); + showShareDropdown = true; + isGeneratingLinks = true; + readonlyLink = ""; + editableLink = ""; - // Build URLs - var httpContext = HttpContextAccessor.HttpContext; - if (httpContext != null) + await InvokeAsync(StateHasChanged); + + try { - var baseUrl = $"{httpContext.Request.Scheme}://{httpContext.Request.Host}"; - readonlyLink = $"{baseUrl}/s/{readonlyToken}"; - editableLink = $"{baseUrl}/e/{editableToken}"; + // Generate share tokens + var readonlyToken = await ShareService.CreateShareTokenAsync(sessionId, "readonly"); + var editableToken = await ShareService.CreateShareTokenAsync(sessionId, "editable"); + + // Build URLs + var httpContext = HttpContextAccessor.HttpContext; + if (httpContext != null) + { + var baseUrl = $"{httpContext.Request.Scheme}://{httpContext.Request.Host}"; + readonlyLink = $"{baseUrl}/s/{readonlyToken}"; + editableLink = $"{baseUrl}/e/{editableToken}"; + } + } + catch + { + readonlyLink = "Error generating link"; + editableLink = "Error generating link"; + } + finally + { + isGeneratingLinks = false; + await InvokeAsync(StateHasChanged); } } - catch + else { - readonlyLink = "Error generating link"; - editableLink = "Error generating link"; - } - finally - { - isGeneratingLinks = false; - await InvokeAsync(StateHasChanged); + CloseShareDropdown(); } } - private void HideShareModal() + private void CloseShareDropdown() { - showShareModal = false; + showShareDropdown = false; } private async Task CopyToClipboard(string text) { - await JSRuntime.InvokeVoidAsync("navigator.clipboard.writeText", text); + try + { + await JSRuntime.InvokeVoidAsync("navigator.clipboard.writeText", text); + // Could add a toast notification here in the future + } + catch + { + // Fallback for browsers that don't support clipboard API + await JSRuntime.InvokeVoidAsync("prompt", "Copy this link:", text); + } } public void Dispose() diff --git a/Notemark/Components/Pages/Editor.razor.css b/Notemark/Components/Pages/Editor.razor.css index 937055e..98a5ab6 100644 --- a/Notemark/Components/Pages/Editor.razor.css +++ b/Notemark/Components/Pages/Editor.razor.css @@ -238,83 +238,145 @@ font-weight: 600; } -/* Modal Styles */ -.modal-overlay { +/* Share Dropdown Styles */ +.share-container { + position: relative; + display: inline-block; +} + +.dropdown-backdrop { position: fixed; top: 0; left: 0; right: 0; bottom: 0; - background-color: rgba(0, 0, 0, 0.5); - display: flex; - align-items: center; - justify-content: center; - z-index: 1000; + background: transparent; + z-index: 999; } -.modal-content { +.share-dropdown { + position: absolute; + top: 100%; + right: 0; + margin-top: 0.5rem; background-color: var(--surface); + border: 2px solid var(--accent); border-radius: 8px; - width: 90%; - max-width: 500px; - max-height: 80vh; - overflow: hidden; - box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04); + box-shadow: 0 8px 25px rgba(0, 0, 0, 0.3); + padding: 1rem; + width: 320px; + z-index: 1000; + animation: dropdownFadeIn 0.15s ease-out; } -.modal-header { - display: flex; - align-items: center; - justify-content: space-between; - padding: 1rem 1.5rem; - border-bottom: 1px solid var(--border); +@keyframes dropdownFadeIn { + from { + opacity: 0; + transform: translateY(-8px); + } + to { + opacity: 1; + transform: translateY(0); + } } -.modal-header h3 { - margin: 0; - font-size: 1.25rem; - color: var(--primary-text); +.share-dropdown::before { + content: ''; + position: absolute; + top: -8px; + right: 1rem; + width: 0; + height: 0; + border-left: 8px solid transparent; + border-right: 8px solid transparent; + border-bottom: 8px solid var(--accent); } -.modal-body { - padding: 1.5rem; +.share-item { + margin-bottom: 1rem; } -.share-link-group { - margin-bottom: 1.5rem; -} - -.share-link-group:last-child { +.share-item:last-child { margin-bottom: 0; } -.share-link-group label { +.share-item label { display: block; margin-bottom: 0.5rem; - font-weight: 500; - color: var(--primary-text); + font-weight: 600; + font-size: 0.875rem; + color: var(--accent); + text-transform: uppercase; + letter-spacing: 0.5px; } -.link-input-group { +.link-group { display: flex; gap: 0.5rem; + align-items: center; } -.link-input { +.link-field { flex: 1; - padding: 0.75rem; + padding: 0.5rem 0.75rem; border: 1px solid var(--border); - border-radius: 6px; + border-radius: 4px; background-color: var(--bg); color: var(--primary-text); - font-family: monospace; - font-size: 0.875rem; + font-family: 'JetBrains Mono', 'SF Mono', 'Monaco', monospace; + font-size: 0.75rem; + font-weight: 500; } -.loading { +.link-field:focus { + outline: none; + border-color: var(--accent); + box-shadow: 0 0 0 2px rgba(79, 70, 229, 0.2); +} + +.btn-copy-small { + padding: 0.5rem; + background-color: var(--accent); + color: white; + border: none; + border-radius: 4px; + cursor: pointer; + transition: all 0.2s ease; + font-size: 0.875rem; + display: flex; + align-items: center; + justify-content: center; + min-width: 2rem; +} + +.btn-copy-small:hover { + background-color: var(--accent-hover); + transform: scale(1.05); +} + +.loading-small { text-align: center; - color: var(--secondary-text); - padding: 2rem; + color: var(--accent); + padding: 1rem; + font-size: 0.875rem; + font-weight: 600; +} + +.loading-small::after { + content: ''; + display: inline-block; + width: 16px; + height: 16px; + border: 2px solid var(--border); + border-radius: 50%; + border-top-color: var(--accent); + animation: spin 1s ease-in-out infinite; + margin-left: 0.5rem; + vertical-align: middle; +} + +@keyframes spin { + to { transform: rotate(360deg); } } /* Mobile Responsive */