mirror of
				https://github.com/jackyzha0/quartz.git
				synced 2025-10-31 12:07:39 +01:00 
			
		
		
		
	Compare commits
	
		
			No commits in common. "c238dd16d9923aac404a16b8551d93e5b91a4c5a" and "bfd72347cf719a7500dd98484d037c2b2320729c" have entirely different histories.
		
	
	
		
			c238dd16d9
			...
			bfd72347cf
		
	
		
| @ -1,44 +0,0 @@ | |||||||
| --- |  | ||||||
| title: Reader Mode |  | ||||||
| tags: |  | ||||||
|   - component |  | ||||||
| --- |  | ||||||
| 
 |  | ||||||
| Reader Mode is a feature that allows users to focus on the content by hiding the sidebars and other UI elements. When enabled, it provides a clean, distraction-free reading experience. |  | ||||||
| 
 |  | ||||||
| ## Configuration |  | ||||||
| 
 |  | ||||||
| Reader Mode is enabled by default. To disable it, you can remove the component from your layout configuration in `quartz.layout.ts`: |  | ||||||
| 
 |  | ||||||
| ```ts |  | ||||||
| // Remove or comment out this line |  | ||||||
| Component.ReaderMode(), |  | ||||||
| ``` |  | ||||||
| 
 |  | ||||||
| ## Usage |  | ||||||
| 
 |  | ||||||
| The Reader Mode toggle appears as a button with a book icon. When clicked: |  | ||||||
| 
 |  | ||||||
| - Sidebars are hidden |  | ||||||
| - Hovering over the content area reveals the sidebars temporarily |  | ||||||
| 
 |  | ||||||
| Unlike Dark Mode, Reader Mode state is not persisted between page reloads but is maintained during SPA navigation within the site. |  | ||||||
| 
 |  | ||||||
| ## Customization |  | ||||||
| 
 |  | ||||||
| You can customize the appearance of Reader Mode through CSS variables and styles. The component uses the following classes: |  | ||||||
| 
 |  | ||||||
| - `.readermode`: The toggle button |  | ||||||
| - `.readerIcon`: The book icon |  | ||||||
| - `[reader-mode="on"]`: Applied to the root element when Reader Mode is active |  | ||||||
| 
 |  | ||||||
| Example customization in your custom CSS: |  | ||||||
| 
 |  | ||||||
| ```scss |  | ||||||
| .readermode { |  | ||||||
|   // Customize the button |  | ||||||
|   svg { |  | ||||||
|     stroke: var(--custom-color); |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| ``` |  | ||||||
							
								
								
									
										1
									
								
								index.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								index.d.ts
									
									
									
									
										vendored
									
									
								
							| @ -8,7 +8,6 @@ interface CustomEventMap { | |||||||
|   prenav: CustomEvent<{}> |   prenav: CustomEvent<{}> | ||||||
|   nav: CustomEvent<{ url: FullSlug }> |   nav: CustomEvent<{ url: FullSlug }> | ||||||
|   themechange: CustomEvent<{ theme: "light" | "dark" }> |   themechange: CustomEvent<{ theme: "light" | "dark" }> | ||||||
|   readermodechange: CustomEvent<{ mode: "on" | "off" }> |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| type ContentIndex = Record<FullSlug, ContentDetails> | type ContentIndex = Record<FullSlug, ContentDetails> | ||||||
|  | |||||||
							
								
								
									
										31
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										31
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							| @ -9,7 +9,7 @@ | |||||||
|       "version": "4.5.0", |       "version": "4.5.0", | ||||||
|       "license": "MIT", |       "license": "MIT", | ||||||
|       "dependencies": { |       "dependencies": { | ||||||
|         "@clack/prompts": "^0.10.1", |         "@clack/prompts": "^0.10.0", | ||||||
|         "@floating-ui/dom": "^1.6.13", |         "@floating-ui/dom": "^1.6.13", | ||||||
|         "@myriaddreamin/rehype-typst": "^0.5.4", |         "@myriaddreamin/rehype-typst": "^0.5.4", | ||||||
|         "@napi-rs/simple-git": "0.1.19", |         "@napi-rs/simple-git": "0.1.19", | ||||||
| @ -80,7 +80,7 @@ | |||||||
|         "@types/d3": "^7.4.3", |         "@types/d3": "^7.4.3", | ||||||
|         "@types/hast": "^3.0.4", |         "@types/hast": "^3.0.4", | ||||||
|         "@types/js-yaml": "^4.0.9", |         "@types/js-yaml": "^4.0.9", | ||||||
|         "@types/node": "^22.14.1", |         "@types/node": "^22.14.0", | ||||||
|         "@types/pretty-time": "^1.1.5", |         "@types/pretty-time": "^1.1.5", | ||||||
|         "@types/source-map-support": "^0.5.10", |         "@types/source-map-support": "^0.5.10", | ||||||
|         "@types/ws": "^8.18.1", |         "@types/ws": "^8.18.1", | ||||||
| @ -178,22 +178,21 @@ | |||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     "node_modules/@clack/core": { |     "node_modules/@clack/core": { | ||||||
|       "version": "0.4.2", |       "version": "0.4.1", | ||||||
|       "resolved": "https://registry.npmjs.org/@clack/core/-/core-0.4.2.tgz", |       "resolved": "https://registry.npmjs.org/@clack/core/-/core-0.4.1.tgz", | ||||||
|       "integrity": "sha512-NYQfcEy8MWIxrT5Fj8nIVchfRFA26yYKJcvBS7WlUIlw2OmQOY9DhGGXMovyI5J5PpxrCPGkgUi207EBrjpBvg==", |       "integrity": "sha512-Pxhij4UXg8KSr7rPek6Zowm+5M22rbd2g1nfojHJkxp5YkFqiZ2+YLEM/XGVIzvGOcM0nqjIFxrpDwWRZYWYjA==", | ||||||
|       "license": "MIT", |  | ||||||
|       "dependencies": { |       "dependencies": { | ||||||
|         "picocolors": "^1.0.0", |         "picocolors": "^1.0.0", | ||||||
|         "sisteransi": "^1.0.5" |         "sisteransi": "^1.0.5" | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     "node_modules/@clack/prompts": { |     "node_modules/@clack/prompts": { | ||||||
|       "version": "0.10.1", |       "version": "0.10.0", | ||||||
|       "resolved": "https://registry.npmjs.org/@clack/prompts/-/prompts-0.10.1.tgz", |       "resolved": "https://registry.npmjs.org/@clack/prompts/-/prompts-0.10.0.tgz", | ||||||
|       "integrity": "sha512-Q0T02vx8ZM9XSv9/Yde0jTmmBQufZhPJfYAg2XrrrxWWaZgq1rr8nU8Hv710BQ1dhoP8rtY7YUdpGej2Qza/cw==", |       "integrity": "sha512-H3rCl6CwW1NdQt9rE3n373t7o5cthPv7yUoxF2ytZvyvlJv89C5RYMJu83Hed8ODgys5vpBU0GKxIRG83jd8NQ==", | ||||||
|       "license": "MIT", |       "license": "MIT", | ||||||
|       "dependencies": { |       "dependencies": { | ||||||
|         "@clack/core": "0.4.2", |         "@clack/core": "0.4.1", | ||||||
|         "picocolors": "^1.0.0", |         "picocolors": "^1.0.0", | ||||||
|         "sisteransi": "^1.0.5" |         "sisteransi": "^1.0.5" | ||||||
|       } |       } | ||||||
| @ -1941,9 +1940,9 @@ | |||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     "node_modules/@types/node": { |     "node_modules/@types/node": { | ||||||
|       "version": "22.14.1", |       "version": "22.14.0", | ||||||
|       "resolved": "https://registry.npmjs.org/@types/node/-/node-22.14.1.tgz", |       "resolved": "https://registry.npmjs.org/@types/node/-/node-22.14.0.tgz", | ||||||
|       "integrity": "sha512-u0HuPQwe/dHrItgHHpmw3N2fYCR6x4ivMNbPHRkBVP4CvN+kiRrKHWk3i8tXiO/joPwXLMYvF9TTF0eqgHIuOw==", |       "integrity": "sha512-Kmpl+z84ILoG+3T/zQFyAJsU6EPTmOCj8/2+83fSN6djd6I4o7uOuGIH6vq3PrjY5BGitSbFuMN18j3iknubbA==", | ||||||
|       "dev": true, |       "dev": true, | ||||||
|       "license": "MIT", |       "license": "MIT", | ||||||
|       "dependencies": { |       "dependencies": { | ||||||
| @ -5504,8 +5503,7 @@ | |||||||
|     "node_modules/picocolors": { |     "node_modules/picocolors": { | ||||||
|       "version": "1.1.1", |       "version": "1.1.1", | ||||||
|       "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", |       "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", | ||||||
|       "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", |       "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" | ||||||
|       "license": "ISC" |  | ||||||
|     }, |     }, | ||||||
|     "node_modules/picomatch": { |     "node_modules/picomatch": { | ||||||
|       "version": "2.3.1", |       "version": "2.3.1", | ||||||
| @ -6704,8 +6702,7 @@ | |||||||
|     "node_modules/sisteransi": { |     "node_modules/sisteransi": { | ||||||
|       "version": "1.0.5", |       "version": "1.0.5", | ||||||
|       "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", |       "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", | ||||||
|       "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", |       "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==" | ||||||
|       "license": "MIT" |  | ||||||
|     }, |     }, | ||||||
|     "node_modules/slash": { |     "node_modules/slash": { | ||||||
|       "version": "5.1.0", |       "version": "5.1.0", | ||||||
|  | |||||||
| @ -35,7 +35,7 @@ | |||||||
|     "quartz": "./quartz/bootstrap-cli.mjs" |     "quartz": "./quartz/bootstrap-cli.mjs" | ||||||
|   }, |   }, | ||||||
|   "dependencies": { |   "dependencies": { | ||||||
|     "@clack/prompts": "^0.10.1", |     "@clack/prompts": "^0.10.0", | ||||||
|     "@floating-ui/dom": "^1.6.13", |     "@floating-ui/dom": "^1.6.13", | ||||||
|     "@myriaddreamin/rehype-typst": "^0.5.4", |     "@myriaddreamin/rehype-typst": "^0.5.4", | ||||||
|     "@napi-rs/simple-git": "0.1.19", |     "@napi-rs/simple-git": "0.1.19", | ||||||
| @ -103,7 +103,7 @@ | |||||||
|     "@types/d3": "^7.4.3", |     "@types/d3": "^7.4.3", | ||||||
|     "@types/hast": "^3.0.4", |     "@types/hast": "^3.0.4", | ||||||
|     "@types/js-yaml": "^4.0.9", |     "@types/js-yaml": "^4.0.9", | ||||||
|     "@types/node": "^22.14.1", |     "@types/node": "^22.14.0", | ||||||
|     "@types/pretty-time": "^1.1.5", |     "@types/pretty-time": "^1.1.5", | ||||||
|     "@types/source-map-support": "^0.5.10", |     "@types/source-map-support": "^0.5.10", | ||||||
|     "@types/ws": "^8.18.1", |     "@types/ws": "^8.18.1", | ||||||
|  | |||||||
| @ -35,7 +35,6 @@ export const defaultContentPageLayout: PageLayout = { | |||||||
|           grow: true, |           grow: true, | ||||||
|         }, |         }, | ||||||
|         { Component: Component.Darkmode() }, |         { Component: Component.Darkmode() }, | ||||||
|         { Component: Component.ReaderMode() }, |  | ||||||
|       ], |       ], | ||||||
|     }), |     }), | ||||||
|     Component.Explorer(), |     Component.Explorer(), | ||||||
|  | |||||||
| @ -1,32 +0,0 @@ | |||||||
| // @ts-ignore
 |  | ||||||
| import readerModeScript from "./scripts/readermode.inline" |  | ||||||
| import styles from "./styles/readermode.scss" |  | ||||||
| import { QuartzComponent, QuartzComponentConstructor, QuartzComponentProps } from "./types" |  | ||||||
| import { classNames } from "../util/lang" |  | ||||||
| 
 |  | ||||||
| const ReaderMode: QuartzComponent = ({ displayClass }: QuartzComponentProps) => { |  | ||||||
|   return ( |  | ||||||
|     <button class={classNames(displayClass, "readermode")}> |  | ||||||
|       <svg |  | ||||||
|         xmlns="http://www.w3.org/2000/svg" |  | ||||||
|         class="readerIcon" |  | ||||||
|         viewBox="0 0 24 24" |  | ||||||
|         fill="none" |  | ||||||
|         stroke="currentColor" |  | ||||||
|         stroke-width="2" |  | ||||||
|         stroke-linecap="round" |  | ||||||
|         stroke-linejoin="round" |  | ||||||
|       > |  | ||||||
|         <rect x="6" y="4" width="12" height="16" rx="1"></rect> |  | ||||||
|         <line x1="9" y1="8" x2="15" y2="8"></line> |  | ||||||
|         <line x1="9" y1="12" x2="15" y2="12"></line> |  | ||||||
|         <line x1="9" y1="16" x2="13" y2="16"></line> |  | ||||||
|       </svg> |  | ||||||
|     </button> |  | ||||||
|   ) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| ReaderMode.beforeDOMLoaded = readerModeScript |  | ||||||
| ReaderMode.css = styles |  | ||||||
| 
 |  | ||||||
| export default (() => ReaderMode) satisfies QuartzComponentConstructor |  | ||||||
| @ -4,7 +4,6 @@ import FolderContent from "./pages/FolderContent" | |||||||
| import NotFound from "./pages/404" | import NotFound from "./pages/404" | ||||||
| import ArticleTitle from "./ArticleTitle" | import ArticleTitle from "./ArticleTitle" | ||||||
| import Darkmode from "./Darkmode" | import Darkmode from "./Darkmode" | ||||||
| import ReaderMode from "./ReaderMode" |  | ||||||
| import Head from "./Head" | import Head from "./Head" | ||||||
| import PageTitle from "./PageTitle" | import PageTitle from "./PageTitle" | ||||||
| import ContentMeta from "./ContentMeta" | import ContentMeta from "./ContentMeta" | ||||||
| @ -30,7 +29,6 @@ export { | |||||||
|   TagContent, |   TagContent, | ||||||
|   FolderContent, |   FolderContent, | ||||||
|   Darkmode, |   Darkmode, | ||||||
|   ReaderMode, |  | ||||||
|   Head, |   Head, | ||||||
|   PageTitle, |   PageTitle, | ||||||
|   ContentMeta, |   ContentMeta, | ||||||
|  | |||||||
| @ -1,25 +0,0 @@ | |||||||
| let isReaderMode = false |  | ||||||
| 
 |  | ||||||
| const emitReaderModeChangeEvent = (mode: "on" | "off") => { |  | ||||||
|   const event: CustomEventMap["readermodechange"] = new CustomEvent("readermodechange", { |  | ||||||
|     detail: { mode }, |  | ||||||
|   }) |  | ||||||
|   document.dispatchEvent(event) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| document.addEventListener("nav", () => { |  | ||||||
|   const switchReaderMode = () => { |  | ||||||
|     isReaderMode = !isReaderMode |  | ||||||
|     const newMode = isReaderMode ? "on" : "off" |  | ||||||
|     document.documentElement.setAttribute("reader-mode", newMode) |  | ||||||
|     emitReaderModeChangeEvent(newMode) |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   for (const readerModeButton of document.getElementsByClassName("readermode")) { |  | ||||||
|     readerModeButton.addEventListener("click", switchReaderMode) |  | ||||||
|     window.addCleanup(() => readerModeButton.removeEventListener("click", switchReaderMode)) |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   // Set initial state
 |  | ||||||
|   document.documentElement.setAttribute("reader-mode", isReaderMode ? "on" : "off") |  | ||||||
| }) |  | ||||||
| @ -6,7 +6,7 @@ | |||||||
|   border: none; |   border: none; | ||||||
|   width: 20px; |   width: 20px; | ||||||
|   height: 20px; |   height: 20px; | ||||||
|   margin: 0; |   margin: 0 10px; | ||||||
|   text-align: inherit; |   text-align: inherit; | ||||||
|   flex-shrink: 0; |   flex-shrink: 0; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,33 +0,0 @@ | |||||||
| .readermode { |  | ||||||
|   cursor: pointer; |  | ||||||
|   padding: 0; |  | ||||||
|   position: relative; |  | ||||||
|   background: none; |  | ||||||
|   border: none; |  | ||||||
|   width: 20px; |  | ||||||
|   height: 20px; |  | ||||||
|   margin: 0; |  | ||||||
|   text-align: inherit; |  | ||||||
|   flex-shrink: 0; |  | ||||||
| 
 |  | ||||||
|   & svg { |  | ||||||
|     position: absolute; |  | ||||||
|     width: 20px; |  | ||||||
|     height: 20px; |  | ||||||
|     top: calc(50% - 10px); |  | ||||||
|     stroke: var(--darkgray); |  | ||||||
|     transition: opacity 0.1s ease; |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| :root[reader-mode="on"] { |  | ||||||
|   & .sidebar.left, |  | ||||||
|   & .sidebar.right { |  | ||||||
|     opacity: 0; |  | ||||||
|     transition: opacity 0.2s ease; |  | ||||||
| 
 |  | ||||||
|     &:hover { |  | ||||||
|       opacity: 1; |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| } |  | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user