fix: editor-preview looks at draft again

This commit is contained in:
2026-02-20 22:15:55 +01:00
parent 1543af6edc
commit 5a2a6c9edb
8 changed files with 104 additions and 15 deletions

View File

@@ -179,6 +179,8 @@ export class PreviewServer {
const requestUrl = new URL(req.url || '/', 'http://127.0.0.1');
const requestTheme = sanitizePicoTheme(requestUrl.searchParams.get('theme'));
const previewThemeMode = sanitizePicoThemeMode(requestUrl.searchParams.get('mode'));
const useDraftContent = requestUrl.searchParams.get('draft') === 'true';
const draftPostId = requestUrl.searchParams.get('postId') || undefined;
const appliedTheme = requestTheme ?? sanitizePicoTheme((metadata as { picoTheme?: unknown } | null)?.picoTheme);
const picoStylesheetHref = getPicoStylesheetHref(appliedTheme);
const htmlRewriteContext = await this.buildHtmlRewriteContext();
@@ -218,7 +220,10 @@ export class PreviewServer {
language,
picoStylesheetHref,
htmlThemeAttribute: undefined,
}, categorySettings, listExcludedCategories);
}, categorySettings, listExcludedCategories, {
useDraftContent,
draftPostId,
});
if (!result) {
const notFoundHtml = await this.pageRenderer.renderNotFound({
page_title: '404 Not Found',
@@ -244,6 +249,7 @@ export class PreviewServer {
pageContext: { pageTitle: string; language: string; picoStylesheetHref: string; htmlThemeAttribute?: string },
categorySettings: Record<string, CategoryRenderSettings>,
listExcludedCategories: string[],
singlePostOptions?: { useDraftContent?: boolean; draftPostId?: string },
): Promise<string | null> {
const routePagination = parseRoutePagination(pathname);
if (!routePagination) {
@@ -263,7 +269,7 @@ export class PreviewServer {
const month = Number(postsYearMonthSlugMatch[2]);
const slug = postsYearMonthSlugMatch[3].replace(/\.html?$/i, '');
if (month < 1 || month > 12) return null;
const post = await this.findPublishedPostBySlug(slug, { year, month: month - 1 });
const post = await this.findSinglePostBySlug(slug, singlePostOptions, { year, month: month - 1 });
if (!post) return null;
return this.pageRenderer.renderSinglePost(post, rewriteContext, {
page_title: pageContext.pageTitle,
@@ -276,7 +282,7 @@ export class PreviewServer {
const postsSlugMatch = pagedPathname.match(/^\/posts\/([^/]+)$/);
if (postsSlugMatch) {
const slug = postsSlugMatch[1].replace(/\.html?$/i, '');
const post = await this.findPublishedPostBySlug(slug);
const post = await this.findSinglePostBySlug(slug, singlePostOptions);
if (!post) return null;
return this.pageRenderer.renderSinglePost(post, rewriteContext, {
page_title: pageContext.pageTitle,
@@ -292,7 +298,7 @@ export class PreviewServer {
const month = Number(legacyPostsYearMonthSlugMatch[2]);
const slug = legacyPostsYearMonthSlugMatch[3].replace(/\.html?$/i, '');
if (month < 1 || month > 12) return null;
const post = await this.findPublishedPostBySlug(slug, { year, month: month - 1 });
const post = await this.findSinglePostBySlug(slug, singlePostOptions, { year, month: month - 1 });
if (!post) return null;
return this.pageRenderer.renderSinglePost(post, rewriteContext, {
page_title: pageContext.pageTitle,
@@ -305,7 +311,7 @@ export class PreviewServer {
const legacyPostsSlugMatch = pagedPathname.match(/^\/post\/([^/]+)$/);
if (legacyPostsSlugMatch) {
const slug = legacyPostsSlugMatch[1].replace(/\.html?$/i, '');
const post = await this.findPublishedPostBySlug(slug);
const post = await this.findSinglePostBySlug(slug, singlePostOptions);
if (!post) return null;
return this.pageRenderer.renderSinglePost(post, rewriteContext, {
page_title: pageContext.pageTitle,
@@ -373,8 +379,7 @@ export class PreviewServer {
const month = Number(daySlugMatch[2]);
const day = Number(daySlugMatch[3]);
const slug = daySlugMatch[4];
const posts = await this.loadPostsForDay(year, month, day);
const post = posts.find((candidate) => candidate.slug === slug) || null;
const post = await this.findSinglePostBySlug(slug, singlePostOptions, { year, month: month - 1, day });
if (!post) return null;
return this.pageRenderer.renderSinglePost(post, rewriteContext, {
page_title: pageContext.pageTitle,
@@ -510,6 +515,31 @@ export class PreviewServer {
return match;
}
private async findSinglePostBySlug(
slug: string,
singlePostOptions?: { useDraftContent?: boolean; draftPostId?: string },
dateFilter?: { year: number; month: number; day?: number },
): Promise<PostData | null> {
if (singlePostOptions?.useDraftContent && singlePostOptions.draftPostId) {
const draftCandidate = await this.postEngine.getPost(singlePostOptions.draftPostId);
if (draftCandidate && draftCandidate.status === 'draft' && draftCandidate.slug === slug) {
if (!dateFilter) {
return draftCandidate;
}
const sameYear = draftCandidate.createdAt.getFullYear() === dateFilter.year;
const sameMonth = draftCandidate.createdAt.getMonth() === dateFilter.month;
const sameDay = dateFilter.day === undefined || draftCandidate.createdAt.getDate() === dateFilter.day;
if (sameYear && sameMonth && sameDay) {
return draftCandidate;
}
}
}
const fallbackDateFilter = dateFilter ? { year: dateFilter.year, month: dateFilter.month } : undefined;
return this.findPublishedPostBySlug(slug, fallbackDateFilter);
}
private async loadPostsForDay(
year: number,
month: number,

View File

@@ -346,7 +346,7 @@ export function registerIpcHandlers(): void {
return engine.getPost(id);
});
safeHandle('posts:getPreviewUrl', async (_, id: string) => {
safeHandle('posts:getPreviewUrl', async (_, id: string, options?: { draft?: boolean }) => {
const engine = getPostEngine();
const post = await engine.getPost(id);
@@ -356,6 +356,10 @@ export function registerIpcHandlers(): void {
const createdAt = resolvePostCreatedAt(post);
const canonicalPath = buildCanonicalPreviewPath(createdAt, post.slug);
if (options?.draft) {
return `http://127.0.0.1:4123${canonicalPath}?draft=true&postId=${encodeURIComponent(id)}`;
}
return `http://127.0.0.1:4123${canonicalPath}`;
});

View File

@@ -53,7 +53,7 @@ export const electronAPI: ElectronAPI = {
update: (id: string, data: unknown) => ipcRenderer.invoke('posts:update', id, data),
delete: (id: string) => ipcRenderer.invoke('posts:delete', id),
get: (id: string) => ipcRenderer.invoke('posts:get', id),
getPreviewUrl: (id: string) => ipcRenderer.invoke('posts:getPreviewUrl', id),
getPreviewUrl: (id: string, options?: { draft?: boolean }) => ipcRenderer.invoke('posts:getPreviewUrl', id, options),
getAll: (options?: { limit?: number; offset?: number }) => ipcRenderer.invoke('posts:getAll', options),
getByStatus: (status: string) => ipcRenderer.invoke('posts:getByStatus', status),
publish: (id: string) => ipcRenderer.invoke('posts:publish', id),

View File

@@ -442,7 +442,7 @@ export interface ElectronAPI {
update: (id: string, data: Partial<PostData>) => Promise<PostData | null>;
delete: (id: string) => Promise<boolean>;
get: (id: string) => Promise<PostData | null>;
getPreviewUrl: (id: string) => Promise<string | null>;
getPreviewUrl: (id: string, options?: { draft?: boolean }) => Promise<string | null>;
getAll: (options?: { limit?: number; offset?: number }) => Promise<PaginatedPostsResult>;
getByStatus: (status: string) => Promise<PostData[]>;
publish: (id: string) => Promise<PostData | null>;