forked from tangled.org/core
this repo has no description

Compare changes

Choose any two refs to compare.

Changed files
+222 -11
appview
notify
pages
templates
posthog
strings
contrib
+18
appview/notify/merged_notifier.go
··· 66 66 notifier.UpdateProfile(ctx, profile) 67 67 } 68 68 } 69 + 70 + func (m *mergedNotifier) NewString(ctx context.Context, string *db.String) { 71 + for _, notifier := range m.notifiers { 72 + notifier.NewString(ctx, string) 73 + } 74 + } 75 + 76 + func (m *mergedNotifier) EditString(ctx context.Context, string *db.String) { 77 + for _, notifier := range m.notifiers { 78 + notifier.EditString(ctx, string) 79 + } 80 + } 81 + 82 + func (m *mergedNotifier) DeleteString(ctx context.Context, did, rkey string) { 83 + for _, notifier := range m.notifiers { 84 + notifier.DeleteString(ctx, did, rkey) 85 + } 86 + }
+8
appview/notify/notifier.go
··· 21 21 NewPullComment(ctx context.Context, comment *db.PullComment) 22 22 23 23 UpdateProfile(ctx context.Context, profile *db.Profile) 24 + 25 + NewString(ctx context.Context, s *db.String) 26 + EditString(ctx context.Context, s *db.String) 27 + DeleteString(ctx context.Context, did, rkey string) 24 28 } 25 29 26 30 // BaseNotifier is a listener that does nothing ··· 42 46 func (m *BaseNotifier) NewPullComment(ctx context.Context, comment *db.PullComment) {} 43 47 44 48 func (m *BaseNotifier) UpdateProfile(ctx context.Context, profile *db.Profile) {} 49 + 50 + func (m *BaseNotifier) NewString(ctx context.Context, s *db.String) {} 51 + func (m *BaseNotifier) EditString(ctx context.Context, s *db.String) {} 52 + func (m *BaseNotifier) DeleteString(ctx context.Context, did, rkey string) {}
+90
appview/pages/templates/fragments/multiline-select.html
··· 1 + {{ define "fragments/multiline-select" }} 2 + <script> 3 + function highlight(scroll = false) { 4 + document.querySelectorAll(".hl").forEach(el => { 5 + el.classList.remove("hl"); 6 + }); 7 + 8 + const hash = window.location.hash; 9 + if (!hash || !hash.startsWith("#L")) { 10 + return; 11 + } 12 + 13 + const rangeStr = hash.substring(2); 14 + const parts = rangeStr.split("-"); 15 + let startLine, endLine; 16 + 17 + if (parts.length === 2) { 18 + startLine = parseInt(parts[0], 10); 19 + endLine = parseInt(parts[1], 10); 20 + } else { 21 + startLine = parseInt(parts[0], 10); 22 + endLine = startLine; 23 + } 24 + 25 + if (isNaN(startLine) || isNaN(endLine)) { 26 + console.log("nan"); 27 + console.log(startLine); 28 + console.log(endLine); 29 + return; 30 + } 31 + 32 + let target = null; 33 + 34 + for (let i = startLine; i<= endLine; i++) { 35 + const idEl = document.getElementById(`L${i}`); 36 + if (idEl) { 37 + const el = idEl.closest(".line"); 38 + if (el) { 39 + el.classList.add("hl"); 40 + target = el; 41 + } 42 + } 43 + } 44 + 45 + if (scroll && target) { 46 + target.scrollIntoView({ 47 + behavior: "smooth", 48 + block: "center", 49 + }); 50 + } 51 + } 52 + 53 + document.addEventListener("DOMContentLoaded", () => { 54 + console.log("DOMContentLoaded"); 55 + highlight(true); 56 + }); 57 + window.addEventListener("hashchange", () => { 58 + console.log("hashchange"); 59 + highlight(); 60 + }); 61 + window.addEventListener("popstate", () => { 62 + console.log("popstate"); 63 + highlight(); 64 + }); 65 + 66 + const lineNumbers = document.querySelectorAll('a[href^="#L"'); 67 + let startLine = null; 68 + 69 + lineNumbers.forEach(el => { 70 + el.addEventListener("click", (event) => { 71 + event.preventDefault(); 72 + const currentLine = parseInt(el.href.split("#L")[1]); 73 + 74 + if (event.shiftKey && startLine !== null) { 75 + const endLine = currentLine; 76 + const min = Math.min(startLine, endLine); 77 + const max = Math.max(startLine, endLine); 78 + const newHash = `#L${min}-${max}`; 79 + history.pushState(null, '', newHash); 80 + } else { 81 + const newHash = `#L${currentLine}`; 82 + history.pushState(null, '', newHash); 83 + startLine = currentLine; 84 + } 85 + 86 + highlight(); 87 + }); 88 + }); 89 + </script> 90 + {{ end }}
+7 -2
appview/pages/templates/layouts/profilebase.html
··· 9 9 10 10 {{ define "content" }} 11 11 {{ template "profileTabs" . }} 12 - <section class="bg-white dark:bg-gray-800 p-6 rounded w-full dark:text-white drop-shadow-sm"> 12 + <section class="bg-white dark:bg-gray-800 px-2 py-6 md:p-6 rounded w-full dark:text-white drop-shadow-sm"> 13 13 <div class="grid grid-cols-1 md:grid-cols-11 gap-4"> 14 - <div class="md:col-span-3 order-1 md:order-1"> 14 + {{ $style := "hidden md:block md:col-span-3" }} 15 + {{ if eq $.Active "overview" }} 16 + {{ $style = "md:col-span-3" }} 17 + {{ end }} 18 + <div class="{{ $style }} order-1 order-1"> 15 19 <div class="flex flex-col gap-4"> 16 20 {{ template "user/fragments/profileCard" .Card }} 17 21 {{ block "punchcard" .Card.Punchcard }} {{ end }} 18 22 </div> 19 23 </div> 24 + 20 25 {{ block "profileContent" . }} {{ end }} 21 26 </div> 22 27 </section>
+1
appview/pages/templates/repo/blob.html
··· 78 78 {{ end }} 79 79 </div> 80 80 {{ end }} 81 + {{ template "fragments/multiline-select" }} 81 82 {{ end }}
+6 -1
appview/pages/templates/repo/fragments/shortTimeAgo.html
··· 1 1 {{ define "repo/fragments/shortTimeAgo" }} 2 - {{ template "repo/fragments/timeWrapper" (dict "Time" . "Content" (print (. | shortRelTimeFmt) " ago")) }} 2 + {{ $formatted := shortRelTimeFmt . }} 3 + {{ $content := printf "%s ago" $formatted }} 4 + {{ if eq $formatted "now" }} 5 + {{ $content = "now" }} 6 + {{ end }} 7 + {{ template "repo/fragments/timeWrapper" (dict "Time" . "Content" $content) }} 3 8 {{ end }} 4 9
+3 -2
appview/pages/templates/strings/string.html
··· 23 23 hx-boost="true" 24 24 href="/strings/{{ .String.Did }}/{{ .String.Rkey }}/edit"> 25 25 {{ i "pencil" "size-4" }} 26 - <span class="hidden md:inline">edit</span> 26 + <span class="hidden md:inline">edit</span> 27 27 {{ i "loader-circle" "w-4 h-4 animate-spin hidden group-[.htmx-request]:inline" }} 28 28 </a> 29 29 <button ··· 34 34 hx-confirm="Are you sure you want to delete the string `{{ .String.Filename }}`?" 35 35 > 36 36 {{ i "trash-2" "size-4" }} 37 - <span class="hidden md:inline">delete</span> 37 + <span class="hidden md:inline">delete</span> 38 38 {{ i "loader-circle" "w-4 h-4 animate-spin hidden group-[.htmx-request]:inline" }} 39 39 </button> 40 40 </div> ··· 80 80 <div id="blob-contents" class="whitespace-pre peer-target:bg-yellow-200 dark:peer-target:bg-yellow-900">{{ .String.Contents | escapeHtml }}</div> 81 81 {{ end }} 82 82 </div> 83 + {{ template "fragments/multiline-select" }} 83 84 </section> 84 85 {{ end }}
+33
appview/posthog/notifier.go
··· 129 129 log.Println("failed to enqueue posthog event:", err) 130 130 } 131 131 } 132 + 133 + func (n *posthogNotifier) DeleteString(ctx context.Context, did, rkey string) { 134 + err := n.client.Enqueue(posthog.Capture{ 135 + DistinctId: did, 136 + Event: "delete_string", 137 + Properties: posthog.Properties{"rkey": rkey}, 138 + }) 139 + if err != nil { 140 + log.Println("failed to enqueue posthog event:", err) 141 + } 142 + } 143 + 144 + func (n *posthogNotifier) EditString(ctx context.Context, string *db.String) { 145 + err := n.client.Enqueue(posthog.Capture{ 146 + DistinctId: string.Did.String(), 147 + Event: "edit_string", 148 + Properties: posthog.Properties{"rkey": string.Rkey}, 149 + }) 150 + if err != nil { 151 + log.Println("failed to enqueue posthog event:", err) 152 + } 153 + } 154 + 155 + func (n *posthogNotifier) CreateString(ctx context.Context, string *db.String) { 156 + err := n.client.Enqueue(posthog.Capture{ 157 + DistinctId: string.Did.String(), 158 + Event: "create_string", 159 + Properties: posthog.Properties{"rkey": string.Rkey}, 160 + }) 161 + if err != nil { 162 + log.Println("failed to enqueue posthog event:", err) 163 + } 164 + }
+8
appview/strings/strings.go
··· 12 12 "tangled.sh/tangled.sh/core/appview/config" 13 13 "tangled.sh/tangled.sh/core/appview/db" 14 14 "tangled.sh/tangled.sh/core/appview/middleware" 15 + "tangled.sh/tangled.sh/core/appview/notify" 15 16 "tangled.sh/tangled.sh/core/appview/oauth" 16 17 "tangled.sh/tangled.sh/core/appview/pages" 17 18 "tangled.sh/tangled.sh/core/appview/pages/markup" ··· 36 37 IdResolver *idresolver.Resolver 37 38 Logger *slog.Logger 38 39 Knotstream *eventconsumer.Consumer 40 + Notifier notify.Notifier 39 41 } 40 42 41 43 func (s *Strings) Router(mw *middleware.Middleware) http.Handler { ··· 284 286 return 285 287 } 286 288 289 + s.Notifier.EditString(r.Context(), &entry) 290 + 287 291 // if that went okay, redir to the string 288 292 s.Pages.HxRedirect(w, "/strings/"+user.Handle+"/"+entry.Rkey) 289 293 } ··· 358 362 return 359 363 } 360 364 365 + s.Notifier.NewString(r.Context(), &string) 366 + 361 367 // successful 362 368 s.Pages.HxRedirect(w, "/strings/"+user.Handle+"/"+string.Rkey) 363 369 } ··· 399 405 fail("Failed to delete string.", err) 400 406 return 401 407 } 408 + 409 + s.Notifier.DeleteString(r.Context(), user.Did, rkey) 402 410 403 411 s.Pages.HxRedirect(w, "/strings/"+user.Handle) 404 412 }
+44
contrib/Tiltfile
··· 1 + common_env = { 2 + "TANGLED_VM_SPINDLE_OWNER": os.getenv("TANGLED_VM_SPINDLE_OWNER", default=""), 3 + "TANGLED_VM_KNOT_OWNER": os.getenv("TANGLED_VM_KNOT_OWNER", default=""), 4 + "TANGLED_DB_PATH": os.getenv("TANGLED_DB_PATH", default="dev.db"), 5 + "TANGLED_DEV": os.getenv("TANGLED_DEV", default="true"), 6 + } 7 + 8 + nix_globs = ["nix/**", "flake.nix", "flake.lock"] 9 + 10 + local_resource( 11 + name="appview", 12 + serve_cmd="nix run .#watch-appview", 13 + serve_dir="..", 14 + deps=nix_globs, 15 + env=common_env, 16 + allow_parallel=True, 17 + ) 18 + 19 + local_resource( 20 + name="tailwind", 21 + serve_cmd="nix run .#watch-tailwind", 22 + serve_dir="..", 23 + deps=nix_globs, 24 + env=common_env, 25 + allow_parallel=True, 26 + ) 27 + 28 + local_resource( 29 + name="redis", 30 + serve_cmd="redis-server", 31 + serve_dir="..", 32 + deps=nix_globs, 33 + env=common_env, 34 + allow_parallel=True, 35 + ) 36 + 37 + local_resource( 38 + name="vm", 39 + serve_cmd="nix run --impure .#vm", 40 + serve_dir="..", 41 + deps=nix_globs, 42 + env=common_env, 43 + allow_parallel=True, 44 + )
+2 -1
flake.nix
··· 151 151 nativeBuildInputs = [ 152 152 pkgs.go 153 153 pkgs.air 154 + pkgs.tilt 154 155 pkgs.gopls 155 156 pkgs.httpie 156 157 pkgs.litecli ··· 187 188 tailwind-watcher = 188 189 pkgs.writeShellScriptBin "run" 189 190 '' 190 - ${pkgs.tailwindcss}/bin/tailwindcss -w -i input.css -o ./appview/pages/static/tw.css 191 + ${pkgs.tailwindcss}/bin/tailwindcss --watch=always -i input.css -o ./appview/pages/static/tw.css 191 192 ''; 192 193 in { 193 194 fmt = {
+2 -5
input.css
··· 228 228 } 229 229 /* LineHighlight */ 230 230 .chroma .hl { 231 - background-color: #bcc0cc; 231 + @apply bg-amber-400/30 dark:bg-amber-500/20; 232 232 } 233 + 233 234 /* LineNumbersTable */ 234 235 .chroma .lnt { 235 236 white-space: pre; ··· 864 865 text-decoration: underline; 865 866 } 866 867 } 867 - 868 - .chroma .line:has(.ln:target) { 869 - @apply bg-amber-400/30 dark:bg-amber-500/20; 870 - }