|
| 1 | +import ConcurrencyExtras |
| 2 | +import Dependencies |
| 3 | +import Elementary |
| 4 | +import Hummingbird |
| 5 | + |
| 6 | +public protocol Page: Sendable, HTML, ResponseGenerator { |
| 7 | + associatedtype Head: HTML |
| 8 | + associatedtype Body: HTML |
| 9 | + |
| 10 | + @HTMLBuilder var head: Head { get } |
| 11 | + @HTMLBuilder var body: Body { get } |
| 12 | + |
| 13 | + var title: String { get } |
| 14 | + var lang: String { get } |
| 15 | + |
| 16 | + var chunkSize: Int { get } |
| 17 | + var headers: HTTPFields { get } |
| 18 | +} |
| 19 | + |
| 20 | +public extension Page { |
| 21 | + var lang: String { "en" } |
| 22 | + var chunkSize: Int { 1024 } |
| 23 | + var headers: HTTPFields { [.contentType: "text/html; charset=utf-8"] } |
| 24 | + |
| 25 | + @HTMLBuilder var content: some HTML { |
| 26 | + withDependencies { |
| 27 | + $0.styleSheetGenerator = StyleSheetGenerator() |
| 28 | + } operation: { |
| 29 | + HTMLBuilder.builder { |
| 30 | + @Dependency(\.styleSheetGenerator) var generator |
| 31 | + let _ = generator.addElements { |
| 32 | + Elementary.body { |
| 33 | + self.body |
| 34 | + VueScript() |
| 35 | + } |
| 36 | + .inlineStyle("background-color", "#1c1c1c") |
| 37 | + .inlineStyle("color", "#fafafa") |
| 38 | + .inlineStyle("font-optical-sizing", "auto") |
| 39 | + .inlineStyle("font-size", "0.7em") |
| 40 | + .inlineStyle("font-size", "0.8em", media: .minWidth(390)) |
| 41 | + .inlineStyle("font-size", "0.9em", media: .minWidth(480)) |
| 42 | + } |
| 43 | + |
| 44 | + HTMLRaw("<!DOCTYPE html>") |
| 45 | + html { |
| 46 | + Elementary.head { |
| 47 | + meta(.charset(.utf8)) |
| 48 | + Elementary.title { self.title } |
| 49 | + meta(name: .viewport, content: "width=device-width, initial-scale=1.0") |
| 50 | + style { HTMLRaw("/*! modern-normalize v3.0.1 | MIT License | https://github.com/sindresorhus/modern-normalize */*,::after,::before{box-sizing:border-box}html{font-family:system-ui,'Segoe UI',Roboto,Helvetica,Arial,sans-serif,'Apple Color Emoji','Segoe UI Emoji';line-height:1.15;-webkit-text-size-adjust:100%;tab-size:4}body{margin:0}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Consolas,'Liberation Mono',Menlo,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{border-color:currentcolor}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}legend{padding:0}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}") } |
| 51 | + style { |
| 52 | + HTMLRaw( |
| 53 | + """ |
| 54 | + @font-face { |
| 55 | + font-family: "CommitMono"; |
| 56 | + src: url("https://raw.githubusercontent.com/eigilnikolajsen/commit-mono/ecd81cdbd7f7eb2acaaa2f2f7e1a585676f9beff/src/fonts/fontlab/CommitMonoV143-VF.woff2"); |
| 57 | + font-style: normal; |
| 58 | + font-weight: 400; |
| 59 | + font-display: swap; |
| 60 | + } |
| 61 | + html { |
| 62 | + line-height: 1.5; |
| 63 | + } |
| 64 | + pre a { |
| 65 | + text-decoration: none; |
| 66 | + } |
| 67 | + h1, h2, h3, h4, h5, figure, p, ol, ul, pre { |
| 68 | + margin: 0; |
| 69 | + } |
| 70 | + ol[role="list"], ul[role="list"] { |
| 71 | + list-style: none; |
| 72 | + padding-inline: 0; |
| 73 | + } |
| 74 | + img, video { |
| 75 | + display: block; |
| 76 | + max-inline-size: 100%; |
| 77 | + } |
| 78 | + code { |
| 79 | + font-family: "CommitMono", monospace; |
| 80 | + font-feature-settings", "ss03", "ss04", "ss05"; |
| 81 | + line-height: 1; |
| 82 | + } |
| 83 | + """ |
| 84 | + ) |
| 85 | + } |
| 86 | + /// Xcode Styling |
| 87 | + style { HTMLRaw(".xml .hljs-meta{color:#6C7986}.hljs-comment,.hljs-quote{color:#6C7986}.hljs-tag,.hljs-attribute,.hljs-keyword,.hljs-selector-tag,.hljs-literal,.hljs-name{color:#FC5FA3}.hljs-variable,.hljs-template-variable{color:#FC5FA3}.hljs-code,.hljs-string,.hljs-meta-string{color:#FC6A5D}.hljs-regexp,.hljs-link{color:#5482FF}.hljs-title,.hljs-symbol,.hljs-bullet,.hljs-number{color:#41A1C0}.hljs-section,.hljs-meta{color:#FC5FA3}.hljs-class .hljs-title,.hljs-type,.hljs-built_in,.hljs-builtin-name,.hljs-params{color:#D0A8FF}.hljs-attr{color:#BF8555}.hljs-subst{color:#FFF}.hljs-formula{font-style:italic}.hljs-selector-id,.hljs-selector-class{color:#9b703f}.hljs-doctag,.hljs-strong{font-weight:bold}.hljs-emphasis{font-style:italic}") } |
| 88 | + script(.src("https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"), .defer) |
| 89 | + script(.src("https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/swift.min.js"), .defer) |
| 90 | + script(.src("https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/rust.min.js"), .defer) |
| 91 | + script(.src("https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/javascript.min.js"), .defer) |
| 92 | + script(.src("https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/typescript.min.js"), .defer) |
| 93 | + script(.type(.module)) { HTMLRaw("hljs.highlightAll();") } |
| 94 | + style { |
| 95 | + HTMLRaw(generator.renderStyleSheet()) |
| 96 | + } |
| 97 | + self.head |
| 98 | + } |
| 99 | + |
| 100 | + HTMLRaw(generator.renderedElements()) |
| 101 | + } |
| 102 | + .attributes(.lang(self.lang)) |
| 103 | + } |
| 104 | + } |
| 105 | + } |
| 106 | +} |
| 107 | + |
| 108 | +private extension HTMLBuilder { |
| 109 | + static func builder<R: HTML>(@HTMLBuilder operation: () -> R) -> R { |
| 110 | + operation() |
| 111 | + } |
| 112 | +} |
0 commit comments