Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,6 @@
<ItemGroup Label="Web">
<PackageVersion Include="AspNetCore.HealthChecks.UI.Client" Version="9.0.0" />
<PackageVersion Include="Blazored.Toast" Version="4.2.1" />
<PackageVersion Include="Blazorise.Bootstrap5" Version="[1.8.1]" />
<!-- License change >1.8.1 forces registration -->
<PackageVersion Include="Blazorise.Markdown" Version="[1.8.1]" />
<PackageVersion Include="Markdig" Version="1.1.1" />
<PackageVersion Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="10.0.5" />
<PackageVersion Include="NCronJob" Version="4.9.0" />
Expand Down
4 changes: 3 additions & 1 deletion src/LinkDotNet.Blog.Web/App.razor
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,12 @@
<link rel="preload" href="css/fonts/icons.woff" as="font" type="font/woff" crossorigin />
<link href="css/basic.css" rel="stylesheet" />
<link href="css/icons.css" rel="stylesheet" />
<link href="css/markdown-editor.css" rel="stylesheet" />
<link rel="preload" href="LinkDotNet.Blog.Web.styles.css" as="style">
<link href="LinkDotNet.Blog.Web.styles.css" rel="stylesheet"/>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/styles/github-dark-dimmed.min.css" integrity="sha512-zcatBMvxa7rT7dDklfjauWsfiSFParF+hRfCdf4Zr40/MmA1gkFcBRbop0zMpvYF3FmznYFgcL8wlcuO/GwHoA==" crossorigin="anonymous" referrerpolicy="no-referrer" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.7/css/bootstrap.min.css" integrity="sha512-fw7f+TcMjTb7bpbLJZlP8g2Y4XcCyFZW8uy8HsRZsH/SwbMw0plKHFHr99DN3l04VsYNwvzicUX/6qurvIxbxw==" crossorigin="anonymous" referrerpolicy="no-referrer" />
<link href="_content/Blazorise/blazorise.css" rel="stylesheet" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
</head>
<body>
<Routes />
Expand All @@ -44,6 +45,7 @@
<a href="/" class="reload">Reload</a>
<a class="dismiss" role="button">x</a>
</div>
<script src="js/markdown-editor.js"></script>
<script src="_framework/blazor.web.js"></script>
<script async src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/highlight.min.js" integrity="sha512-EBLzUL8XLl+va/zAsmXwS7Z2B1F9HUHkZwyS/VKwh3S7T/U0nF4BaU29EP/ZSf6zgiIxYAnKLu6bJ8dqpmX5uw==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
Copy link

Copilot AI Mar 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

highlight.js is loaded with async, but preview rendering triggers highlighting immediately via JS interop. If hljs hasn’t loaded yet, markdownEditor.highlightCodeBlocks() will no-op and code blocks won’t be highlighted for that preview session. Consider loading highlight.js with defer (or without async), or implement a retry/ready mechanism before attempting to highlight.

Suggested change
<script async src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/highlight.min.js" integrity="sha512-EBLzUL8XLl+va/zAsmXwS7Z2B1F9HUHkZwyS/VKwh3S7T/U0nF4BaU29EP/ZSf6zgiIxYAnKLu6bJ8dqpmX5uw==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script defer src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/highlight.min.js" integrity="sha512-EBLzUL8XLl+va/zAsmXwS7Z2B1F9HUHkZwyS/VKwh3S7T/U0nF4BaU29EP/ZSf6zgiIxYAnKLu6bJ8dqpmX5uw==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>

Copilot uses AI. Check for mistakes.
<script async src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.7/js/bootstrap.bundle.min.js" integrity="sha512-Tc0i+vRogmX4NN7tuLbQfBxa8JkfUSAxSFVzmU31nVdHyiHElPPy2cWfFacmCJKw0VqovrzKhdd2TSTMdAxp2g==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,353 @@
/* ── IDE Workspace ──────────────────────────────────────────────── */
.ide-workspace {
overflow-x: hidden;
}

/* ── Top Bar ────────────────────────────────────────────────────── */
.ide-topbar {
position: sticky;
top: 0;
z-index: 10;
height: 44px;
background-color: var(--bs-secondary-bg);
border-bottom: 1px solid var(--bs-border-color);
display: flex;
align-items: center;
padding: 0 1rem;
gap: 0.5rem;
}

.ide-breadcrumb {
display: flex;
align-items: center;
gap: 0.375rem;
font-size: 0.8125rem;
overflow: hidden;
min-width: 0;
}

.ide-breadcrumb-sep {
font-size: 0.625rem;
opacity: 0.4;
flex-shrink: 0;
}

.ide-breadcrumb-current {
font-weight: 500;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 250px;
color: var(--bs-body-color);
}

/* ── Status Pills ───────────────────────────────────────────────── */
.ide-pill {
display: inline-flex;
align-items: center;
gap: 0.25rem;
font-size: 0.6875rem;
font-weight: 500;
padding: 0.2rem 0.625rem;
border-radius: 2rem;
border: 1px solid var(--bs-border-color);
background-color: var(--bs-body-bg);
color: var(--bs-secondary-color);
white-space: nowrap;
flex-shrink: 0;
}

.ide-pill-dirty {
color: var(--bs-warning);
border-color: var(--bs-warning);
background-color: transparent;
}

.ide-pill-publish {
color: var(--bs-success);
border-color: var(--bs-success);
}

.ide-pill-editing {
color: var(--bs-info);
border-color: var(--bs-info);
}

.ide-pill-scheduled {
color: var(--bs-warning);
border-color: var(--bs-warning);
}

/* ── Main Two-Column Layout ─────────────────────────────────────── */
.ide-layout {
display: flex;
align-items: flex-start;
}

/* ── Editor Panel ───────────────────────────────────────────────── */
.ide-editor-panel {
flex: 1;
min-width: 0;
border-right: 1px solid var(--bs-border-color);
}

/* ── Title Section ──────────────────────────────────────────────── */
.ide-title-section {
padding: 1.5rem 1.75rem 1.25rem;
border-bottom: 1px solid var(--bs-border-color);
}

.ide-title-input {
display: block;
width: 100%;
background: transparent;
border: none;
border-bottom: 2px solid transparent;
font-size: 1.75rem;
font-weight: 700;
color: var(--bs-body-color);
padding: 0 0 0.25rem;
line-height: 1.2;
transition: border-color 0.15s ease;
}

.ide-title-input:focus {
outline: none;
border-bottom-color: var(--bs-primary);
}

.ide-title-input::placeholder {
color: var(--bs-secondary-color);
opacity: 0.35;
font-weight: 400;
}

/* ── IDE Tab Bar ────────────────────────────────────────────────── */
.ide-tabs {
display: flex;
background-color: var(--bs-secondary-bg);
border-bottom: 1px solid var(--bs-border-color);
overflow-x: auto;
scrollbar-width: none;
}

.ide-tabs::-webkit-scrollbar {
display: none;
}

.ide-tab {
display: inline-flex;
align-items: center;
gap: 0.4rem;
padding: 0.5rem 1.25rem;
border: none;
border-right: 1px solid var(--bs-border-color);
border-bottom: 2px solid transparent;
background: transparent;
color: var(--bs-secondary-color);
font-size: 0.8125rem;
font-family: 'JetBrains Mono', 'Fira Code', 'Menlo', 'Courier New', monospace;
cursor: pointer;
white-space: nowrap;
transition: background-color 0.1s ease, color 0.1s ease;
}

.ide-tab:hover {
background-color: var(--bs-tertiary-bg);
color: var(--bs-body-color);
}

.ide-tab-active {
background-color: var(--bs-body-bg) !important;
color: var(--bs-body-color) !important;
border-bottom-color: var(--bs-primary) !important;
}

.ide-tab-icon {
opacity: 0.65;
font-size: 0.875rem;
}

.ide-tab-badge {
font-size: 0.6875rem;
opacity: 0.5;
font-family: var(--bs-font-sans-serif);
}

/* ── Editor Action Toolbar ──────────────────────────────────────── */
.ide-editor-actions {
display: flex;
align-items: center;
gap: 0.25rem;
padding: 0.375rem 0.75rem;
background-color: var(--bs-secondary-bg);
border-bottom: 1px solid var(--bs-border-color);
flex-wrap: wrap;
}

.ide-action-btn {
display: inline-flex;
align-items: center;
gap: 0.375rem;
padding: 0.25rem 0.625rem;
border: 1px solid transparent;
border-radius: 0.25rem;
background: transparent;
color: var(--bs-secondary-color);
font-size: 0.8125rem;
cursor: pointer;
transition: all 0.1s ease;
white-space: nowrap;
}

.ide-action-btn:hover {
background-color: var(--bs-tertiary-bg);
border-color: var(--bs-border-color);
color: var(--bs-body-color);
}

/* ── MarkdownTextArea integration ───────────────────────────────── */
::deep .markdown-editor-container.ide-editor {
border-radius: 0;
border-left: none;
border-right: none;
border-bottom: none;
box-shadow: none;
}

::deep .markdown-editor-container.ide-editor:focus-within {
outline: none;
}

/* ── Validation Errors ──────────────────────────────────────────── */
.ide-error {
display: block;
font-size: 0.75rem;
color: var(--bs-danger);
padding-top: 0.25rem;
}

/* ── Settings Sidebar ───────────────────────────────────────────── */
.ide-settings-sidebar {
width: 300px;
min-width: 300px;
max-width: 300px;
background-color: var(--bs-secondary-bg);
border-left: 1px solid var(--bs-border-color);
}

.ide-settings-section {
padding: 1rem 1.25rem;
border-bottom: 1px solid var(--bs-border-color);
}

.ide-settings-label {
display: block;
font-size: 0.6875rem;
font-weight: 700;
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--bs-secondary-color);
margin-bottom: 0.875rem;
}

/* ── Tag Chips ──────────────────────────────────────────────────── */
.ide-tag-chip {
display: inline-flex;
align-items: center;
font-size: 0.6875rem;
padding: 0.125rem 0.5rem;
border-radius: 2rem;
background-color: var(--bs-tertiary-bg);
border: 1px solid var(--bs-border-color);
color: var(--bs-body-color);
margin: 0.125rem 0.125rem 0 0;
}

/* ── Status Bar ─────────────────────────────────────────────────── */
.ide-statusbar {
height: 28px;
background-color: var(--bs-primary);
color: rgba(255, 255, 255, 0.9);
font-size: 0.75rem;
display: flex;
align-items: center;
}

.ide-statusbar-item {
display: inline-flex;
align-items: center;
height: 100%;
padding: 0 0.75rem;
opacity: 0.85;
cursor: default;
white-space: nowrap;
transition: background-color 0.1s, opacity 0.1s;
}

.ide-statusbar-item:hover {
background-color: rgba(255, 255, 255, 0.15);
opacity: 1;
}

.ide-statusbar-gap {
flex: 1;
}

.ide-statusbar-save {
display: inline-flex;
align-items: center;
gap: 0.375rem;
height: calc(100% - 4px);
margin: 2px;
padding: 0 0.875rem;
border-radius: 0.25rem;
background-color: rgba(255, 255, 255, 0.2);
color: white;
border: 1px solid rgba(255, 255, 255, 0.3);
font-size: 0.75rem;
font-weight: 600;
cursor: pointer;
transition: background-color 0.1s;
}

.ide-statusbar-save:hover:not(:disabled) {
background-color: rgba(255, 255, 255, 0.3);
}

.ide-statusbar-save:disabled {
opacity: 0.5;
cursor: not-allowed;
}

/* ── Desktop: sticky sidebar ────────────────────────────────────── */
@media (min-width: 992px) {
.ide-settings-sidebar {
position: sticky;
top: 44px;
max-height: calc(100svh - 44px);
overflow-y: auto;
align-self: flex-start;
}
}

/* ── Mobile ─────────────────────────────────────────────────────── */
@media (max-width: 991.98px) {
.ide-editor-panel {
border-right: none;
}

.ide-title-section {
padding: 1rem 1rem 0.75rem;
}

.ide-title-input {
font-size: 1.375rem;
}

.ide-statusbar {
position: sticky;
bottom: 0;
z-index: 10;
}
}
Loading