22import BaseLayout from " ./BaseLayout.astro" ;
33import { formatDate , slugify , withBase } from " ../lib/utils" ;
44
5+ interface Reference {
6+ type: string ;
7+ label: string ;
8+ url: string ;
9+ }
10+
511interface Props {
612 title: string ;
713 publishDate: string ;
@@ -10,10 +16,31 @@ interface Props {
1016 description? : string | null ;
1117 tags? : readonly string [];
1218 slug? : string ;
19+ legacyUrl? : string | null ;
20+ references? : Reference [];
1321}
1422
1523const isDev = import .meta .env .DEV ;
16- const { title, publishDate, updatedDate, author, description, tags, slug } = Astro .props ;
24+ const { title, publishDate, updatedDate, author, description, tags, slug, legacyUrl, references = [] } = Astro .props ;
25+ const bloggerUrl = legacyUrl ? ` https://pythoninsider.blogspot.com${legacyUrl } ` : null ;
26+
27+ // Group references by type
28+ const refGroups = new Map <string , Reference []>();
29+ for (const ref of references ) {
30+ if (! refGroups .has (ref .type )) refGroups .set (ref .type , []);
31+ refGroups .get (ref .type )! .push (ref );
32+ }
33+
34+ const groupMeta: Record <string , { label: string ; order: number }> = {
35+ " pep" : { label: " PEPs" , order: 0 },
36+ " docs" : { label: " Documentation" , order: 1 },
37+ " gh-repo" : { label: " Repositories" , order: 2 },
38+ " gh-user" : { label: " People" , order: 3 },
39+ " pypi" : { label: " Packages" , order: 4 },
40+ };
41+
42+ const sortedGroups = [... refGroups .entries ()]
43+ .sort ((a , b ) => (groupMeta [a [0 ]]?.order ?? 99 ) - (groupMeta [b [0 ]]?.order ?? 99 ));
1744---
1845
1946<BaseLayout title ={ ` ${title } | Python Insider ` } description ={ description ?? undefined } >
@@ -66,12 +93,100 @@ const { title, publishDate, updatedDate, author, description, tags, slug } = Ast
6693 <div class =" mt-8 h-px" style =" background: linear-gradient(to right, rgba(48, 105, 152, 0.3), rgba(255, 212, 59, 0.2), transparent);" ></div >
6794 </header >
6895
96+ <!-- Blogger legacy notice -->
97+ { bloggerUrl && (
98+ <div class = " not-prose mb-6 animate-fade-up stagger-1" >
99+ <a
100+ href = { bloggerUrl }
101+ target = " _blank"
102+ rel = " noopener noreferrer"
103+ class = " flex items-center gap-3 rounded-lg border border-orange-200/60 bg-gradient-to-r from-orange-50 to-amber-50/50 px-4 py-3 transition-colors hover:border-orange-300/80 hover:from-orange-50 hover:to-amber-50 dark:border-orange-800/30 dark:from-orange-950/20 dark:to-amber-950/10 dark:hover:border-orange-700/40 dark:hover:from-orange-950/30 dark:hover:to-amber-950/20"
104+ >
105+ <div class = " flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-full bg-orange-100 dark:bg-orange-900/30" >
106+ <svg class = " h-4 w-4 text-orange-500 dark:text-orange-400" fill = " none" viewBox = " 0 0 24 24" stroke = " currentColor" stroke-width = " 2" stroke-linecap = " round" stroke-linejoin = " round" >
107+ <path d = " M12 8v4l3 3" />
108+ <circle cx = " 12" cy = " 12" r = " 10" />
109+ </svg >
110+ </div >
111+ <div class = " min-w-0 flex-1" >
112+ <p class = " text-xs font-medium text-orange-800 dark:text-orange-300" >Originally published on Blogger</p >
113+ <p class = " mt-0.5 truncate text-[11px] text-orange-600/70 dark:text-orange-400/50" >{ bloggerUrl } </p >
114+ </div >
115+ <svg class = " h-4 w-4 flex-shrink-0 text-orange-400 dark:text-orange-600" fill = " none" viewBox = " 0 0 24 24" stroke = " currentColor" stroke-width = " 2" >
116+ <path stroke-linecap = " round" stroke-linejoin = " round" d = " M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14" />
117+ </svg >
118+ </a >
119+ </div >
120+ )}
121+
122+ <!-- References panel -->
123+ { sortedGroups .length > 0 && (
124+ <div class = " not-prose mb-8 animate-fade-up stagger-1" >
125+ <div class = " post-refs-panel overflow-hidden rounded-xl border border-zinc-200/80 dark:border-zinc-700/60" >
126+ <div class = " post-refs-header flex items-center gap-2.5 px-5 py-3" >
127+ <svg class = " h-4 w-4 text-[#306998] dark:text-[#ffd43b]" fill = " none" viewBox = " 0 0 24 24" stroke = " currentColor" stroke-width = " 2" stroke-linecap = " round" stroke-linejoin = " round" >
128+ <path d = " M4 19.5v-15A2.5 2.5 0 0 1 6.5 2H19a1 1 0 0 1 1 1v18a1 1 0 0 1-1 1H6.5a1 1 0 0 1 0-5H20" />
129+ </svg >
130+ <span class = " text-xs font-bold uppercase tracking-[0.12em] text-zinc-500 dark:text-zinc-400" style = " font-family: var(--font-display);" >
131+ Referenced in this post
132+ </span >
133+ <span class = " ml-auto text-[10px] tabular-nums font-medium text-zinc-400 dark:text-zinc-500" >
134+ { references .length }
135+ </span >
136+ </div >
137+
138+ <div class = " space-y-px bg-zinc-100/50 dark:bg-zinc-800/30" >
139+ { sortedGroups .map (([type , refs ]) => (
140+ <div class = " bg-white px-5 py-3.5 dark:bg-[#0f1117]" >
141+ <div class = " mb-2.5 text-[10px] font-bold uppercase tracking-[0.15em] text-zinc-400 dark:text-zinc-500" >
142+ { groupMeta [type ]?.label ?? type }
143+ </div >
144+ <div class = " flex flex-wrap gap-1.5" >
145+ { refs .map ((ref ) => (
146+ <a
147+ href = { ref .url }
148+ class :list = { [
149+ " ref-footer-chip group inline-flex items-center gap-1.5 rounded-md px-2.5 py-1 text-xs font-medium transition-all" ,
150+ type === " pep" && " bg-amber-50 text-amber-800 hover:bg-amber-100 dark:bg-amber-900/20 dark:text-amber-300 dark:hover:bg-amber-900/40" ,
151+ type === " docs" && " bg-blue-50 text-blue-700 hover:bg-blue-100 dark:bg-blue-900/20 dark:text-blue-300 dark:hover:bg-blue-900/40" ,
152+ type === " gh-repo" && " bg-zinc-100 text-zinc-700 hover:bg-zinc-200 dark:bg-zinc-800 dark:text-zinc-300 dark:hover:bg-zinc-700" ,
153+ type === " gh-user" && " bg-zinc-100 text-zinc-700 hover:bg-zinc-200 dark:bg-zinc-800 dark:text-zinc-300 dark:hover:bg-zinc-700" ,
154+ type === " pypi" && " bg-emerald-50 text-emerald-700 hover:bg-emerald-100 dark:bg-emerald-900/20 dark:text-emerald-300 dark:hover:bg-emerald-900/40" ,
155+ ]}
156+ target = " _blank"
157+ rel = " noopener noreferrer"
158+ >
159+ { type === " pep" && (
160+ <svg class = " h-3 w-3 opacity-60" fill = " none" viewBox = " 0 0 24 24" stroke = " currentColor" stroke-width = " 2" stroke-linecap = " round" stroke-linejoin = " round" ><path d = " M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z" /><path d = " M14 2v4a2 2 0 0 0 2 2h4" /></svg >
161+ )}
162+ { type === " docs" && (
163+ <svg class = " h-3 w-3 opacity-60" fill = " none" viewBox = " 0 0 24 24" stroke = " currentColor" stroke-width = " 2" stroke-linecap = " round" stroke-linejoin = " round" ><path d = " M12 7v14" /><path d = " M3 18a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h5a4 4 0 0 1 4 4 4 4 0 0 1 4-4h5a1 1 0 0 1 1 1v13a1 1 0 0 1-1 1h-6a3 3 0 0 0-3 3 3 3 0 0 0-3-3z" /></svg >
164+ )}
165+ { (type === " gh-repo" || type === " gh-user" ) && (
166+ <svg class = " h-3 w-3 opacity-60" viewBox = " 0 0 24 24" fill = " currentColor" ><path d = " M12 0C5.37 0 0 5.37 0 12c0 5.31 3.435 9.795 8.205 11.385.6.105.825-.255.825-.57 0-.285-.015-1.23-.015-2.235-3.015.555-3.795-.735-4.035-1.41-.135-.345-.72-1.41-1.23-1.695-.42-.225-1.02-.78-.015-.795.945-.015 1.62.87 1.845 1.23 1.08 1.815 2.805 1.305 3.495.99.105-.78.42-1.305.765-1.605-2.67-.3-5.46-1.335-5.46-5.925 0-1.305.465-2.385 1.23-3.225-.12-.3-.54-1.53.12-3.18 0 0 1.005-.315 3.3 1.23.96-.27 1.98-.405 3-.405s2.04.135 3 .405c2.295-1.56 3.3-1.23 3.3-1.23.66 1.65.24 2.88.12 3.18.765.84 1.23 1.905 1.23 3.225 0 4.605-2.805 5.625-5.475 5.925.435.375.81 1.095.81 2.22 0 1.605-.015 2.895-.015 3.3 0 .315.225.69.825.57A12.02 12.02 0 0024 12c0-6.63-5.37-12-12-12z" /></svg >
167+ )}
168+ { type === " pypi" && (
169+ <svg class = " h-3 w-3 opacity-60" fill = " none" viewBox = " 0 0 24 24" stroke = " currentColor" stroke-width = " 2" stroke-linecap = " round" stroke-linejoin = " round" ><path d = " M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z" /><polyline points = " 3.29 7 12 12 20.71 7" /><line x1 = " 12" x2 = " 12" y1 = " 22" y2 = " 12" /></svg >
170+ )}
171+ <span class = " truncate max-w-[200px]" >{ ref .label } </span >
172+ <svg class = " h-2.5 w-2.5 opacity-0 transition-opacity group-hover:opacity-50" fill = " none" viewBox = " 0 0 24 24" stroke = " currentColor" stroke-width = " 2.5" ><path d = " M7 7h10v10" /><path d = " M7 17 17 7" /></svg >
173+ </a >
174+ ))}
175+ </div >
176+ </div >
177+ ))}
178+ </div >
179+ </div >
180+ </div >
181+ )}
182+
69183 <div class =" animate-fade-up stagger-2" >
70184 <slot />
71185 </div >
72186
73- <footer class =" not-prose mt-14 border-t border-zinc-200 pt-8 dark:border-zinc-700" >
74- <div class =" flex items-center justify-between" >
187+ <footer class =" not-prose mt-16" >
188+ <!-- Navigation -->
189+ <div class =" flex items-center justify-between border-t border-zinc-200 pt-6 dark:border-zinc-700" >
75190 <a
76191 href ={ withBase (" /blog" )}
77192 class =" group inline-flex items-center gap-2 text-sm font-medium text-[#306998] transition-colors hover:text-[#254f73] dark:text-[#ffd43b] dark:hover:text-[#f0c419]"
0 commit comments