Skip to content

Commit 34a656b

Browse files
linkdotnetCopilot
andcommitted
feat: Custom editor
fix: Add aria-labels Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> fix: Check for mac properly Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> fix: undo/redo Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> fix: proper handling Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent cf67e02 commit 34a656b

10 files changed

Lines changed: 1853 additions & 67 deletions

File tree

Directory.Packages.props

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,6 @@
2525
<ItemGroup Label="Web">
2626
<PackageVersion Include="AspNetCore.HealthChecks.UI.Client" Version="9.0.0" />
2727
<PackageVersion Include="Blazored.Toast" Version="4.2.1" />
28-
<PackageVersion Include="Blazorise.Bootstrap5" Version="[1.8.1]" />
29-
<!-- License change >1.8.1 forces registration -->
30-
<PackageVersion Include="Blazorise.Markdown" Version="[1.8.1]" />
3128
<PackageVersion Include="Markdig" Version="1.1.1" />
3229
<PackageVersion Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="10.0.5" />
3330
<PackageVersion Include="NCronJob" Version="4.9.0" />

src/LinkDotNet.Blog.Web/App.razor

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,12 @@
2424
<link rel="preload" href="css/fonts/icons.woff" as="font" type="font/woff" crossorigin />
2525
<link href="css/basic.css" rel="stylesheet" />
2626
<link href="css/icons.css" rel="stylesheet" />
27+
<link href="css/markdown-editor.css" rel="stylesheet" />
2728
<link rel="preload" href="LinkDotNet.Blog.Web.styles.css" as="style">
2829
<link href="LinkDotNet.Blog.Web.styles.css" rel="stylesheet"/>
2930
<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" />
3031
<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" />
31-
<link href="_content/Blazorise/blazorise.css" rel="stylesheet" />
32+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
3233
</head>
3334
<body>
3435
<Routes />
@@ -44,6 +45,7 @@
4445
<a href="/" class="reload">Reload</a>
4546
<a class="dismiss" role="button">x</a>
4647
</div>
48+
<script src="js/markdown-editor.js"></script>
4749
<script src="_framework/blazor.web.js"></script>
4850
<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>
4951
<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>
Lines changed: 353 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,353 @@
1+
/* ── IDE Workspace ──────────────────────────────────────────────── */
2+
.ide-workspace {
3+
overflow-x: hidden;
4+
}
5+
6+
/* ── Top Bar ────────────────────────────────────────────────────── */
7+
.ide-topbar {
8+
position: sticky;
9+
top: 0;
10+
z-index: 10;
11+
height: 44px;
12+
background-color: var(--bs-secondary-bg);
13+
border-bottom: 1px solid var(--bs-border-color);
14+
display: flex;
15+
align-items: center;
16+
padding: 0 1rem;
17+
gap: 0.5rem;
18+
}
19+
20+
.ide-breadcrumb {
21+
display: flex;
22+
align-items: center;
23+
gap: 0.375rem;
24+
font-size: 0.8125rem;
25+
overflow: hidden;
26+
min-width: 0;
27+
}
28+
29+
.ide-breadcrumb-sep {
30+
font-size: 0.625rem;
31+
opacity: 0.4;
32+
flex-shrink: 0;
33+
}
34+
35+
.ide-breadcrumb-current {
36+
font-weight: 500;
37+
white-space: nowrap;
38+
overflow: hidden;
39+
text-overflow: ellipsis;
40+
max-width: 250px;
41+
color: var(--bs-body-color);
42+
}
43+
44+
/* ── Status Pills ───────────────────────────────────────────────── */
45+
.ide-pill {
46+
display: inline-flex;
47+
align-items: center;
48+
gap: 0.25rem;
49+
font-size: 0.6875rem;
50+
font-weight: 500;
51+
padding: 0.2rem 0.625rem;
52+
border-radius: 2rem;
53+
border: 1px solid var(--bs-border-color);
54+
background-color: var(--bs-body-bg);
55+
color: var(--bs-secondary-color);
56+
white-space: nowrap;
57+
flex-shrink: 0;
58+
}
59+
60+
.ide-pill-dirty {
61+
color: var(--bs-warning);
62+
border-color: var(--bs-warning);
63+
background-color: transparent;
64+
}
65+
66+
.ide-pill-publish {
67+
color: var(--bs-success);
68+
border-color: var(--bs-success);
69+
}
70+
71+
.ide-pill-editing {
72+
color: var(--bs-info);
73+
border-color: var(--bs-info);
74+
}
75+
76+
.ide-pill-scheduled {
77+
color: var(--bs-warning);
78+
border-color: var(--bs-warning);
79+
}
80+
81+
/* ── Main Two-Column Layout ─────────────────────────────────────── */
82+
.ide-layout {
83+
display: flex;
84+
align-items: flex-start;
85+
}
86+
87+
/* ── Editor Panel ───────────────────────────────────────────────── */
88+
.ide-editor-panel {
89+
flex: 1;
90+
min-width: 0;
91+
border-right: 1px solid var(--bs-border-color);
92+
}
93+
94+
/* ── Title Section ──────────────────────────────────────────────── */
95+
.ide-title-section {
96+
padding: 1.5rem 1.75rem 1.25rem;
97+
border-bottom: 1px solid var(--bs-border-color);
98+
}
99+
100+
.ide-title-input {
101+
display: block;
102+
width: 100%;
103+
background: transparent;
104+
border: none;
105+
border-bottom: 2px solid transparent;
106+
font-size: 1.75rem;
107+
font-weight: 700;
108+
color: var(--bs-body-color);
109+
padding: 0 0 0.25rem;
110+
line-height: 1.2;
111+
transition: border-color 0.15s ease;
112+
}
113+
114+
.ide-title-input:focus {
115+
outline: none;
116+
border-bottom-color: var(--bs-primary);
117+
}
118+
119+
.ide-title-input::placeholder {
120+
color: var(--bs-secondary-color);
121+
opacity: 0.35;
122+
font-weight: 400;
123+
}
124+
125+
/* ── IDE Tab Bar ────────────────────────────────────────────────── */
126+
.ide-tabs {
127+
display: flex;
128+
background-color: var(--bs-secondary-bg);
129+
border-bottom: 1px solid var(--bs-border-color);
130+
overflow-x: auto;
131+
scrollbar-width: none;
132+
}
133+
134+
.ide-tabs::-webkit-scrollbar {
135+
display: none;
136+
}
137+
138+
.ide-tab {
139+
display: inline-flex;
140+
align-items: center;
141+
gap: 0.4rem;
142+
padding: 0.5rem 1.25rem;
143+
border: none;
144+
border-right: 1px solid var(--bs-border-color);
145+
border-bottom: 2px solid transparent;
146+
background: transparent;
147+
color: var(--bs-secondary-color);
148+
font-size: 0.8125rem;
149+
font-family: 'JetBrains Mono', 'Fira Code', 'Menlo', 'Courier New', monospace;
150+
cursor: pointer;
151+
white-space: nowrap;
152+
transition: background-color 0.1s ease, color 0.1s ease;
153+
}
154+
155+
.ide-tab:hover {
156+
background-color: var(--bs-tertiary-bg);
157+
color: var(--bs-body-color);
158+
}
159+
160+
.ide-tab-active {
161+
background-color: var(--bs-body-bg) !important;
162+
color: var(--bs-body-color) !important;
163+
border-bottom-color: var(--bs-primary) !important;
164+
}
165+
166+
.ide-tab-icon {
167+
opacity: 0.65;
168+
font-size: 0.875rem;
169+
}
170+
171+
.ide-tab-badge {
172+
font-size: 0.6875rem;
173+
opacity: 0.5;
174+
font-family: var(--bs-font-sans-serif);
175+
}
176+
177+
/* ── Editor Action Toolbar ──────────────────────────────────────── */
178+
.ide-editor-actions {
179+
display: flex;
180+
align-items: center;
181+
gap: 0.25rem;
182+
padding: 0.375rem 0.75rem;
183+
background-color: var(--bs-secondary-bg);
184+
border-bottom: 1px solid var(--bs-border-color);
185+
flex-wrap: wrap;
186+
}
187+
188+
.ide-action-btn {
189+
display: inline-flex;
190+
align-items: center;
191+
gap: 0.375rem;
192+
padding: 0.25rem 0.625rem;
193+
border: 1px solid transparent;
194+
border-radius: 0.25rem;
195+
background: transparent;
196+
color: var(--bs-secondary-color);
197+
font-size: 0.8125rem;
198+
cursor: pointer;
199+
transition: all 0.1s ease;
200+
white-space: nowrap;
201+
}
202+
203+
.ide-action-btn:hover {
204+
background-color: var(--bs-tertiary-bg);
205+
border-color: var(--bs-border-color);
206+
color: var(--bs-body-color);
207+
}
208+
209+
/* ── MarkdownTextArea integration ───────────────────────────────── */
210+
::deep .markdown-editor-container.ide-editor {
211+
border-radius: 0;
212+
border-left: none;
213+
border-right: none;
214+
border-bottom: none;
215+
box-shadow: none;
216+
}
217+
218+
::deep .markdown-editor-container.ide-editor:focus-within {
219+
outline: none;
220+
}
221+
222+
/* ── Validation Errors ──────────────────────────────────────────── */
223+
.ide-error {
224+
display: block;
225+
font-size: 0.75rem;
226+
color: var(--bs-danger);
227+
padding-top: 0.25rem;
228+
}
229+
230+
/* ── Settings Sidebar ───────────────────────────────────────────── */
231+
.ide-settings-sidebar {
232+
width: 300px;
233+
min-width: 300px;
234+
max-width: 300px;
235+
background-color: var(--bs-secondary-bg);
236+
border-left: 1px solid var(--bs-border-color);
237+
}
238+
239+
.ide-settings-section {
240+
padding: 1rem 1.25rem;
241+
border-bottom: 1px solid var(--bs-border-color);
242+
}
243+
244+
.ide-settings-label {
245+
display: block;
246+
font-size: 0.6875rem;
247+
font-weight: 700;
248+
letter-spacing: 0.08em;
249+
text-transform: uppercase;
250+
color: var(--bs-secondary-color);
251+
margin-bottom: 0.875rem;
252+
}
253+
254+
/* ── Tag Chips ──────────────────────────────────────────────────── */
255+
.ide-tag-chip {
256+
display: inline-flex;
257+
align-items: center;
258+
font-size: 0.6875rem;
259+
padding: 0.125rem 0.5rem;
260+
border-radius: 2rem;
261+
background-color: var(--bs-tertiary-bg);
262+
border: 1px solid var(--bs-border-color);
263+
color: var(--bs-body-color);
264+
margin: 0.125rem 0.125rem 0 0;
265+
}
266+
267+
/* ── Status Bar ─────────────────────────────────────────────────── */
268+
.ide-statusbar {
269+
height: 28px;
270+
background-color: var(--bs-primary);
271+
color: rgba(255, 255, 255, 0.9);
272+
font-size: 0.75rem;
273+
display: flex;
274+
align-items: center;
275+
}
276+
277+
.ide-statusbar-item {
278+
display: inline-flex;
279+
align-items: center;
280+
height: 100%;
281+
padding: 0 0.75rem;
282+
opacity: 0.85;
283+
cursor: default;
284+
white-space: nowrap;
285+
transition: background-color 0.1s, opacity 0.1s;
286+
}
287+
288+
.ide-statusbar-item:hover {
289+
background-color: rgba(255, 255, 255, 0.15);
290+
opacity: 1;
291+
}
292+
293+
.ide-statusbar-gap {
294+
flex: 1;
295+
}
296+
297+
.ide-statusbar-save {
298+
display: inline-flex;
299+
align-items: center;
300+
gap: 0.375rem;
301+
height: calc(100% - 4px);
302+
margin: 2px;
303+
padding: 0 0.875rem;
304+
border-radius: 0.25rem;
305+
background-color: rgba(255, 255, 255, 0.2);
306+
color: white;
307+
border: 1px solid rgba(255, 255, 255, 0.3);
308+
font-size: 0.75rem;
309+
font-weight: 600;
310+
cursor: pointer;
311+
transition: background-color 0.1s;
312+
}
313+
314+
.ide-statusbar-save:hover:not(:disabled) {
315+
background-color: rgba(255, 255, 255, 0.3);
316+
}
317+
318+
.ide-statusbar-save:disabled {
319+
opacity: 0.5;
320+
cursor: not-allowed;
321+
}
322+
323+
/* ── Desktop: sticky sidebar ────────────────────────────────────── */
324+
@media (min-width: 992px) {
325+
.ide-settings-sidebar {
326+
position: sticky;
327+
top: 44px;
328+
max-height: calc(100svh - 44px);
329+
overflow-y: auto;
330+
align-self: flex-start;
331+
}
332+
}
333+
334+
/* ── Mobile ─────────────────────────────────────────────────────── */
335+
@media (max-width: 991.98px) {
336+
.ide-editor-panel {
337+
border-right: none;
338+
}
339+
340+
.ide-title-section {
341+
padding: 1rem 1rem 0.75rem;
342+
}
343+
344+
.ide-title-input {
345+
font-size: 1.375rem;
346+
}
347+
348+
.ide-statusbar {
349+
position: sticky;
350+
bottom: 0;
351+
z-index: 10;
352+
}
353+
}

0 commit comments

Comments
 (0)