betetr home pager
940 unmodified linesExpand all9419429439449459469459469479489499509519529519529539549579559569579589599609619629639649659669679669679689699709719729739749759769739749759779789799809819829839819829839849859849859869879889899909919929939949959969979989991000100110029979981003100410051006100710081009101010061007100810091010101110111012101310141015101610171018187 unmodified linesExpand all12061207120812091207121012111212121312141215121612171218121912201212122112221223122412253 unmodified lines12291230123112321224123312341235123612371238123912401233124112421243124425 unmodified lines127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981270129913001301130235 unmodified lines13381339134013411314134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701322132313711372137313741328137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162787 unmodified lines17151716171717181719172017211722172317241725172617271728172913 unmodified lines17431744174517461747174817491451175017511752175317541390 unmodified linesExpand all3145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436940 unmodified linesExpand all}
.untangled-home-workspace { display: grid; grid-template-columns: 14.5rem minmax(0, 1fr) 16rem; gap: 1rem; grid-template-columns: minmax(0, 1fr) 22rem; gap: 1.5rem; align-items: start;}
.untangled-home-side { position: sticky; top: 0.75rem; display: flex; min-width: 0; flex-direction: column; gap: 0.75rem; padding: 0.75rem 0; padding: 0 0 0.75rem; position: sticky; top: 0;}
.untangled-home-side-left { border-right: 1px solid rgb(229 231 235); padding-right: 1rem;}
.untangled-home-side-right { border-left: 1px solid rgb(229 231 235); padding-left: 1rem; padding-left: 1.5rem;}
.untangled-home-section-header { position: sticky; top: 0; z-index: 10; background: rgb(241 245 249); padding-top: 0.5rem; display: flex; align-items: center; gap: 0.5rem; padding-bottom: 0.5rem; border-bottom: 1px solid rgb(229 231 235); gap: 0.75rem; padding-bottom: 1rem; color: inherit;}
.untangled-home-section-header h2 { margin: 0; font-size: 0.8125rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.03em; color: rgb(107 114 128); font-size: 1.25rem; font-weight: 750; color: rgb(17 24 39);}
.untangled-home-feed { min-width: 0;}
.untangled-home-feed-header { position: sticky; top: 0; z-index: 10; background: rgb(241 245 249); padding-top: 0.5rem; display: flex; align-items: center; justify-content: space-between; gap: 1rem; padding-bottom: 0.5rem; border-bottom: 1px solid rgb(229 231 235); padding-bottom: 1rem; margin-bottom: 0.5rem;}
.untangled-home-feed-title { margin: 0; display: flex; align-items: center; gap: 0.5rem; font-size: 0.8125rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.03em; color: rgb(107 114 128); gap: 0.75rem; font-size: 1.25rem; font-weight: 750; color: rgb(17 24 39);}
.untangled-home-live-pill { display: inline-flex;187 unmodified linesExpand all font-size: 0.875rem; line-height: 1.5;}
.untangled-home-compact-list,.untangled-home-compact-list { display: flex; min-width: 0; flex-direction: column; gap: 0.5rem;}
.untangled-home-following-list { display: flex; min-width: 0; flex-direction: column; gap: 0; gap: 0.75rem;}
/* upstream repoCard style: flat link with icon + name, description below, time at right */.untangled-home-repo-row {3 unmodified lines gap: 0.125rem; border: 1px solid rgb(229 231 235); border-radius: 0.125rem; padding: 0.625rem 0.75rem; margin-bottom: 0.25rem; color: inherit; text-decoration: none; transition: background-color 150ms; background: rgb(255 255 255);}
.untangled-home-repo-row:last-child { border-bottom-width: 1px; margin-bottom: 0;}
.untangled-home-repo-row:hover { background: rgb(249 250 251);25 unmodified lines white-space: nowrap; display: block;}
.untangled-home-repo-row-footer { display: flex; align-items: center; gap: 0.75rem; margin-top: 0.125rem;}
.untangled-home-repo-row-lang { display: inline-flex; align-items: center; gap: 0.25rem; font-size: 0.75rem; color: rgb(75 85 99);}
.untangled-home-repo-row-lang-dot { display: inline-block; width: 0.5rem; height: 0.5rem; border-radius: 9999px;}
.untangled-home-repo-row-time { color: rgb(107 114 128); font-size: 0.75rem; margin-top: 0.125rem;}
.untangled-home-following-row { display: flex;35 unmodified lines.untangled-home-below { display: flex; flex-direction: column; gap: 1.5rem; padding-top: 0.5rem; padding-top: 2rem; padding-bottom: 4rem; width: 100vw; margin-left: calc(50% - 50vw); margin-right: calc(50% - 50vw); padding-left: 2rem; padding-right: 2rem; background: rgb(248 250 252); box-sizing: border-box;}
@media (prefers-color-scheme: dark) { .untangled-home-below { background: rgb(15 23 42); }}
@media (min-width: 1280px) { .untangled-home-below { padding-left: 4rem; padding-right: 4rem; }}
.untangled-home-wide-section { display: flex; flex-direction: column; gap: 0.75rem; min-width: 0; border-top: 1px solid rgb(209 213 219); padding-top: 0.75rem;}
.untangled-home-lower-grid { display: grid; grid-template-columns: minmax(0, 1.2fr) minmax(18rem, 0.8fr); grid-template-columns: 1fr; gap: 1.5rem; align-items: start;}
.untangled-home-promos-column { order: 3;}
.untangled-home-lower-left { order: 1;}
.untangled-home-lower-right { order: 2;}
@media (min-width: 768px) { .untangled-home-lower-grid { grid-template-columns: minmax(0, 2fr) minmax(0, 1fr); }
.untangled-home-promos-column { grid-column: span 2; display: flex; flex-direction: column; gap: 1rem; }}
@media (min-width: 1024px) { .untangled-home-lower-grid { grid-template-columns: 18rem minmax(0, 1fr) 20rem; gap: 2rem; }
.untangled-home-promos-column { grid-column: span 1; display: flex; flex-direction: column; gap: 1.25rem; order: 0; }
.untangled-home-lower-left, .untangled-home-lower-right { order: 0; }}
@media (min-width: 1280px) { .untangled-home-lower-grid { grid-template-columns: 22rem minmax(0, 1fr) 24rem; gap: 3.5rem; }}
.untangled-home-announcements-grid { display: grid; grid-template-columns: 1fr; gap: 1rem;}
@media (min-width: 768px) and (max-width: 1023px) { .untangled-home-announcements-grid { grid-template-columns: repeat(2, 1fr); }}
/* Vouch Card styles */.untangled-home-vouch-card { display: flex; flex-direction: column; border: 1px solid rgb(229 231 235); border-radius: 0.25rem; background: rgb(255 255 255); padding: 1.25rem; box-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05); gap: 1rem;}
@media (prefers-color-scheme: dark) { .untangled-home-vouch-card { border-color: rgb(55 65 81); background: rgb(31 41 55); }}
.untangled-home-vouch-header { display: flex; align-items: center; justify-content: space-between;}
.untangled-home-vouch-title { margin: 0; display: flex; align-items: center; gap: 0.5rem; font-size: 1rem; font-weight: 700; color: rgb(17 24 39);}
@media (prefers-color-scheme: dark) { .untangled-home-vouch-title { color: rgb(243 244 246); }}
.untangled-home-vouch-list { display: flex; flex-direction: column; gap: 1rem;}
.untangled-home-vouch-item { display: flex; align-items: center; gap: 0.75rem;}
.untangled-home-vouch-info { display: flex; flex-direction: column; min-width: 0; flex: 1;}
.untangled-home-vouch-name { font-size: 0.875rem; font-weight: 600; color: rgb(17 24 39); text-decoration: none;}
.untangled-home-vouch-name:hover { text-decoration: underline;}
@media (prefers-color-scheme: dark) { .untangled-home-vouch-name { color: rgb(243 244 246); }}
.untangled-home-vouch-reason { font-size: 0.75rem; color: rgb(107 114 128); overflow: hidden; text-overflow: ellipsis; white-space: nowrap;}
@media (prefers-color-scheme: dark) { .untangled-home-vouch-reason { color: rgb(156 163 175); }}
.untangled-home-vouch-action-btn { display: inline-flex; align-items: center; justify-content: center; gap: 0.375rem; font-size: 0.75rem; font-weight: 600; padding: 0.375rem 0.75rem; border-radius: 0.125rem; border: 1px solid rgb(209 213 219); background: rgb(255 255 255); color: rgb(55 65 81); cursor: pointer; transition: all 150ms;}
.untangled-home-vouch-action-btn:hover { background: rgb(249 250 251); border-color: rgb(156 163 175);}
.untangled-home-vouch-action-btn.is-vouched { background: rgb(220 252 231); border-color: rgb(187 247 208); color: rgb(21 128 61);}
.untangled-home-vouch-action-btn.is-vouched:hover { background: rgb(240 253 244);}
@media (prefers-color-scheme: dark) { .untangled-home-vouch-action-btn { border-color: rgb(75 85 99); background: rgb(31 41 55); color: rgb(209 213 219); }
.untangled-home-vouch-action-btn:hover { background: rgb(55 65 81); border-color: rgb(156 163 175); }
.untangled-home-vouch-action-btn.is-vouched { background: rgb(20 83 45 / 0.4); border-color: rgb(20 83 45); color: rgb(74 222 128); }
.untangled-home-vouch-action-btn.is-vouched:hover { background: rgb(20 83 45 / 0.6); }}
.untangled-home-scroll-container { display: flex; flex-direction: column; gap: 0.75rem; max-height: 38rem; overflow-y: auto; padding-right: 0.5rem; padding-bottom: 3rem; transition: mask-image 0.2s, -webkit-mask-image 0.2s;}
/* 1. At the top (only fade at bottom) */.untangled-home-scroll-container.is-at-top { mask-image: linear-gradient(to bottom, black 90%, transparent 100%); -webkit-mask-image: linear-gradient(to bottom, black 90%, transparent 100%);}
/* 2. At the bottom (only fade at top) */.untangled-home-scroll-container.is-at-bottom { mask-image: linear-gradient(to top, black 90%, transparent 100%); -webkit-mask-image: linear-gradient(to top, black 90%, transparent 100%);}
/* 3. In the middle (fade at both top and bottom) */.untangled-home-scroll-container.is-in-middle { mask-image: linear-gradient(to bottom, transparent 0%, black 10%, black 90%, transparent 100%); -webkit-mask-image: linear-gradient(to bottom, transparent 0%, black 10%, black 90%, transparent 100%);}
/* 4. Not scrollable (no fade at all) */.untangled-home-scroll-container.is-not-scrollable { mask-image: none; -webkit-mask-image: none;}
.untangled-home-recent-strip,.untangled-home-sample-list { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr));87 unmodified lines .untangled-home-activity-card-body:hover { background: rgb(31 41 55); }
.untangled-home-section-header, .untangled-home-feed-header { background: rgb(17 24 39); }
.untangled-home-section-header h2, .untangled-home-feed-title, .untangled-home-activity-repo-label { color: rgb(243 244 246); }
13 unmodified lines color: rgb(243 244 246); }
.untangled-home-repo-row-desc, .untangled-home-repo-row-lang, .untangled-home-repo-row-time, .untangled-home-following-row small { color: rgb(107 114 128); color: rgb(156 163 175); }
.untangled-home-brand p, .untangled-home-activity-body,1390 unmodified linesExpand all .untangled-file-tree-pane { border-right: 0; }}
/* Lower grid section header background overrides *//* Lower grid section header background overrides */.untangled-home-below .untangled-home-section-header { background: rgb(248 250 252) !important; z-index: 15 !important;}
@media (prefers-color-scheme: dark) { .untangled-home-below .untangled-home-section-header { background: rgb(15 23 42) !important; z-index: 15 !important; }}
/* Homepage themed announcement cards */.untangled-home-promo-card-indigo { border: 1px solid rgb(224 231 255); background: linear-gradient(to bottom, #ffffff, rgb(240 244 255)); color: rgb(67 56 202);}.untangled-home-promo-card-indigo h4 { color: rgb(55 48 163);}.untangled-home-promo-card-indigo a { color: rgb(67 56 202);}.untangled-home-promo-card-indigo a:hover { color: rgb(49 46 129);}
.untangled-home-promo-card-green { border: 1px solid rgb(209 250 229); background: linear-gradient(to bottom, #ffffff, rgb(236 253 245)); color: rgb(4 120 87);}.untangled-home-promo-card-green h4 { color: rgb(6 95 70);}.untangled-home-promo-card-green a { color: rgb(4 120 87);}.untangled-home-promo-card-green a:hover { color: rgb(6 78 59);}
.untangled-home-promo-card-purple { border: 1px solid rgb(243 232 255); background: linear-gradient(to bottom, #ffffff, rgb(250 245 255)); color: rgb(126 34 206);}.untangled-home-promo-card-purple h4 { color: rgb(107 33 168);}.untangled-home-promo-card-purple a { color: rgb(126 34 206);}.untangled-home-promo-card-purple a:hover { color: rgb(88 28 135);}
.untangled-home-promo-card-blue { border: 1px solid rgb(219 234 254); background: linear-gradient(to bottom, #ffffff, rgb(239 246 255)); color: rgb(37 99 235);}.untangled-home-promo-card-blue h4 { color: rgb(26 86 219);}.untangled-home-promo-card-blue a { color: rgb(37 99 235);}.untangled-home-promo-card-blue a:hover { color: rgb(30 58 138);}
.untangled-home-promo-card-amber { border: 1px solid rgb(254 243 199); background: linear-gradient(to bottom, #ffffff, rgb(254 252 232)); color: rgb(217 119 6);}.untangled-home-promo-card-amber h4 { color: rgb(180 83 9);}.untangled-home-promo-card-amber a { color: rgb(217 119 6);}.untangled-home-promo-card-amber a:hover { color: rgb(120 53 4);}
.untangled-home-promo-card-rose { border: 1px solid rgb(254 226 226); background: linear-gradient(to bottom, #ffffff, rgb(255 241 241)); color: rgb(225 29 72);}.untangled-home-promo-card-rose h4 { color: rgb(190 24 74);}.untangled-home-promo-card-rose a { color: rgb(225 29 72);}.untangled-home-promo-card-rose a:hover { color: rgb(159 18 57);}
.untangled-home-promo-card-orange { border: 1px solid rgb(254 237 222); background: linear-gradient(to bottom, #ffffff, rgb(255 247 237)); color: rgb(234 88 12);}.untangled-home-promo-card-orange h4 { color: rgb(194 65 12);}.untangled-home-promo-card-orange a { color: rgb(234 88 12);}.untangled-home-promo-card-orange a:hover { color: rgb(154 52 18);}
.untangled-home-promo-card-teal { border: 1px solid rgb(204 251 241); background: linear-gradient(to bottom, #ffffff, rgb(240 253 250)); color: rgb(13 148 136);}.untangled-home-promo-card-teal h4 { color: rgb(15 118 110);}.untangled-home-promo-card-teal a { color: rgb(13 148 136);}.untangled-home-promo-card-teal a:hover { color: rgb(17 94 89);}
.untangled-home-promo-card-sky { border: 1px solid rgb(224 242 254); background: linear-gradient(to bottom, #ffffff, rgb(240 249 255)); color: rgb(2 132 199);}.untangled-home-promo-card-sky h4 { color: rgb(3 105 161);}.untangled-home-promo-card-sky a { color: rgb(2 132 199);}.untangled-home-promo-card-sky a:hover { color: rgb(7 89 133);}
.untangled-home-promo-card-pink { border: 1px solid rgb(252 231 243); background: linear-gradient(to bottom, #ffffff, rgb(253 242 248)); color: rgb(219 39 119);}.untangled-home-promo-card-pink h4 { color: rgb(190 24 93);}.untangled-home-promo-card-pink a { color: rgb(219 39 119);}.untangled-home-promo-card-pink a:hover { color: rgb(157 23 77);}
/* Dark mode overrides */@media (prefers-color-scheme: dark) { .untangled-home-promo-card-indigo { border-color: rgb(67 56 202 / 0.4); background: linear-gradient(to bottom, rgb(30 27 75 / 0.3), rgb(30 27 75 / 0.5)); color: rgb(165 180 252); } .untangled-home-promo-card-indigo h4 { color: rgb(199 210 254); } .untangled-home-promo-card-indigo a { color: rgb(165 180 252); }
.untangled-home-promo-card-green { border-color: rgb(6 95 70 / 0.4); background: linear-gradient(to bottom, rgb(6 78 59 / 0.2), rgb(6 78 59 / 0.4)); color: rgb(110 231 183); } .untangled-home-promo-card-green h4 { color: rgb(167 243 208); } .untangled-home-promo-card-green a { color: rgb(110 231 183); }
.untangled-home-promo-card-purple { border-color: rgb(126 34 206 / 0.4); background: linear-gradient(to bottom, rgb(88 28 135 / 0.2), rgb(88 28 135 / 0.4)); color: rgb(216 180 254); } .untangled-home-promo-card-purple h4 { color: rgb(233 213 255); } .untangled-home-promo-card-purple a { color: rgb(216 180 254); }
.untangled-home-promo-card-blue { border-color: rgb(37 99 235 / 0.4); background: linear-gradient(to bottom, rgb(30 58 138 / 0.2), rgb(30 58 138 / 0.4)); color: rgb(147 197 253); } .untangled-home-promo-card-blue h4 { color: rgb(191 219 254); } .untangled-home-promo-card-blue a { color: rgb(147 197 253); }
.untangled-home-promo-card-amber { border-color: rgb(217 119 6 / 0.4); background: linear-gradient(to bottom, rgb(120 53 4 / 0.2), rgb(120 53 4 / 0.4)); color: rgb(253 230 138); } .untangled-home-promo-card-amber h4 { color: rgb(254 243 199); } .untangled-home-promo-card-amber a { color: rgb(253 230 138); }
.untangled-home-promo-card-rose { border-color: rgb(225 29 72 / 0.4); background: linear-gradient(to bottom, rgb(136 19 55 / 0.2), rgb(136 19 55 / 0.4)); color: rgb(244 63 94); } .untangled-home-promo-card-rose h4 { color: rgb(251 113 133); } .untangled-home-promo-card-rose a { color: rgb(244 63 94); }
.untangled-home-promo-card-orange { border-color: rgb(234 88 12 / 0.4); background: linear-gradient(to bottom, rgb(124 45 18 / 0.2), rgb(124 45 18 / 0.4)); color: rgb(251 146 60); } .untangled-home-promo-card-orange h4 { color: rgb(253 186 116); } .untangled-home-promo-card-orange a { color: rgb(251 146 60); }
.untangled-home-promo-card-teal { border-color: rgb(13 148 136 / 0.4); background: linear-gradient(to bottom, rgb(17 94 89 / 0.2), rgb(17 94 89 / 0.4)); color: rgb(45 212 191); } .untangled-home-promo-card-teal h4 { color: rgb(94 234 212); } .untangled-home-promo-card-teal a { color: rgb(45 212 191); }
.untangled-home-promo-card-sky { border-color: rgb(2 132 199 / 0.4); background: linear-gradient(to bottom, rgb(7 89 133 / 0.2), rgb(7 89 133 / 0.4)); color: rgb(56 189 248); } .untangled-home-promo-card-sky h4 { color: rgb(125 211 252); } .untangled-home-promo-card-sky a { color: rgb(56 189 248); }
.untangled-home-promo-card-pink { border-color: rgb(219 39 119 / 0.4); background: linear-gradient(to bottom, rgb(157 23 77 / 0.2), rgb(157 23 77 / 0.4)); color: rgb(244 114 182); } .untangled-home-promo-card-pink h4 { color: rgb(247 178 208); } .untangled-home-promo-card-pink a { color: rgb(244 114 182); }}17 unmodified lines18192021222324252627282930312732333435361568 unmodified linesExpand all1605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517 unmodified lines PanelRightOpen, Square, SquareCheckBig, UnfoldVertical, BookMarked, GitFork, Star, CircleDot, GitPullRequest,} from 'lucide-solid';import { marked } from 'marked';import { A } from '@solidjs/router';import { For, Show, createMemo, createSignal, onCleanup, onMount, type Component, type JSX } from 'solid-js';import type { RepoContext, TreeEntry } from '../lib/api/repos';import { formatRelativeTime } from '../lib/repo-utils';import { formatRelativeTime, languageColor } from '../lib/repo-utils';import { getTangledAppviewService } from '../lib/settings';import { Avatar, SkeletonBlock, buttonStyles, cardStyles } from './common';
marked.setOptions({1568 unmodified linesExpand all </span> </section> );};
export interface RepoCardProps { owner: string; name: string; description?: string; isFork?: boolean; language?: string; stars?: number; forks?: number; openIssues?: number; openPRs?: number; compact?: boolean; href?: string; noBorder?: boolean;}
export const RepoCard: Component<RepoCardProps> = (props) => { const linkHref = () => props.href || `/${props.owner}/${props.name}`; const langColor = () => props.language ? languageColor(props.language) : undefined;
return ( <div class={clsx( 'flex flex-col gap-1 bg-white dark:bg-gray-800 drop-shadow-sm', props.compact ? 'p-3 md:p-4' : 'p-4 md:p-6', !props.noBorder && 'border border-gray-200 dark:border-gray-700 rounded-sm', !props.compact && 'min-h-[8rem]', props.compact && 'focus-within:bg-gray-100 dark:focus-within:bg-gray-800/80' )} > <div class="font-medium dark:text-white flex items-center justify-between"> <div class="flex items-center min-w-0 flex-1 mr-2 text-base"> <Show when={props.isFork} fallback={<BookMarked class="w-4 h-4 mr-1.5 shrink-0 text-gray-400" />} > <GitFork class="w-4 h-4 mr-1.5 shrink-0 text-gray-400" /> </Show> <A href={linkHref()} class={clsx( 'truncate min-w-0 no-underline hover:underline text-black dark:text-white font-bold', props.compact && 'focus:outline-none' )} > {props.owner}/{props.name} </A> </div> </div>
<Show when={props.description}> <div class="text-gray-600 dark:text-gray-300 text-sm line-clamp-2 mt-1"> {props.description} </div> </Show>
<Show when={props.language || props.stars !== undefined || props.forks !== undefined || props.openIssues !== undefined || props.openPRs !== undefined}> <div class="text-gray-400 text-xs font-mono inline-flex gap-4 mt-auto pt-2"> <Show when={props.language}> <div class="flex gap-1.5 items-center"> <span class="rounded-full inline-block" style={{ 'background-color': langColor(), width: '10px', height: '10px' }} /> <span>{props.language}</span> </div> </Show> <Show when={props.stars !== undefined && props.stars > 0}> <div class="flex gap-1 items-center"> <Star class="w-3 h-3 fill-current" /> <span>{props.stars}</span> </div> </Show> <Show when={props.forks !== undefined && props.forks > 0}> <div class="flex gap-1 items-center"> <GitFork class="w-3 h-3" /> <span>{props.forks}</span> </div> </Show> <Show when={props.openIssues !== undefined && props.openIssues > 0}> <div class="flex gap-1 items-center"> <CircleDot class="w-3 h-3" /> <span>{props.openIssues}</span> </div> </Show> <Show when={props.openPRs !== undefined && props.openPRs > 0}> <div class="flex gap-1 items-center"> <GitPullRequest class="w-3 h-3" /> <span>{props.openPRs}</span> </div> </Show> </div> </Show> </div> );};
20 unmodified lines21222324252627282930313233343536373839404142434445464748165 unmodified linesExpand all21421521621720121821922022122222322422522622722822923023123223323420823523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529619 unmodified lines20 unmodified linesexport interface RecentRepo { owner: string; slug: string; title: string; description?: string; language?: string; stars?: number; openIssues?: number; openPRs?: number; updatedAt: string;}
export interface RecentIssuePull { uri: string; kind: 'issue' | 'pull'; title: string; repoLabel: string; href: string; state: 'open' | 'closed' | 'merged'; number: number; author: string; updatedAt: string;}
export const parseRepoJump = (value: string): { owner: string; repo: string } | null => { const trimmed = value.trim(); if (!trimmed) { return null;165 unmodified linesExpand all return []; }};
export const saveRecentRepo = (repo: RepoContext) => {export const saveRecentRepo = ( repo: RepoContext, stats?: { stars?: number; openIssues?: number; openPRs?: number }) => { const currentItems = loadRecentRepos(); const existing = currentItems.find((item) => item.owner === repo.owner.handle && item.slug === repo.slug); const next: RecentRepo = { owner: repo.owner.handle, slug: repo.slug, title: `${repo.owner.handle}/${repo.slug}`, description: repo.record.value.description?.trim() || undefined, language: existing?.language, stars: stats?.stars ?? existing?.stars, openIssues: stats?.openIssues ?? existing?.openIssues, openPRs: stats?.openPRs ?? existing?.openPRs, updatedAt: new Date().toISOString(), }; const items = loadRecentRepos().filter((item) => !(item.owner === next.owner && item.slug === next.slug)); const items = currentItems.filter((item) => !(item.owner === next.owner && item.slug === next.slug)); items.unshift(next); localStorage.setItem(RECENT_REPOS_KEY, JSON.stringify(items.slice(0, 8)));};
export const updateRecentRepoLanguage = (owner: string, slug: string, language: string) => { try { const items = loadRecentRepos(); const idx = items.findIndex((item) => item.owner === owner && item.slug === slug); if (idx !== -1) { items[idx] = { ...items[idx], language }; localStorage.setItem(RECENT_REPOS_KEY, JSON.stringify(items)); } } catch { // ignore }};
export const loadRecentIssuesPulls = (): RecentIssuePull[] => { try { const raw = localStorage.getItem('untangled.recentIssuesPulls'); if (!raw) { return []; } return JSON.parse(raw) as RecentIssuePull[]; } catch { return []; }};
export const saveRecentIssueOrPull = ( owner: string, slug: string, item: { uri: string; kind: 'issue' | 'pull'; title: string; number: number; state: 'open' | 'closed' | 'merged'; author: string; },) => { const next: RecentIssuePull = { uri: item.uri, kind: item.kind, title: item.title, repoLabel: `${owner}/${slug}`, href: `/${owner}/${slug}/${item.kind}s/${item.number || item.uri.split('/').pop()}`, state: item.state, number: item.number, author: item.author, updatedAt: new Date().toISOString(), }; const items = loadRecentIssuesPulls().filter((x) => x.uri !== next.uri); items.unshift(next); localStorage.setItem('untangled.recentIssuesPulls', JSON.stringify(items.slice(0, 10)));};
export const uniqueCommenters = ( comments: Array<IssueComment | PullComment>,): Array<{ did: string; handle: string }> => { const seen = new Set<string>();19 unmodified lines5 unmodified lines6789101011121314505 unmodified linesExpand all520521522523524525526527528529530531532533534535536537485 unmodified linesExpand all5 unmodified linesimport { buildBlobDataUrl, decodeBlobText, getRepoBlob, getRepoBranches, getRepoDefaultBranch, getRepoLanguages, getRepoLog, getRepoTags, getRepoTree } from '../../lib/api/repos';import { Avatar, ErrorState, PlaceholderAvatar, buttonStyles, cardStyles } from '../../components/common';import { CloneDropdown, CodeView, FileRow, MarkdownBlock, OverviewFileRow, ReadmeCard, RepoBlobSkeleton, RepoLanguageBarSkeleton, RepoTreeSkeleton } from '../../components/repo';import { RepoFrame, useRepoQuery } from './shared';import { blobHref, commitHref, commitsHref, countLines, decodeRoutePath, formatBytes, formatLanguagePercent, formatRelativeTime, getErrorMessage, getParentPath, imageLike, isDirectory, joinPath, languageColor, markdownLike, safeDecode, sortedTreeEntries, svgLike, treeHref, videoLike } from '../../lib/repo-utils';import { blobHref, commitHref, commitsHref, countLines, decodeRoutePath, formatBytes, formatLanguagePercent, formatRelativeTime, getErrorMessage, getParentPath, imageLike, isDirectory, joinPath, languageColor, markdownLike, safeDecode, sortedTreeEntries, svgLike, treeHref, updateRecentRepoLanguage, videoLike } from '../../lib/repo-utils';import { LOADING_DELAY_MS, hasNamedRef, normalizeLanguages, resolveDefaultBranchName, resolveRouteRefAndPath, shouldAnimateNavigation, useDelayedLoading } from './code-helpers';
const PathBreadcrumbs: Component<{ repo: Parameters<typeof treeHref>[0];505 unmodified linesExpand all createEffect(() => { if (!treeQuery.isFetching) setPendingTreeHref(undefined); });
createEffect(() => { const repo = repoQuery.data; const langs = languagesQuery.data; if (repo && langs?.languages?.length) { const normalized = normalizeLanguages(langs.languages, langs.totalSize); const top = normalized[0]?.name; if (top) updateRecentRepoLanguage(repo.owner.handle, repo.slug, top); } });
const handleFileClick = ( event: MouseEvent, href: string, repo: Parameters<typeof getRepoBlob>[0],485 unmodified linesExpand all10 unmodified lines11121314151516171819298 unmodified linesExpand all318319320321322323324325326327328329330331332333334335336337338339340355 unmodified linesExpand all10 unmodified linesimport { useAuth } from '../../lib/auth';import { Avatar, ErrorState, PaginationControls, StateBadge, ToggleButton, buttonStyles, cardStyles, inputStyles, textareaStyles } from '../../components/common';import { AtUriPanel, MarkdownBlock, RepoFormSkeleton, RepoListPageSkeleton, RepoListSkeleton, RepoThreadSkeleton } from '../../components/repo';import { RepoFrame, issueQueryKey, issuesQueryKey, useRepoQuery } from './shared';import { REPO_LIST_PAGE_LIMIT, formatRelativeTime, getErrorMessage, issueHref, parseIntegerSearchParam, uniqueCommenters } from '../../lib/repo-utils';import { REPO_LIST_PAGE_LIMIT, formatRelativeTime, getErrorMessage, issueHref, parseIntegerSearchParam, uniqueCommenters, saveRecentIssueOrPull } from '../../lib/repo-utils';import { buildIssueCommentThreads, buildIssueFilterQuery, parseIssueFilter } from './issues-helpers';
export const IssuesPage: Component = () => { const auth = useAuth();298 unmodified linesExpand all queryFn: async () => getIssue(repo!, issueRef()), }; });
createEffect(() => { const repo = repoQuery.data; const detail = issueQuery.data; if (repo && detail) { saveRecentIssueOrPull(repo.owner.handle, repo.slug, { uri: detail.issue.uri, kind: 'issue', title: detail.issue.value.title, number: detail.issue.number, state: detail.issue.state, author: detail.issue.author.handle, }); } });
const withIssueInvalidation = async (repo: RepoContext, issueRef: string, issueUri: string) => { await client.invalidateQueries({ queryKey: issuesQueryKey(repo.repoDid) }); await client.invalidateQueries({ queryKey: issueQueryKey(repo.repoDid, issueRef) }); await client.invalidateQueries({ queryKey: issueQueryKey(repo.repoDid, parseAtUri(issueUri).rkey) });355 unmodified linesExpand all24 unmodified lines25262728292930313233772 unmodified linesExpand all806807808809810811812813814815816817818819820821822823824825826827828350 unmodified linesExpand all24 unmodified linesimport { useAuth } from '../../lib/auth';import { Avatar, ErrorState, LoadingState, PaginationControls, StateBadge, ToggleButton, buttonStyles, cardStyles, textareaStyles } from '../../components/common';import { AtUriPanel, BranchPill, CommentCard, DiffView, MarkdownBlock, PullComposeSkeleton, PullDiffSkeleton, PullDiffView, RepoListPageSkeleton, RepoListSkeleton, RepoThreadSkeleton } from '../../components/repo';import { RepoFrame, pullQueryKey, pullsQueryKey, useRepoQuery } from './shared';import { REPO_LIST_PAGE_LIMIT, formatRelativeTime, getErrorMessage, parseIntegerSearchParam, pullHref, uniqueCommenters } from '../../lib/repo-utils';import { REPO_LIST_PAGE_LIMIT, formatRelativeTime, getErrorMessage, parseIntegerSearchParam, pullHref, uniqueCommenters, saveRecentIssueOrPull } from '../../lib/repo-utils';import { buildPullFilterQuery, parsePastedPatchPrefill, parsePullFilter, type PullSourceMode } from './pulls-helpers';
export const PullsPage: Component = () => { const auth = useAuth();772 unmodified linesExpand all setRoundIndex(rounds - 1); } });
createEffect(() => { const repo = repoQuery.data; const detail = pullQuery.data; if (repo && detail) { saveRecentIssueOrPull(repo.owner.handle, repo.slug, { uri: detail.pull.uri, kind: 'pull', title: detail.pull.value.title, number: detail.pull.number, state: detail.pull.state, author: detail.pull.author.handle, }); } });
const patchQuery = createQuery(() => { const detail = pullQuery.data; return { queryKey: ['pull-patch', detail?.pull.uri, roundIndex()],350 unmodified linesExpand all