This repository has no description
1{{ define "layouts/base" }}
2 <!doctype html>
3 <html lang="en"{{ if or (eq .ThemePreference "dark") (eq .ThemePreference "light") }} data-theme="{{ .ThemePreference }}"{{ end }}>
4 <head>
5 <meta charset="UTF-8" />
6 <meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover"/>
7 <meta name="description" content="The next-generation social coding platform."/>
8 <meta name="htmx-config" content='{"includeIndicatorStyles": false}'>
9
10 <!-- Open Graph defaults -->
11 <meta property="og:site_name" content="Tangled" />
12 <meta property="og:type" content="website" />
13 <meta property="og:locale" content="en_US" />
14
15
16 <!-- Keywords -->
17 <meta name="keywords" content="git, code collaboration, AT Protocol, open source, version control, social coding, code hosting" />
18
19 <!-- Author and copyright -->
20 <meta name="author" content="Tangled" />
21 <meta name="robots" content="index, follow" />
22
23 <script defer src="/static/htmx.min.js"></script>
24 <script defer src="/static/htmx-ext-ws.min.js"></script>
25 <script defer src="/static/actor-typeahead.js" type="module"></script>
26 <script defer src="/static/markdown-editor.js" type="module"></script>
27 <script defer src="/static/topbar-search.js"></script>
28
29 <link rel="icon" href="/static/logos/dolly.ico" sizes="48x48"/>
30 <link rel="icon" href="/static/logos/dolly.svg" sizes="any" type="image/svg+xml"/>
31 <link rel="apple-touch-icon" href="/static/logos/dolly.png"/>
32
33 <!-- preconnect to image cdn -->
34 <link rel="preconnect" href="https://avatar.tangled.sh" />
35 <link rel="preconnect" href="https://camo.tangled.sh" />
36
37 <!-- pwa manifest -->
38 <link rel="manifest" href="/pwa-manifest.json" />
39
40 <!-- preload main font -->
41 <link rel="preload" href="/static/fonts/InterVariable.woff2" as="font" type="font/woff2" crossorigin />
42
43 <script>
44 if (!document.documentElement.dataset.theme && matchMedia('(prefers-color-scheme: dark)').matches)
45 document.documentElement.dataset.theme = 'dark';
46 </script>
47 <link rel="stylesheet" href="/static/tw.css?{{ cssContentHash }}" type="text/css" />
48
49 <script>
50 document.addEventListener('DOMContentLoaded', () => {
51 const nodes = document.querySelectorAll('pre.mermaid');
52 if (!nodes.length) return;
53 const script = document.createElement('script');
54 script.src = '/static/mermaid.min.js';
55 script.onload = async () => {
56 mermaid.initialize({
57 startOnLoad: true,
58 theme: window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'default',
59 });
60 await mermaid.run({ nodes });
61 };
62 document.head.appendChild(script);
63 });
64 </script>
65 <script>
66 document.addEventListener('DOMContentLoaded', () => {
67 const nodes = document.querySelectorAll('span.math');
68 if (!nodes.length) return;
69 window.MathJax = {
70 tex: {
71 inlineMath: [['\\(', '\\)']],
72 displayMath: [['\\[', '\\]']],
73 },
74 options: {
75 skipHtmlTags: ['script', 'noscript', 'style', 'textarea', 'pre', 'code'],
76 // v4 turns on semantic enrichment + speech by default, which
77 // spins up a speech webworker. These are gated by the menu
78 // settings (even when the menu itself is disabled), so turn
79 // them off here to keep rendering self-contained.
80 enableMenu: false,
81 menuOptions: {
82 settings: {
83 enrich: false,
84 speech: false,
85 braille: false,
86 assistiveMml: false,
87 collapsible: false,
88 },
89 },
90 },
91 startup: { typeset: false },
92 };
93 const script = document.createElement('script');
94 script.src = '/static/mathjax.min.js';
95 script.onload = () => {
96 MathJax.typesetPromise(Array.from(nodes));
97 };
98 document.head.appendChild(script);
99 });
100 </script>
101 <script>
102 window.addEventListener('scroll', function() {
103 document.querySelectorAll('[data-profile-popover]:not(.hidden)').forEach(function(p) {
104 p.classList.add('hidden');
105 });
106 }, { passive: true });
107 </script>
108 <title>{{ block "title" . }}{{ end }}</title>
109 {{ block "extrameta" . }}{{ end }}
110 </head>
111 <body class="min-h-screen flex flex-col gap-4 bg-slate-100 dark:bg-gray-900 dark:text-white transition-colors duration-200 {{ block "bodyClasses" . }} {{ end }}">
112 {{ block "topbarLayout" . }}
113 <header class="w-full col-span-full md:col-span-1 md:col-start-2 shadow-sm dark:text-white bg-white dark:bg-gray-800 pt-[env(safe-area-inset-top)]" style="z-index: 20;">
114
115 {{ if .LoggedInUser }}
116 <div id="upgrade-banner"
117 hx-get="/upgradeBanner"
118 hx-trigger="load"
119 hx-swap="innerHTML">
120 </div>
121 {{ end }}
122 {{ template "layouts/fragments/topbar" . }}
123 </header>
124 {{ end }}
125
126 {{ block "mainLayout" . }}
127 <div class="flex-grow">
128 <div class="max-w-screen-lg mx-auto flex flex-col gap-4">
129 {{ block "contentLayout" . }}
130 <main>
131 {{ block "content" . }}{{ end }}
132 </main>
133 {{ end }}
134
135 {{ block "contentAfterLayout" . }}
136 <main>
137 {{ block "contentAfter" . }}{{ end }}
138 </main>
139 {{ end }}
140 </div>
141 </div>
142 {{ end }}
143
144 {{ block "footerLayout" . }}
145 <footer class="mt-12 pb-[env(safe-area-inset-bottom)]">
146 {{ template "layouts/fragments/footer" . }}
147 </footer>
148 {{ end }}
149
150 {{ template "focus/pill" .FocusParams }}
151 </body>
152 </html>
153{{ end }}