diff --git a/quartz.layout.ts b/quartz.layout.ts index f3fbae8f8..c2d1aacd0 100644 --- a/quartz.layout.ts +++ b/quartz.layout.ts @@ -41,7 +41,36 @@ export const defaultContentPageLayout: PageLayout = { { Component: Component.ReaderMode() }, ], }), - Component.Explorer(), + Component.Explorer({ + sortFn: (a, b) => { + // Sort folders first, then files by date (newest first) + if (a.isFolder && b.isFolder) { + // Both folders: alphabetical + return a.displayName.localeCompare(b.displayName, undefined, { + numeric: true, + sensitivity: "base", + }) + } + + if (a.isFolder) return -1 // Folders before files + if (b.isFolder) return 1 // Folders before files + + // Both files: sort by date descending (newest first) + // Note: dates come from JSON as strings, need to convert to Date objects + const aDate = a.data?.date ? new Date(a.data.date).getTime() : 0 + const bDate = b.data?.date ? new Date(b.data.date).getTime() : 0 + + if (aDate !== bDate) { + return bDate - aDate // Descending order (newest first) + } + + // Same date or no dates: alphabetical fallback + return a.displayName.localeCompare(b.displayName, undefined, { + numeric: true, + sensitivity: "base", + }) + }, + }), ], right: [ Component.ConditionalRender({ @@ -82,7 +111,36 @@ export const defaultListPageLayout: PageLayout = { { Component: Component.Darkmode() }, ], }), - Component.Explorer(), + Component.Explorer({ + sortFn: (a, b) => { + // Sort folders first, then files by date (newest first) + if (a.isFolder && b.isFolder) { + // Both folders: alphabetical + return a.displayName.localeCompare(b.displayName, undefined, { + numeric: true, + sensitivity: "base", + }) + } + + if (a.isFolder) return -1 // Folders before files + if (b.isFolder) return 1 // Folders before files + + // Both files: sort by date descending (newest first) + // Note: dates come from JSON as strings, need to convert to Date objects + const aDate = a.data?.date ? new Date(a.data.date).getTime() : 0 + const bDate = b.data?.date ? new Date(b.data.date).getTime() : 0 + + if (aDate !== bDate) { + return bDate - aDate // Descending order (newest first) + } + + // Same date or no dates: alphabetical fallback + return a.displayName.localeCompare(b.displayName, undefined, { + numeric: true, + sensitivity: "base", + }) + }, + }), ], right: [], } diff --git a/quartz/plugins/emitters/contentIndex.tsx b/quartz/plugins/emitters/contentIndex.tsx index 56392b358..1cc333c75 100644 --- a/quartz/plugins/emitters/contentIndex.tsx +++ b/quartz/plugins/emitters/contentIndex.tsx @@ -140,11 +140,11 @@ export const ContentIndex: QuartzEmitterPlugin> = (opts) => { const fp = joinSegments("static", "contentIndex") as FullSlug const simplifiedIndex = Object.fromEntries( Array.from(linkIndex).map(([slug, content]) => { - // remove description and from content index as nothing downstream + // remove description from content index as nothing downstream // actually uses it. we only keep it in the index as we need it - // for the RSS feed + // for the RSS feed. Keep date for Explorer sorting. delete content.description - delete content.date + // Note: keeping content.date for Explorer sorting return [slug, content] }), ) diff --git a/quartz/plugins/transformers/lastmod.ts b/quartz/plugins/transformers/lastmod.ts index 32a89cc23..ec3ed1764 100644 --- a/quartz/plugins/transformers/lastmod.ts +++ b/quartz/plugins/transformers/lastmod.ts @@ -23,6 +23,21 @@ function coerceDate(fp: string, d: any): Date { d = `${d}T00:00:00` } + // Handle MM.DD.YY format (e.g., "03.21.24") + if (typeof d === "string") { + const mmddyyRegex = /^(\d{1,2})\.(\d{1,2})\.(\d{2})$/ + const match = d.match(mmddyyRegex) + if (match) { + const month = match[1] + const day = match[2] + const year = match[3] + // Convert YY to YYYY (assume 20XX for years 00-99) + const fullYear = `20${year}` + // Convert to ISO format: YYYY-MM-DD + d = `${fullYear}-${month.padStart(2, '0')}-${day.padStart(2, '0')}T00:00:00` + } + } + const dt = new Date(d) const invalidDate = isNaN(dt.getTime()) || dt.getTime() === 0 if (invalidDate && d !== undefined) {