Merge feat/reskin-tangled: reskin + Tangled morphism fixes
Author: Aaron Steven White
Commit
ee055a221d369f0718f71c38bc7651728b181274Structural diff unavailable
These commits were pushed via plain git push, so no pre-parsed
schemas are available. Install git-remote-cospan and re-push via panproto:// to
see scope-level changes, breaking change detection, and semantic diffs.
brew install panproto/tap/git-remote-cospan37 files changed +1184 -745
@@ -1,33 +1,112 @@
11 @import "tailwindcss"; 2+@import url('https://fonts.googleapis.com/css2?family=DM+Sans:ital,opsz,wght@0,9..40,100..1000;1,9..40,100..1000&family=DM+Mono:wght@300;400;500&display=swap'); 23 34 @theme { 4- --color-surface-0: oklch(0.13 0.01 260); 5- --color-surface-1: oklch(0.16 0.01 260); 6- --color-surface-2: oklch(0.20 0.01 260); 7- --color-border: oklch(0.28 0.01 260); 8- --color-accent: oklch(0.65 0.15 250); 9- --color-accent-hover: oklch(0.70 0.15 250); 10- --color-text-primary: oklch(0.93 0.01 260); 11- --color-text-secondary: oklch(0.65 0.01 260); 12- --color-breaking: oklch(0.65 0.20 25); 13- --color-compatible: oklch(0.65 0.17 145); 14- --color-conflict: oklch(0.70 0.18 55); 15- --color-lens: oklch(0.65 0.15 280); 16- --font-sans: "Inter Variable", system-ui, sans-serif; 17- --font-mono: "JetBrains Mono", "Fira Code", monospace; 5+ /* ── Palette: deep indigo-black with cold precision ── */ 6+ --color-void: #07080f; 7+ --color-ground: #0c0d17; 8+ --color-surface: #12131f; 9+ --color-raised: #1a1b2a; 10+ --color-elevated: #222336; 11+ --color-line: #2a2b40; 12+ --color-line-bright: #3d3e5a; 13+ 14+ /* Text: high contrast hierarchy */ 15+ --color-ink: #e8e9f0; 16+ --color-caption: #8b8da3; 17+ --color-ghost: #4e5068; 18+ 19+ /* Accent: a single electric blue, used sparingly */ 20+ --color-focus: #5b7ff5; 21+ --color-focus-bright: #7b9aff; 22+ --color-focus-dim: #3a5ad1; 23+ 24+ /* Semantic */ 25+ --color-ok: #3dd68c; 26+ --color-err: #f55b6a; 27+ --color-warn: #f5c542; 28+ --color-info: #5bb8f5; 29+ 30+ /* Legacy aliases */ 31+ --color-breaking: #f55b6a; 32+ --color-compatible: #3dd68c; 33+ --color-conflict: #f5c542; 34+ --color-lens: #5bb8f5; 35+ --color-accent: #5b7ff5; 36+ --color-accent-hover: #7b9aff; 37+ --color-success: #3dd68c; 38+ --color-danger: #f55b6a; 39+ --color-warning: #f5c542; 40+ 41+ /* Backwards compat tokens */ 42+ --color-bg: #07080f; 43+ --color-surface-0: #0c0d17; 44+ --color-surface-1: #12131f; 45+ --color-surface-2: #1a1b2a; 46+ --color-border: #2a2b40; 47+ --color-border-hover: #3d3e5a; 48+ --color-text-primary: #e8e9f0; 49+ --color-text-secondary: #8b8da3; 50+ --color-text-muted: #4e5068; 51+ 52+ /* Typography */ 53+ --font-sans: 'DM Sans', system-ui, -apple-system, sans-serif; 54+ --font-mono: 'DM Mono', 'JetBrains Mono', 'SF Mono', monospace; 55+} 56+ 57+/* ── Base ── */ 58+html { 59+ -webkit-font-smoothing: antialiased; 60+ -moz-osx-font-smoothing: grayscale; 61+ font-feature-settings: 'ss01', 'ss02', 'cv01'; 1862 } 1963 2064 body { 21- background-color: var(--color-surface-0); 22- color: var(--color-text-primary); 65+ background: var(--color-void); 66+ color: var(--color-ink); 2367 font-family: var(--font-sans); 68+ font-size: 14px; 69+ line-height: 1.65; 70+} 71+ 72+::selection { 73+ background: oklch(0.45 0.12 260 / 0.4); 2474 } 2575 26-/* Hide scrollbar for horizontal scroll areas while keeping scroll behavior */ 27-.scrollbar-none { 28- -ms-overflow-style: none; 29- scrollbar-width: none; 76+/* ── Grid texture: the mathematical substrate ── */ 77+.grid-texture { 78+ background-image: 79+ linear-gradient(to right, oklch(0.20 0.02 270 / 0.08) 1px, transparent 1px), 80+ linear-gradient(to bottom, oklch(0.20 0.02 270 / 0.08) 1px, transparent 1px); 81+ background-size: 40px 40px; 3082 } 31-.scrollbar-none::-webkit-scrollbar { 32- display: none; 83+ 84+.dot-grid { 85+ background-image: radial-gradient(oklch(0.35 0.02 270 / 0.25) 1px, transparent 1px); 86+ background-size: 20px 20px; 3387 } 88+ 89+/* ── Glow effects ── */ 90+.glow-focus { 91+ box-shadow: 0 0 0 1px var(--color-focus-dim), 0 0 20px oklch(0.45 0.12 260 / 0.15); 92+} 93+ 94+/* ── Scrollbar ── */ 95+.scrollbar-none { -ms-overflow-style: none; scrollbar-width: none; } 96+.scrollbar-none::-webkit-scrollbar { display: none; } 97+ 98+::-webkit-scrollbar { width: 6px; } 99+::-webkit-scrollbar-track { background: transparent; } 100+::-webkit-scrollbar-thumb { background: var(--color-line); border-radius: 3px; } 101+::-webkit-scrollbar-thumb:hover { background: var(--color-line-bright); } 102+ 103+/* ── Focus ring ── */ 104+:focus-visible { 105+ outline: 1.5px solid var(--color-focus); 106+ outline-offset: 2px; 107+} 108+ 109+/* ── Graph paper (legacy util) ── */ 110+.graph-paper { background-image: radial-gradient(circle, oklch(0.30 0.008 270) 0.5px, transparent 0.5px); background-size: 24px 24px; } 111+.noise-overlay { position: relative; } 112+.noise-overlay::after { content: ''; position: absolute; inset: 0; opacity: 0.025; pointer-events: none; background: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.8' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E"); }
@@ -1,5 +1,12 @@
11 import { xrpcQuery } from './client.js'; 22 3+export interface AlgebraicChecks { 4+ gatTypeCheck: string | null; 5+ equationVerification: string | null; 6+ lensLawCheck: string | null; 7+ breakingChangeCheck: string | null; 8+} 9+ 310 export interface RefUpdate { 411 rkey: string; 512 repo: string;
@@ -9,8 +16,10 @@ export interface RefUpdate {
916 protocol: string; 1017 commitCount: number; 1118 migrationId: string | null; 19+ lensId: string | null; 1220 lensQuality: number | null; 1321 breakingChangeCount: number; 22+ algebraicChecks: AlgebraicChecks | null; 1423 committerDid: string; 1524 committerHandle: string | null; 1625 createdAt: string;
@@ -6,8 +6,16 @@ export interface Repo {
66 description: string | null; 77 protocol: string; 88 starCount: number; 9+ forkCount: number; 910 openIssueCount: number; 1011 openMrCount: number; 12+ source: string; 13+ sourceUri: string | null; 14+ sourceRepo: string | null; 15+ nodeDid: string; 16+ nodeUrl: string; 17+ defaultBranch: string; 18+ visibility: string; 1119 createdAt: string; 1220 } 1321
@@ -23,6 +31,7 @@ interface RawRepoListResponse {
2331 2432 export async function listRepos(params?: { 2533 did?: string; 34+ source?: string; 2635 limit?: number; 2736 cursor?: string; 2837 }): Promise<RepoListResponse> {
@@ -9,19 +9,18 @@
99 1010 <a 1111 href="{basePath}/issues/{issue.rkey}" 12- class="block rounded-lg border border-border bg-surface-1 p-4 transition-colors hover:border-accent" 12+ class="block rounded-lg border border-border bg-surface-1 p-4 transition-all hover:border-border-hover" 1313 > 1414 <div class="flex items-start gap-3"> 1515 <div class="mt-0.5"> 1616 <StateBadge state={issue.state} /> 1717 </div> 1818 <div class="min-w-0 flex-1"> 19- <h3 class="font-medium text-text-primary">{issue.title}</h3> 20- <div class="mt-1.5 flex flex-wrap items-center gap-x-3 gap-y-1 text-xs text-text-secondary"> 19+ <h3 class="font-semibold text-text-primary">{issue.title}</h3> 20+ <div class="mt-1.5 flex flex-wrap items-center gap-x-3 gap-y-1 text-xs text-text-muted"> 2121 <span>#{issue.rkey}</span> 22- <span>opened {timeAgo(issue.createdAt)}</span> 2322 {#if issue.creatorHandle} 24- <span>by {issue.creatorHandle}</span> 23+ <span>opened by {issue.creatorHandle}</span> 2524 {/if} 2625 {#if issue.commentCount > 0} 2726 <span class="flex items-center gap-1">
@@ -31,6 +30,7 @@
3130 {issue.commentCount} 3231 </span> 3332 {/if} 33+ <span>{timeAgo(issue.createdAt)}</span> 3434 </div> 3535 {#if issue.labels.length > 0} 3636 <div class="mt-2 flex flex-wrap gap-1">
@@ -1,228 +1,67 @@
11 <script lang="ts"> 2- import { goto } from '$app/navigation'; 32 import { page } from '$app/stores'; 4- import { debounce } from '$lib/utils/debounce.js'; 53 import UserMenu from '$lib/components/auth/UserMenu.svelte'; 6- import NotificationBell from '$lib/components/shared/NotificationBell.svelte'; 74 85 let { user }: { user?: { authenticated: boolean; did: string; handle: string; displayName?: string; avatar?: string } | null } = $props(); 96 10- let searchValue = $state(''); 11- let mobileSearchValue = $state(''); 12- let searchInputRef = $state<HTMLInputElement | null>(null); 13- let mobileMenuOpen = $state(false); 7+ let currentPath = $derived($page.url.pathname); 148 15- // Close mobile menu on navigation 16- $effect(() => { 17- $page.url.pathname; 18- mobileMenuOpen = false; 19- }); 9+ const navLinks = [ 10+ { href: '/', label: 'Explore' }, 11+ { href: '/feed', label: 'Feed' }, 12+ { href: '/search', label: 'Search' }, 13+ ] as const; 2014 21- const submitSearch = debounce((q: string) => { 22- if (q.trim()) { 23- goto(`/search?q=${encodeURIComponent(q.trim())}`); 24- } 25- }, 300); 26- 27- function handleSearchKeydown(event: KeyboardEvent) { 28- if (event.key === 'Enter' && searchValue.trim()) { 29- goto(`/search?q=${encodeURIComponent(searchValue.trim())}`); 30- } 31- } 32- 33- function handleMobileSearchKeydown(event: KeyboardEvent) { 34- if (event.key === 'Enter' && mobileSearchValue.trim()) { 35- goto(`/search?q=${encodeURIComponent(mobileSearchValue.trim())}`); 36- mobileMenuOpen = false; 37- } 38- } 39- 40- function handleGlobalKeydown(event: KeyboardEvent) { 41- const target = event.target as HTMLElement; 42- if ( 43- target.tagName === 'INPUT' || 44- target.tagName === 'TEXTAREA' || 45- target.isContentEditable 46- ) { 47- return; 48- } 49- 50- if (event.key === '/') { 51- event.preventDefault(); 52- searchInputRef?.focus(); 53- } 54- 55- if (event.key === 'Escape' && mobileMenuOpen) { 56- mobileMenuOpen = false; 57- } 58- } 59- 60- function toggleMobileMenu() { 61- mobileMenuOpen = !mobileMenuOpen; 15+ function isActive(href: string): boolean { 16+ if (href === '/') return currentPath === '/'; 17+ return currentPath.startsWith(href); 6218 } 6319 </script> 6420 65-<svelte:window onkeydown={handleGlobalKeydown} /> 66- 67-<header class="border-b border-border bg-surface-1"> 68- <nav class="mx-auto flex h-12 max-w-6xl items-center justify-between px-4"> 69- <div class="flex items-center gap-6"> 70- <a href="/" class="flex items-center gap-2 text-lg font-semibold tracking-tight text-text-primary"> 71- <img src="/logo-dark.svg" alt="" class="h-6 w-6" /> 72- <span class="hidden xs:inline">Cospan</span> 73- </a> 74- <div class="hidden items-center gap-4 md:flex"> 75- <a href="/" class="text-sm text-text-secondary hover:text-text-primary transition-colors"> 76- Explore 77- </a> 78- <a href="/feed" class="text-sm text-text-secondary hover:text-text-primary transition-colors"> 79- Feed 80- </a> 81- <a href="/orgs" class="text-sm text-text-secondary hover:text-text-primary transition-colors"> 82- Orgs 83- </a> 84- <a href="/search" class="text-sm text-text-secondary hover:text-text-primary transition-colors"> 85- Search 21+<header class="sticky top-0 z-50 border-b border-line/60 bg-void/80 backdrop-blur-xl"> 22+ <nav class="mx-auto flex h-14 max-w-[1200px] items-center justify-between px-6"> 23+ <!-- Wordmark --> 24+ <a href="/" class="group flex items-center gap-2 font-mono text-[15px] font-medium tracking-tight text-ink"> 25+ <!-- Cospan mark: three vertices, the apex brighter --> 26+ <svg class="h-[18px] w-[18px]" viewBox="0 0 18 18" fill="none"> 27+ <circle cx="3" cy="14" r="1.8" fill="currentColor" opacity="0.3"/> 28+ <circle cx="9" cy="3" r="2" fill="currentColor" opacity="0.7"/> 29+ <circle cx="15" cy="14" r="1.8" fill="currentColor" opacity="0.3"/> 30+ <line x1="3" y1="14" x2="9" y2="3" stroke="currentColor" stroke-width="0.8" opacity="0.2"/> 31+ <line x1="15" y1="14" x2="9" y2="3" stroke="currentColor" stroke-width="0.8" opacity="0.2"/> 32+ </svg> 33+ cospan 34+ </a> 35+ 36+ <!-- Nav --> 37+ <div class="flex items-center gap-1"> 38+ {#each navLinks as link} 39+ <a 40+ href={link.href} 41+ class="relative rounded-md px-3.5 py-1.5 text-[13px] font-medium transition-all duration-150 42+ {isActive(link.href) 43+ ? 'bg-raised text-ink' 44+ : 'text-ghost hover:text-caption hover:bg-surface'}" 45+ > 46+ {link.label} 8647 </a> 87- </div> 48+ {/each} 8849 </div> 89- <div class="flex items-center gap-3"> 90- <!-- Desktop search --> 91- <div class="relative hidden md:block"> 92- <svg 93- class="pointer-events-none absolute left-2.5 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-text-secondary" 94- fill="none" 95- viewBox="0 0 24 24" 96- stroke="currentColor" 97- stroke-width="2" 98- > 99- <path 100- stroke-linecap="round" 101- stroke-linejoin="round" 102- d="M21 21l-5.197-5.197m0 0A7.5 7.5 0 105.196 5.196a7.5 7.5 0 0010.607 10.607z" 103- /> 104- </svg> 105- <input 106- bind:this={searchInputRef} 107- type="text" 108- bind:value={searchValue} 109- oninput={() => submitSearch(searchValue)} 110- onkeydown={handleSearchKeydown} 111- placeholder="Search... (/)" 112- class="w-48 rounded-md border border-border bg-surface-0 py-1 pl-8 pr-3 text-xs text-text-primary placeholder:text-text-secondary focus:border-accent focus:outline-none" 113- /> 114- </div> 115- <NotificationBell /> 50+ 51+ <!-- Right --> 52+ <div class="flex items-center gap-2"> 11653 {#if user?.authenticated} 11754 <a 11855 href="/new" 119- class="hidden rounded-md border border-border bg-surface-1 p-1.5 text-text-secondary transition-colors hover:border-accent hover:text-text-primary sm:block" 120- title="New repository" 56+ class="flex items-center gap-1.5 rounded-md border border-line px-3 py-1.5 text-[12px] font-medium text-caption transition-all hover:border-line-bright hover:text-ink" 12157 > 122- <svg class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"> 58+ <svg class="h-3.5 w-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5"> 12359 <path stroke-linecap="round" stroke-linejoin="round" d="M12 4.5v15m7.5-7.5h-15" /> 12460 </svg> 61+ New 12562 </a> 12663 {/if} 127- <div class="hidden sm:block"> 128- <UserMenu {user} /> 129- </div> 130- 131- <!-- Mobile hamburger --> 132- <button 133- onclick={toggleMobileMenu} 134- class="rounded-md p-1.5 text-text-secondary transition-colors hover:bg-surface-2 hover:text-text-primary md:hidden" 135- aria-label="Toggle menu" 136- > 137- {#if mobileMenuOpen} 138- <svg class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"> 139- <path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" /> 140- </svg> 141- {:else} 142- <svg class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"> 143- <path stroke-linecap="round" stroke-linejoin="round" d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5" /> 144- </svg> 145- {/if} 146- </button> 64+ <UserMenu {user} /> 14765 </div> 14866 </nav> 149- 150- <!-- Mobile menu --> 151- {#if mobileMenuOpen} 152- <div class="border-t border-border bg-surface-1 px-4 py-3 md:hidden"> 153- <!-- Mobile search --> 154- <div class="mb-3"> 155- <div class="relative"> 156- <svg 157- class="pointer-events-none absolute left-2.5 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-text-secondary" 158- fill="none" 159- viewBox="0 0 24 24" 160- stroke="currentColor" 161- stroke-width="2" 162- > 163- <path 164- stroke-linecap="round" 165- stroke-linejoin="round" 166- d="M21 21l-5.197-5.197m0 0A7.5 7.5 0 105.196 5.196a7.5 7.5 0 0010.607 10.607z" 167- /> 168- </svg> 169- <input 170- type="text" 171- bind:value={mobileSearchValue} 172- onkeydown={handleMobileSearchKeydown} 173- placeholder="Search repositories..." 174- class="w-full rounded-md border border-border bg-surface-0 py-2 pl-8 pr-3 text-sm text-text-primary placeholder:text-text-secondary focus:border-accent focus:outline-none" 175- /> 176- </div> 177- </div> 178- 179- <!-- Mobile nav links --> 180- <div class="space-y-1"> 181- <a href="/" class="block rounded-md px-3 py-2 text-sm text-text-secondary transition-colors hover:bg-surface-2 hover:text-text-primary"> 182- Explore 183- </a> 184- <a href="/feed" class="block rounded-md px-3 py-2 text-sm text-text-secondary transition-colors hover:bg-surface-2 hover:text-text-primary"> 185- Feed 186- </a> 187- <a href="/orgs" class="block rounded-md px-3 py-2 text-sm text-text-secondary transition-colors hover:bg-surface-2 hover:text-text-primary"> 188- Orgs 189- </a> 190- <a href="/search" class="block rounded-md px-3 py-2 text-sm text-text-secondary transition-colors hover:bg-surface-2 hover:text-text-primary"> 191- Search 192- </a> 193- </div> 194- 195- {#if user?.authenticated} 196- <div class="mt-3 border-t border-border pt-3 space-y-1"> 197- <a 198- href="/new" 199- class="flex items-center gap-2 rounded-md px-3 py-2 text-sm text-text-secondary transition-colors hover:bg-surface-2 hover:text-text-primary" 200- > 201- <svg class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"> 202- <path stroke-linecap="round" stroke-linejoin="round" d="M12 4.5v15m7.5-7.5h-15" /> 203- </svg> 204- New repository 205- </a> 206- <a 207- href="/{user.did}" 208- class="block rounded-md px-3 py-2 text-sm text-text-secondary transition-colors hover:bg-surface-2 hover:text-text-primary" 209- > 210- Your profile 211- </a> 212- <a 213- href="/settings" 214- class="block rounded-md px-3 py-2 text-sm text-text-secondary transition-colors hover:bg-surface-2 hover:text-text-primary" 215- > 216- Settings 217- </a> 218- </div> 219- {:else} 220- <div class="mt-3 border-t border-border pt-3"> 221- <div class="sm:hidden"> 222- <UserMenu {user} /> 223- </div> 224- </div> 225- {/if} 226- </div> 227- {/if} 22867 </header>