diff --git a/src/index.css b/src/index.css --- a/src/index.css +++ b/src/index.css @@ -3620,3 +3620,50 @@ } } +.untangled-comment-highlight { + position: relative; + background-color: rgb(254 240 138 / 0.15) !important; + border-radius: 0; +} + +.untangled-comment-highlight::before { + content: ''; + position: absolute; + top: 0; + bottom: 0; + left: 0; + width: 3px; + background-color: #eab308; + border-top-left-radius: 0.25rem; + border-bottom-left-radius: 0.25rem; + z-index: 1; +} + +/* Align solid bar with the vertical replies thread line */ +.-ml-4.untangled-comment-highlight::before, +.-ml-4 > .untangled-comment-highlight::before { + left: calc(1rem - 2px); + border-radius: 0; +} + +.-ml-4.untangled-comment-highlight, +.-ml-4 > .untangled-comment-highlight { + background-color: transparent !important; + background-image: linear-gradient(to right, transparent calc(1rem - 2px), rgb(254 240 138 / 0.15) calc(1rem - 2px)) !important; +} + +@media (prefers-color-scheme: dark) { + .untangled-comment-highlight { + background-color: rgb(161 98 7 / 0.1) !important; + } + .untangled-comment-highlight::before { + background-color: #ca8a04; + } + .-ml-4.untangled-comment-highlight, + .-ml-4 > .untangled-comment-highlight { + background-color: transparent !important; + background-image: linear-gradient(to right, transparent calc(1rem - 2px), rgb(161 98 7 / 0.1) calc(1rem - 2px)) !important; + } +} + + diff --git a/src/components/repo.tsx b/src/components/repo.tsx --- a/src/components/repo.tsx +++ b/src/components/repo.tsx @@ -2092,3 +2092,33 @@ ); }; +export const RepoCardSkeleton: Component<{ compact?: boolean; noBorder?: boolean }> = (props) => { + return ( +
+
+
+ + +
+
+ +
+ + +
+ +
+ + +
+
+ ); +}; + + diff --git a/src/pages/profile.tsx b/src/pages/profile.tsx --- a/src/pages/profile.tsx +++ b/src/pages/profile.tsx @@ -22,8 +22,8 @@ import { createQuery, useQueryClient } from '@tanstack/solid-query'; import { For, Show, Switch, Match, createMemo, createSignal, createEffect, type Component } from 'solid-js'; import type { Did } from '@atcute/lexicons/syntax'; -import { ErrorState, LoadingState, PlaceholderAvatar, textareaStyles, inputStyles } from '../components/common'; -import { RepoCard } from '../components/repo'; +import { ErrorState, LoadingState, PlaceholderAvatar, SkeletonBlock, textareaStyles, inputStyles } from '../components/common'; +import { RepoCard, RepoCardSkeleton } from '../components/repo'; import { listFollowRecords, createFollow, @@ -48,11 +48,13 @@ activeTab: string; reposCount: number; starsCount: number; + reposLoading?: boolean; + starsLoading?: boolean; }> = (props) => { const tabs = [ { id: 'overview', label: 'overview', icon: () => }, - { id: 'repos', label: 'repos', icon: () => , count: () => props.reposCount }, - { id: 'starred', label: 'starred', icon: () => , count: () => props.starsCount }, + { id: 'repos', label: 'repos', icon: () => , count: () => props.reposCount, loading: () => props.reposLoading }, + { id: 'starred', label: 'starred', icon: () => , count: () => props.starsCount, loading: () => props.starsLoading }, ]; return ( @@ -78,8 +80,10 @@ {tab.icon()} {tab.label} {tab.count !== undefined && ( - - {tab.count()} + + }> + {tab.count()} + )} @@ -115,6 +119,10 @@ openPRCount: number; openIssueCount: number; closedIssueCount: number; + profileLoading?: boolean; + followersLoading?: boolean; + followingLoading?: boolean; + vouchLoading?: boolean; }> = (props) => { const userIdent = () => props.actor.handle || props.actor.did; @@ -265,8 +273,10 @@

{userIdent()}

- -

{pronouns()}

+ }> + +

{pronouns()}

+
@@ -274,11 +284,21 @@ @@ -291,67 +311,88 @@ when={isEditing()} fallback={ <> - -

{description()}

+ + + + + } + > + +

{description()}

+
-
- -
- - {location()} + +
+ + +
+
+ + +
-
+ } + > +
+ +
+ + {location()} +
+
- -
- - - Bluesky - - - - - {props.actor.handle} - -
-
+ +
+ + + Bluesky + + + + + {props.actor.handle} + +
+
- - {(link) => ( - - - - )} - -
- - 0}> -
- - {(statKind) => ( - 0}> -
- {getStatValue(statKind)} - {formatStatLabel(statKind)} + + {(link) => ( + + )} @@ -359,50 +400,86 @@
+ +
+ + +
+
+ + +
+
+ } + > + 0}> +
+ + {(statKind) => ( + 0}> +
+ {getStatValue(statKind)} + {formatStatLabel(statKind)} +
+
+ )} +
+
+
+
+
@@ -1324,24 +1440,33 @@
- - This user does not have any repos yet. -
+ + {() => } + } > - {(repo) => ( - - )} - + + This user does not have any repos yet. + + } + > + {(repo) => ( + + )} + +
@@ -1351,7 +1476,13 @@
} + fallback={ +
+ + {() => } + +
+ } >
all followers

} + fallback={ +
+ + {() => } + +
+ } >
all following

} + fallback={ +
+ + {() => } + +
+ } >
- + )} diff --git a/src/pages/search.tsx b/src/pages/search.tsx --- a/src/pages/search.tsx +++ b/src/pages/search.tsx @@ -174,6 +174,7 @@ kind: 'issue' | 'pull'; repoDid: Did; threadRef: string; + commentRef?: string; children: JSX.Element; }> = (props) => { const repoQuery = createQuery(() => ({ @@ -185,7 +186,8 @@ const repo = repoQuery.data; if (!repo) return undefined; const fn = props.kind === 'issue' ? issueHref : pullHref; - return fn(repo, props.threadRef); + const baseHref = fn(repo, props.threadRef); + return props.commentRef ? `${baseHref}#comment-${props.commentRef}` : baseHref; }); return ( @@ -240,7 +242,7 @@ ? (parent().record.value as { repo: Did }).repo : (parent().record.value as { target: { repo: Did } }).target.repo); return ( - + comment on {parent().kind} ); diff --git a/src/pages/repo/issues.tsx b/src/pages/repo/issues.tsx --- a/src/pages/repo/issues.tsx +++ b/src/pages/repo/issues.tsx @@ -1,6 +1,6 @@ import clsx from 'clsx'; import { Ban, CirclePlus, LoaderCircle, MessageSquarePlus, Pencil, RefreshCcwDot, Reply, Search, Smile, X } from 'lucide-solid'; -import { A, useNavigate, useParams, useSearchParams } from '@solidjs/router'; +import { A, useNavigate, useParams, useSearchParams, useLocation } from '@solidjs/router'; import { createQuery, useQueryClient } from '@tanstack/solid-query'; import type { ResourceUri } from '@atcute/lexicons/syntax'; import { For, Match, Show, Switch, createEffect, createMemo, createSignal, type Component } from 'solid-js'; @@ -302,6 +302,7 @@ const repoQuery = useRepoQuery(); const params = useParams(); const client = useQueryClient(); + const location = useLocation(); const issueRef = createMemo(() => params.issueRef ?? ''); const [comment, setComment] = createSignal(''); const [replyingTo, setReplyingTo] = createSignal(null); @@ -331,6 +332,24 @@ state: detail.issue.state, author: detail.issue.author.handle, }); + } + }); + + createEffect(() => { + const hash = location.hash; + const cleanHash = hash.startsWith('#') ? hash.slice(1) : hash; + if (cleanHash && cleanHash.startsWith('comment-') && issueQuery.data?.comments) { + setTimeout(() => { + const element = document.getElementById(cleanHash); + if (element) { + // Remove highlight from any other elements first + document.querySelectorAll('.untangled-comment-highlight').forEach((el) => { + el.classList.remove('untangled-comment-highlight'); + }); + element.scrollIntoView({ behavior: 'smooth', block: 'center' }); + element.classList.add('untangled-comment-highlight'); + } + }, 150); } }); @@ -516,7 +535,7 @@ {(thread) => (
-
+
@@ -527,7 +546,7 @@ (author) · - {formatRelativeTime(thread.item.value.createdAt)} + {formatRelativeTime(thread.item.value.createdAt)}
@@ -537,7 +556,7 @@
{(reply) => ( -
+
@@ -548,7 +567,7 @@ (author) · - {formatRelativeTime(reply.value.createdAt)} + {formatRelativeTime(reply.value.createdAt)}
diff --git a/src/pages/repo/pulls.tsx b/src/pages/repo/pulls.tsx --- a/src/pages/repo/pulls.tsx +++ b/src/pages/repo/pulls.tsx @@ -19,7 +19,7 @@ X, } from 'lucide-solid'; import type { Did } from '@atcute/lexicons/syntax'; -import { A, useNavigate, useParams, useSearchParams } from '@solidjs/router'; +import { A, useNavigate, useParams, useSearchParams, useLocation } from '@solidjs/router'; import { createQuery, useQueryClient } from '@tanstack/solid-query'; import { For, Match, Show, Switch, createEffect, createMemo, createSignal, type Component, type JSX } from 'solid-js'; import { resolveActor } from '../../lib/api/identity'; @@ -1014,8 +1014,10 @@ createdAt: string; markdown: string; isReply?: boolean; + id?: string; }> = (props) => (
{props.author} · - {formatRelativeTime(props.createdAt)} + {formatRelativeTime(props.createdAt)}}> + {formatRelativeTime(props.createdAt)} +
@@ -1042,6 +1046,7 @@ const repoQuery = useRepoQuery(); const params = useParams(); const client = useQueryClient(); + const location = useLocation(); const pullRef = createMemo(() => params.pullRef ?? ''); const [roundIndex, setRoundIndex] = createSignal(0); const [comment, setComment] = createSignal(''); @@ -1100,6 +1105,28 @@ state: detail.pull.state, author: detail.pull.author.handle, }); + } + }); + + createEffect(() => { + const hash = location.hash; + const cleanHash = hash.startsWith('#') ? hash.slice(1) : hash; + if (cleanHash && cleanHash.startsWith('comment-') && pullQuery.data?.comments) { + setTimeout(() => { + const element = document.getElementById(cleanHash); + if (element) { + const details = element.closest('details'); + if (details) { + details.open = true; + } + // Remove highlight from any other elements first + document.querySelectorAll('.untangled-comment-highlight').forEach((el) => { + el.classList.remove('untangled-comment-highlight'); + }); + element.scrollIntoView({ behavior: 'smooth', block: 'center' }); + element.classList.add('untangled-comment-highlight'); + } + }, 150); } }); @@ -1515,6 +1542,7 @@ {(thread) => (
(