diff --git a/src/components/repo.tsx b/src/components/repo.tsx index 3f7820c..83b418a 100644 --- a/src/components/repo.tsx +++ b/src/components/repo.tsx @@ -2092,3 +2092,33 @@ export const RepoCard: Component = (props) => { ); }; +export const RepoCardSkeleton: Component<{ compact?: boolean; noBorder?: boolean }> = (props) => { + return ( +
+
+
+ + +
+
+ +
+ + +
+ +
+ + +
+
+ ); +}; + + diff --git a/src/index.css b/src/index.css index 6ecd434..e0d2b8d 100644 --- a/src/index.css +++ b/src/index.css @@ -3620,3 +3620,50 @@ details[open] > .untangled-language-bar { } } +.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/pages/profile.tsx b/src/pages/profile.tsx index 6059b26..d826346 100644 --- a/src/pages/profile.tsx +++ b/src/pages/profile.tsx @@ -22,8 +22,8 @@ import { A, useParams, useSearchParams, useNavigate } from '@solidjs/router'; 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 @@ const ProfileTabs: Component<{ 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 @@ const ProfileTabs: Component<{ {tab.icon()} {tab.label} {tab.count !== undefined && ( - - {tab.count()} + + }> + {tab.count()} + )} @@ -115,6 +119,10 @@ const ProfileCardComponent: Component<{ 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 @@ const ProfileCardComponent: Component<{

{userIdent()}

- -

{pronouns()}

+ }> + +

{pronouns()}

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

{description()}

+ + + + + } + > + +

{description()}

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

all followers

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

all following

} + fallback={ +
+ + {() => } + +
+ } >
{
- + )} diff --git a/src/pages/repo/issues.tsx b/src/pages/repo/issues.tsx index bb08e13..62e0168 100644 --- 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 @@ export const IssuePage: Component = () => { 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); @@ -334,6 +335,24 @@ export const IssuePage: Component = () => { } }); + 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); + } + }); + const withIssueInvalidation = async (repo: RepoContext, issueRef: string, issueUri: string) => { await client.invalidateQueries({ queryKey: issuesQueryKey(repo.repoDid) }); await client.invalidateQueries({ queryKey: issueQueryKey(repo.repoDid, issueRef) }); @@ -516,7 +535,7 @@ export const IssuePage: Component = () => { {(thread) => (
-
+
@@ -527,7 +546,7 @@ export const IssuePage: Component = () => { (author) · - {formatRelativeTime(thread.item.value.createdAt)} + {formatRelativeTime(thread.item.value.createdAt)}
@@ -537,7 +556,7 @@ export const IssuePage: Component = () => {
{(reply) => ( -
+
@@ -548,7 +567,7 @@ export const IssuePage: Component = () => { (author) · - {formatRelativeTime(reply.value.createdAt)} + {formatRelativeTime(reply.value.createdAt)}
diff --git a/src/pages/repo/pulls.tsx b/src/pages/repo/pulls.tsx index 96a8c34..910c354 100644 --- a/src/pages/repo/pulls.tsx +++ b/src/pages/repo/pulls.tsx @@ -19,7 +19,7 @@ import { 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 @@ const PRCommentView: Component<{ createdAt: string; markdown: string; isReply?: boolean; + id?: string; }> = (props) => (
{props.author} · - {formatRelativeTime(props.createdAt)} + {formatRelativeTime(props.createdAt)}}> + {formatRelativeTime(props.createdAt)} +
@@ -1042,6 +1046,7 @@ export const PullPage: Component = () => { 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(''); @@ -1103,6 +1108,28 @@ export const PullPage: Component = () => { } }); + 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); + } + }); + const patchQuery = createQuery(() => { const detail = pullQuery.data; return { @@ -1515,6 +1542,7 @@ export const PullPage: Component = () => { {(thread) => (
{ {(reply) => (
= (props) => { const repoQuery = createQuery(() => ({ @@ -185,7 +186,8 @@ const RepoThreadLink: Component<{ 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 @@ const CommentParentLink: Component<{ hit: SearchHit }> = (props) => { ? (parent().record.value as { repo: Did }).repo : (parent().record.value as { target: { repo: Did } }).target.repo); return ( - + comment on {parent().kind} );