11{% extends "_base.html" %}
22{% load static page_tags %}
33
4- {% block title %}{{ title }} - DjangoStarter Docs{% endblock %}
4+ {% block title %}{{ page_title }} - DjangoStarter Docs{% endblock %}
55
66{% block head %}
77<!-- Highlight.js -->
88< link rel ="stylesheet " href ="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github-dark-dimmed.min.css ">
99{% endblock %}
1010
1111{% block content %}
12- {% page_header title breadcrumbs %}
1312
1413< div class ="drawer lg:drawer-open min-h-[calc(100vh-10rem)] rounded-box border border-base-200 bg-base-100 shadow-sm mb-8 ">
1514 < input id ="docs-drawer " type ="checkbox " class ="drawer-toggle " />
1615
1716 <!-- Main Content Area -->
18- < div class ="drawer-content flex flex-col xl:flex-row px-0 ">
19- <!-- Mobile Toggle -->
20- < div class ="lg:hidden px-4 mb-4 ">
21- < label for ="docs-drawer " class ="btn btn-primary drawer-button w-full ">
22- < i class ="fas fa-bars mr-2 "> </ i > 目录导航
23- </ label >
24- </ div >
25-
26- <!-- Doc Content -->
27- < div class ="flex-1 min-w-0 px-4 lg:px-8 ">
28- <!-- Search -->
29- < div class ="mb-8 max-w-2xl ">
30- < form method ="get " action ="{% url 'djs_docs:index' %} ">
31- < div class ="join w-full shadow-sm ">
32- < input name ="q " value ="{{ search_query }} " placeholder ="搜索文档、关键字... " class ="input input-bordered join-item w-full focus:outline-none " />
33- < button class ="btn btn-primary join-item px-6 "> < i class ="fas fa-search "> </ i > </ button >
34- </ div >
35- </ form >
36- </ div >
37-
38- {% if search_results %}
39- < div class ="mb-8 ">
40- < h3 class ="font-bold text-lg mb-4 "> 搜索结果: "{{ search_query }}"</ h3 >
41- < div class ="grid gap-4 ">
42- {% for result in search_results %}
43- < a href ="{% url 'djs_docs:detail' slug=result.slug %} " class ="card bg-base-200 hover:bg-base-300 transition-colors compact border border-base-300 ">
44- < div class ="card-body ">
45- < h2 class ="card-title text-base "> {{ result.title }}</ h2 >
46- < p class ="text-sm opacity-70 "> {{ result.summary }}</ p >
47- </ div >
48- </ a >
49- {% endfor %}
50- {% if not search_results %}
51- < div class ="alert ">
52- < i class ="fas fa-info-circle "> </ i >
53- < span > 未找到相关文档,请尝试更换关键词。</ span >
54- </ div >
55- {% endif %}
56- </ div >
57- < div class ="divider "> </ div >
58- </ div >
59- {% endif %}
60-
61- < article class ="prose prose-base lg:prose-lg max-w-none prose-pre:p-0 prose-pre:bg-transparent prose-img:rounded-xl ">
62- {% if active_page and not search_results %}
63- < div class ="mb-8 not-prose ">
64- < h1 class ="text-3xl lg:text-4xl font-extrabold mb-2 "> {{ active_page.title }}</ h1 >
65- < p class ="text-xl text-base-content/70 "> {{ active_page.summary }}</ p >
66- </ div >
67- {% endif %}
68-
69- {{ active_html|safe }}
70- </ article >
71-
72- <!-- Prev/Next Nav -->
73- < div class ="flex flex-col sm:flex-row justify-between mt-12 gap-4 pt-8 border-t border-base-200 ">
74- {% if prev_page %}
75- < a href ="{% url 'djs_docs:detail' slug=prev_page.slug %} " class ="group flex-1 p-4 rounded-xl border border-base-300 hover:border-primary hover:bg-base-200 transition-all text-left ">
76- < div class ="text-xs text-base-content/50 mb-1 group-hover:text-primary "> 上一篇</ div >
77- < div class ="font-bold text-lg flex items-center gap-2 ">
78- < i class ="fas fa-arrow-left text-sm transition-transform group-hover:-translate-x-1 "> </ i >
79- {{ prev_page.title }}
80- </ div >
81- </ a >
82- {% else %}
83- < div class ="flex-1 hidden sm:block "> </ div >
84- {% endif %}
85-
86- {% if next_page %}
87- < a href ="{% url 'djs_docs:detail' slug=next_page.slug %} " class ="group flex-1 p-4 rounded-xl border border-base-300 hover:border-primary hover:bg-base-200 transition-all text-right ">
88- < div class ="text-xs text-base-content/50 mb-1 group-hover:text-primary "> 下一篇</ div >
89- < div class ="font-bold text-lg flex items-center justify-end gap-2 ">
90- {{ next_page.title }}
91- < i class ="fas fa-arrow-right text-sm transition-transform group-hover:translate-x-1 "> </ i >
92- </ div >
93- </ a >
94- {% else %}
95- < div class ="flex-1 hidden sm:block "> </ div >
96- {% endif %}
97- </ div >
98- </ div >
99-
100- <!-- Right Sidebar (TOC) -->
101- {% if toc %}
102- < aside class ="hidden xl:block w-64 shrink-0 pl-6 border-l border-base-200 ">
103- < div class ="sticky top-6 ">
104- < div class ="text-xs font-bold mb-4 uppercase text-base-content/50 tracking-wider "> 本页目录</ div >
105- < div class ="prose prose-sm prose-ul:list-none prose-ul:pl-0 prose-li:pl-0 prose-a:no-underline prose-a:text-base-content/70 hover:prose-a:text-primary ">
106- {{ toc|safe }}
107- </ div >
108- </ div >
109- </ aside >
110- {% endif %}
17+ <!-- Added id for HTMX target -->
18+ < div id ="doc-main-content " class ="drawer-content ">
19+ {% include "django_starter/docs/_doc_content.html" %}
11120 </ div >
11221
11322 <!-- Left Sidebar (Drawer Side) -->
11423 < div class ="drawer-side z-50 lg:z-auto ">
11524 < label for ="docs-drawer " aria-label ="close sidebar " class ="drawer-overlay "> </ label >
116- < div class ="menu p-4 w-72 min-h-full bg-base-100 lg:bg-transparent text-base-content border-r lg:border-none border-base-200 ">
117- <!-- Mobile Header -->
118- < div class ="lg:hidden mb-6 px-2 flex items-center justify-between ">
119- < span class ="font-bold text-lg "> 文档导航</ span >
120- < label for ="docs-drawer " class ="btn btn-sm btn-ghost btn-circle "> < i class ="fas fa-times "> </ i > </ label >
121- </ div >
122-
123- {% for item in categories %}
124- < details open class ="group ">
125- < summary class ="font-bold opacity-60 hover:opacity-100 group-open:text-primary transition-colors "> {{ item.category.title }}</ summary >
126- < ul >
127- {% for page in item.pages %}
128- < li >
129- < a href ="{% url 'djs_docs:detail' slug=page.slug %} "
130- class ="{% if active_page and page.slug == active_page.slug %}active font-medium{% endif %} border-l-2 border-transparent {% if active_page and page.slug == active_page.slug %}border-primary{% endif %} ">
131- {{ page.title }}
132- </ a >
133- </ li >
134- {% endfor %}
135- </ ul >
136- </ details >
137- {% endfor %}
138-
139- < div class ="divider my-4 "> </ div >
140- < ul class ="menu-title px-2 "> 快捷链接</ ul >
141- < li > < a href ="/api/docs " target ="_blank "> < i class ="fas fa-code w-4 "> </ i > API 文档</ a > </ li >
142- < li > < a href ="/admin " target ="_blank "> < i class ="fas fa-cogs w-4 "> </ i > 管理后台</ a > </ li >
25+ <!-- Added id for HTMX OOB Swap -->
26+ < div id ="doc-sidebar-menu " class ="menu p-4 w-72 min-h-full bg-base-100 lg:bg-transparent text-base-content border-r lg:border-none border-base-200 ">
27+ {% include "django_starter/docs/_sidebar_menu.html" %}
14328 </ div >
14429 </ div >
14530</ div >
@@ -148,35 +33,49 @@ <h1 class="text-3xl lg:text-4xl font-extrabold mb-2">{{ active_page.title }}</h1
14833{% block scripts %}
14934< script src ="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js "> </ script >
15035< script >
151- // Init Highlight.js
152- hljs . highlightAll ( ) ;
36+ function initDocs ( ) {
37+ // Init Highlight.js
38+ hljs . highlightAll ( ) ;
39+
40+ // Copy Code Button Logic
41+ document . querySelectorAll ( 'pre' ) . forEach ( ( pre ) => {
42+ if ( pre . parentNode . classList . contains ( 'relative' ) && pre . parentNode . classList . contains ( 'group' ) ) {
43+ return ; // Already initialized
44+ }
45+
46+ // Wrapper for positioning
47+ const wrapper = document . createElement ( 'div' ) ;
48+ wrapper . className = 'relative group mb-4' ;
49+
50+ // Replace pre with wrapper in DOM
51+ pre . parentNode . insertBefore ( wrapper , pre ) ;
52+ wrapper . appendChild ( pre ) ;
53+
54+ // Styling the pre to look nice with the button
55+ pre . className += ' !bg-[#0d1117] !p-4 !rounded-xl !my-0' ; // Force GitHub Dark bg
56+
57+ // Create button
58+ const btn = document . createElement ( 'button' ) ;
59+ btn . className = 'btn btn-xs btn-square btn-ghost absolute top-2 right-2 opacity-0 group-hover:opacity-100 transition-all bg-base-100/10 hover:bg-base-100/20 text-white' ;
60+ btn . innerHTML = '<i class="fas fa-copy"></i>' ;
61+ btn . title = "复制代码" ;
62+
63+ btn . onclick = ( ) => {
64+ const code = pre . querySelector ( 'code' ) . innerText ;
65+ navigator . clipboard . writeText ( code ) ;
66+ btn . innerHTML = '<i class="fas fa-check text-success"></i>' ;
67+ setTimeout ( ( ) => btn . innerHTML = '<i class="fas fa-copy"></i>' , 2000 ) ;
68+ } ;
69+ wrapper . appendChild ( btn ) ;
70+ } ) ;
71+ }
15372
154- // Copy Code Button Logic
155- document . querySelectorAll ( 'pre' ) . forEach ( ( pre ) => {
156- // Wrapper for positioning
157- const wrapper = document . createElement ( 'div' ) ;
158- wrapper . className = 'relative group mb-4' ;
159-
160- // Replace pre with wrapper in DOM
161- pre . parentNode . insertBefore ( wrapper , pre ) ;
162- wrapper . appendChild ( pre ) ;
73+ // Initial load
74+ initDocs ( ) ;
16375
164- // Styling the pre to look nice with the button
165- pre . className += ' !bg-[#0d1117] !p-4 !rounded-xl !my-0' ; // Force GitHub Dark bg
166-
167- // Create button
168- const btn = document . createElement ( 'button' ) ;
169- btn . className = 'btn btn-xs btn-square btn-ghost absolute top-2 right-2 opacity-0 group-hover:opacity-100 transition-all bg-base-100/10 hover:bg-base-100/20 text-white' ;
170- btn . innerHTML = '<i class="fas fa-copy"></i>' ;
171- btn . title = "复制代码" ;
172-
173- btn . onclick = ( ) => {
174- const code = pre . querySelector ( 'code' ) . innerText ;
175- navigator . clipboard . writeText ( code ) ;
176- btn . innerHTML = '<i class="fas fa-check text-success"></i>' ;
177- setTimeout ( ( ) => btn . innerHTML = '<i class="fas fa-copy"></i>' , 2000 ) ;
178- } ;
179- wrapper . appendChild ( btn ) ;
76+ // Re-init on HTMX swap
77+ document . body . addEventListener ( 'htmx:afterSwap' , function ( evt ) {
78+ initDocs ( ) ;
18079 } ) ;
18180</ script >
18281{% endblock %}
0 commit comments