Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
6769029
Add author pages, is_resolved badge, participant count, reaction coun…
HarshMN2345 Jun 12, 2026
6dc92de
Add author pages, enriched thread data, participant count icon
HarshMN2345 Jun 12, 2026
978a699
Fix persons icon size
HarshMN2345 Jun 12, 2026
7d4a58a
Fix bot review: env var for authors collection, separate catches, met…
HarshMN2345 Jun 12, 2026
4b03226
Fix: hardcode authors collection id, restore mongo icon type
HarshMN2345 Jun 12, 2026
f449183
Fix icon font ordering: use inline SVG for persons icon, restore icon…
HarshMN2345 Jun 12, 2026
93b4f1f
Add ProfilePage structured data schema to author pages
HarshMN2345 Jun 15, 2026
9905d81
Remove accidental allowedHosts, restore mongo sprite symbol
HarshMN2345 Jun 15, 2026
0adccec
Fix icon types: remove duplicate mongo, add zzz-persons
HarshMN2345 Jun 15, 2026
5a32369
Add last_activity timestamp to thread cards
HarshMN2345 Jun 15, 2026
5c8b89d
Show reply count before last activity time
HarshMN2345 Jun 15, 2026
7dfe163
Use Intl.RelativeTimeFormat for last_activity display
HarshMN2345 Jun 15, 2026
5e8b5f1
Detect resolved threads from title on website side without waiting fo…
HarshMN2345 Jun 15, 2026
2643532
Green color for resolved badge
HarshMN2345 Jun 15, 2026
4159556
Use inline style for resolved badge instead of global CSS override
HarshMN2345 Jun 15, 2026
23ac397
Use scoped CSS class for resolved badge color
HarshMN2345 Jun 15, 2026
ad77c4c
Merge branch 'main' into feat/threads-author-pages
HarshMN2345 Jun 15, 2026
7d1ed9d
Guard getAuthorThreads, add year branch to timeAgo
HarshMN2345 Jun 15, 2026
9decbad
Fix implicit any type on threads array
HarshMN2345 Jun 15, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/icons/optimized/persons.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 6 additions & 0 deletions src/icons/svg/persons.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
24 changes: 16 additions & 8 deletions src/lib/components/ui/icon/sprite/sprite.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -105,14 +105,6 @@
fill-rule="evenodd"
></path>
</symbol>
<symbol id="mongo" stroke="currentColor" viewBox="0 0 120 258">
<path
d="M83.009 28.756 C 72.133 15.909,62.767 2.861,60.854 0.151 C 60.653 -0.050,60.350 -0.050,60.149 0.151 C 58.236 2.861,48.870 15.909,37.994 28.756 C -55.359 147.292,52.697 227.287,52.697 227.287 L 53.603 227.889 C 54.409 240.235,56.423 258.000,56.423 258.000 L 60.451 258.000 L 64.479 258.000 C 64.479 258.000,66.493 240.335,67.299 227.889 L 68.205 227.187 C 68.306 227.187,176.362 147.292,83.009 28.756 M60.451 225.480 C 60.451 225.480,55.617 221.365,54.308 219.257 L 54.308 219.057 L 60.149 89.981 C 60.149 89.580,60.753 89.580,60.753 89.981 L 66.594 219.057 L 66.594 219.257 C 65.285 221.365,60.451 225.480,60.451 225.480 Z"
fill="currentColor"
stroke="none"
fill-rule="evenodd"
></path>
</symbol>
<symbol id="divider-vertical" stroke="currentColor" viewBox="0 0 20 20">
<path
d="M9.750 3.463 C 9.591 3.536,9.520 3.610,9.451 3.775 C 9.406 3.884,9.400 4.585,9.400 10.009 L 9.400 16.119 9.475 16.268 C 9.696 16.705,10.304 16.705,10.525 16.268 L 10.600 16.119 10.599 10.001 C 10.599 4.136,10.596 3.878,10.537 3.750 C 10.464 3.591,10.390 3.520,10.225 3.451 C 10.064 3.384,9.914 3.388,9.750 3.463 "
Expand All @@ -137,6 +129,22 @@
fill-rule="evenodd"
></path>
</symbol>
<symbol id="mongo" stroke="currentColor" viewBox="0 0 120 258">
<path
d="M83.009 28.756 C 72.133 15.909,62.767 2.861,60.854 0.151 C 60.653 -0.050,60.350 -0.050,60.149 0.151 C 58.236 2.861,48.870 15.909,37.994 28.756 C -55.359 147.292,52.697 227.287,52.697 227.287 L 53.603 227.889 C 54.409 240.235,56.423 258.000,56.423 258.000 L 60.451 258.000 L 64.479 258.000 C 64.479 258.000,66.493 240.335,67.299 227.889 L 68.205 227.187 C 68.306 227.187,176.362 147.292,83.009 28.756 M60.451 225.480 C 60.451 225.480,55.617 221.365,54.308 219.257 L 54.308 219.057 L 60.149 89.981 C 60.149 89.580,60.753 89.580,60.753 89.981 L 66.594 219.057 L 66.594 219.257 C 65.285 221.365,60.451 225.480,60.451 225.480 Z"
fill="currentColor"
stroke="none"
fill-rule="evenodd"
></path>
</symbol>
<symbol id="zzz-persons" stroke="currentColor" viewBox="0 0 20 20">
<path
d="M7.017 2.438 C 6.652 2.484,6.313 2.588,5.948 2.766 C 3.954 3.733,3.295 6.168,4.523 8.025 C 4.691 8.278,5.064 8.669,5.333 8.875 C 5.631 9.102,6.217 9.382,6.617 9.489 C 6.914 9.569,7.009 9.578,7.500 9.578 C 7.989 9.578,8.086 9.569,8.376 9.491 C 8.735 9.394,9.224 9.176,9.523 8.978 C 9.778 8.809,10.169 8.437,10.375 8.167 C 10.602 7.869,10.882 7.283,10.989 6.883 C 11.069 6.586,11.078 6.491,11.078 6.000 C 11.078 5.511,11.069 5.414,10.991 5.124 C 10.831 4.530,10.527 3.973,10.133 3.553 C 9.335 2.703,8.182 2.290,7.017 2.438 M8.133 3.681 C 8.234 3.709,8.444 3.794,8.600 3.871 C 8.829 3.984,8.942 4.070,9.187 4.315 C 9.448 4.575,9.513 4.664,9.643 4.934 C 9.840 5.347,9.883 5.537,9.883 6.000 C 9.883 6.467,9.840 6.656,9.641 7.067 C 9.510 7.335,9.441 7.430,9.184 7.687 C 8.925 7.947,8.835 8.014,8.566 8.144 C 7.857 8.486,7.143 8.486,6.434 8.144 C 6.165 8.014,6.074 7.946,5.814 7.686 C 5.552 7.425,5.487 7.336,5.357 7.066 C 5.160 6.653,5.117 6.463,5.117 6.000 C 5.117 5.537,5.160 5.347,5.357 4.934 C 5.487 4.664,5.552 4.575,5.814 4.314 C 6.074 4.053,6.165 3.987,6.434 3.857 C 6.608 3.774,6.832 3.689,6.933 3.670 C 7.034 3.650,7.147 3.627,7.183 3.619 C 7.310 3.589,7.951 3.631,8.133 3.681 M13.234 3.673 C 12.906 3.841,12.805 4.232,13.010 4.538 C 13.061 4.615,13.185 4.703,13.402 4.817 C 13.577 4.909,13.800 5.043,13.897 5.115 C 14.273 5.391,14.639 5.963,14.751 6.448 C 14.780 6.575,14.804 6.824,14.804 7.000 C 14.804 7.176,14.780 7.425,14.751 7.552 C 14.639 8.038,14.273 8.609,13.886 8.901 C 13.786 8.976,13.565 9.108,13.394 9.194 C 13.174 9.305,13.062 9.383,13.009 9.462 C 12.806 9.769,12.907 10.159,13.234 10.327 C 13.445 10.436,13.623 10.413,14.007 10.227 C 14.960 9.765,15.637 8.921,15.925 7.836 C 15.998 7.560,16.009 7.447,16.009 7.000 C 16.009 6.553,15.998 6.440,15.925 6.164 C 15.636 5.076,14.968 4.244,14.010 3.777 C 13.618 3.587,13.445 3.564,13.234 3.673 M4.952 10.438 C 4.337 10.497,3.556 10.743,3.025 11.046 C 1.978 11.644,1.149 12.594,0.749 13.656 C 0.464 14.411,0.401 14.858,0.401 16.135 C 0.400 17.090,0.402 17.124,0.475 17.268 C 0.696 17.705,1.304 17.705,1.525 17.268 C 1.597 17.125,1.600 17.085,1.600 16.242 C 1.600 14.895,1.669 14.485,2.014 13.783 C 2.406 12.987,2.987 12.406,3.783 12.014 C 4.216 11.801,4.605 11.685,5.044 11.636 C 5.472 11.588,9.528 11.588,9.956 11.636 C 11.453 11.803,12.759 12.884,13.216 14.333 C 13.357 14.781,13.399 15.211,13.399 16.218 C 13.400 17.086,13.403 17.125,13.475 17.268 C 13.696 17.706,14.301 17.705,14.527 17.266 C 14.604 17.117,14.604 17.116,14.590 15.983 C 14.574 14.720,14.550 14.537,14.311 13.833 C 13.676 11.963,11.966 10.619,9.983 10.434 C 9.544 10.393,5.385 10.396,4.952 10.438 M15.732 10.975 C 15.294 11.196,15.295 11.809,15.734 12.023 C 15.816 12.063,16.041 12.171,16.233 12.264 C 17.021 12.642,17.625 13.256,18.018 14.076 C 18.324 14.715,18.400 15.165,18.400 16.335 C 18.400 17.082,18.404 17.126,18.475 17.268 C 18.696 17.705,19.304 17.705,19.525 17.268 C 19.597 17.125,19.600 17.085,19.600 16.267 C 19.600 15.330,19.573 15.018,19.448 14.512 C 19.055 12.916,17.853 11.560,16.320 10.983 C 16.047 10.880,15.923 10.879,15.732 10.975 "
stroke="none"
fill-rule="evenodd"
fill="currentColor"
></path>
</symbol>
<symbol id="ycombinator" stroke="currentColor" viewBox="0 0 24 24">
<path
d="M3.000 12.000 L 3.000 21.000 12.000 21.000 L 21.000 21.000 21.000 12.000 L 21.000 3.000 12.000 3.000 L 3.000 3.000 3.000 12.000 M9.813 7.550 C 9.928 7.832,11.602 11.179,11.718 11.360 C 11.782 11.459,11.835 11.567,11.837 11.600 C 11.839 11.633,11.886 11.758,11.942 11.877 C 12.043 12.093,12.043 12.094,12.107 12.003 C 12.142 11.952,12.228 11.774,12.299 11.606 C 12.369 11.438,12.479 11.216,12.543 11.114 C 12.607 11.012,13.047 10.135,13.520 9.165 L 14.380 7.402 15.051 7.401 L 15.722 7.400 15.666 7.490 C 15.636 7.540,14.933 8.859,14.105 10.422 L 12.600 13.265 12.600 15.132 L 12.600 17.000 12.000 17.000 L 11.400 17.000 11.400 15.102 L 11.400 13.204 9.844 10.302 L 8.288 7.400 9.020 7.400 L 9.751 7.400 9.813 7.550 "
Expand Down
5 changes: 3 additions & 2 deletions src/lib/components/ui/icon/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ export type IconType =
| 'github'
| 'ticket'
| 'sparkle'
| 'mongo'
| 'divider-vertical'
| 'nuxt'
| 'chevron-up'
Expand Down Expand Up @@ -68,4 +67,6 @@ export type IconType =
| 'minus'
| 'chevron-left'
| 'light'
| 'mcp';
| 'mcp'
| 'mongo'
| 'zzz-persons';
37 changes: 37 additions & 0 deletions src/lib/utils/metadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -254,3 +254,40 @@ export function createDiscussionForumPageSchema(options: {

return escapeJsonLtForHtmlScript(JSON.stringify(graph));
}

export function createAuthorPageSchema({
canonicalUrl,
author
}: {
canonicalUrl: string;
author: {
display_name: string;
username: string;
avatar?: string;
thread_count: number;
reply_count: number;
bio?: string;
};
}): string {
const schema: Record<string, unknown> = {
'@context': 'https://schema.org',
'@type': 'ProfilePage',
url: canonicalUrl,
mainEntity: {
'@type': 'Person',
name: author.display_name,
alternateName: author.username,
...(author.avatar ? { image: author.avatar } : {}),
...(author.bio ? { description: author.bio } : {}),
interactionStatistic: [
{
'@type': 'InteractionCounter',
interactionType: 'https://schema.org/WriteAction',
userInteractionCount: author.thread_count + author.reply_count
}
]
}
};

return escapeJsonLtForHtmlScript(JSON.stringify(schema));
}
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,10 @@
});
}

function formatGroup(group: string) {
return group.replace(/([a-z])([A-Z])/g, '$1 $2');
}

function groupMethodsByGroup(methods: SDKMethod[]) {
return methods.reduce<Record<string, SDKMethod[]>>((acc, method) => {
const groupKey = method.group || '';
Expand Down Expand Up @@ -387,7 +391,7 @@
<li class="web-references-menu-group">
{#if group !== ''}
<h6 class="text-eyebrow text-greyscale-500 mb-2 uppercase">
{group}
{formatGroup(group)}
</h6>
{/if}
<ul class="flex flex-col gap-2">
Expand Down
41 changes: 35 additions & 6 deletions src/routes/threads/ThreadCard.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,17 @@
export let query: string;

$: highlightTerms = query?.split(' ') ?? [];
$: isResolved = thread.is_resolved || /\[(solved|resolved|closed|fixed)\]/i.test(thread.title);

function timeAgo(dateStr: string): string {
const diff = (Date.now() - new Date(dateStr).getTime()) / 1000;
const formatter = new Intl.RelativeTimeFormat('en', { numeric: 'auto' });
if (diff < 3600) return formatter.format(-Math.floor(diff / 60), 'minute');
if (diff < 86400) return formatter.format(-Math.floor(diff / 3600), 'hour');
if (diff < 2592000) return formatter.format(-Math.floor(diff / 86400), 'day');
if (diff < 31536000) return formatter.format(-Math.floor(diff / 2592000), 'month');
return formatter.format(-Math.floor(diff / 31536000), 'year');
}
</script>

{#key highlightTerms}
Expand All @@ -30,19 +41,32 @@

<div class="mt-4 flex min-w-0 flex-wrap justify-between gap-4">
<ul class="flex min-w-0 flex-wrap gap-2">
{#if isResolved}
<li class="min-w-0">
<div class="web-tag tag-resolved truncate">
<span class="web-icon-check"></span>
Resolved
</div>
</li>
{/if}
{#each thread.tags ?? [] as tag, index (tag + index)}
<li class="min-w-0">
<div class="web-tag truncate">{tag}</div>
</li>
{/each}
</ul>

<div
class="web-icon-button is-more-content web-u-pointer-events-none flex shrink-0 items-center"
aria-label="Replies"
>
<span class="web-icon-message web-u-font-size-16" aria-hidden="true"></span>
<span class="text-caption font-inter">{thread.message_count}</span>
<div class="flex shrink-0 items-center gap-3">
<div
class="web-icon-button is-more-content web-u-pointer-events-none flex items-center"
aria-label="Replies"
>
<span class="web-icon-message web-u-font-size-16" aria-hidden="true"></span>
<span class="text-caption font-inter">{thread.message_count}</span>
</div>
{#if thread.last_activity}
<span class="text-caption">{timeAgo(thread.last_activity)}</span>
{/if}
</div>
</div>
</a>
Expand All @@ -54,6 +78,11 @@
overflow: hidden;
}

.tag-resolved {
color: #22c55e;
border-color: rgba(34, 197, 94, 0.3);
}

.thread {
position: relative;
max-width: 100%;
Expand Down
49 changes: 49 additions & 0 deletions src/routes/threads/[id]/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@
let { data } = $props();

const title = $derived(data.title + ' - Threads' + TITLE_SUFFIX);
const isResolved = $derived(
data.is_resolved || /\[(solved|resolved|closed|fixed)\]/i.test(data.title)
);
const description = DEFAULT_DESCRIPTION;
const discordLink = $derived(
`https://discord.com/channels/564160730845151244/${data.discord_id}`
Expand Down Expand Up @@ -55,6 +58,52 @@
<span class="web-icon-arrow-up"></span>
<span class="text">{data.vote_count}</span>
</li>
{#if isResolved}
<li class="web-tag is-success">
<span class="web-icon-check"></span>
<span class="text">Resolved</span>
</li>
{/if}
{#if data.participant_count}
<li class="web-tag">
<svg
width="16"
height="16"
viewBox="0 0 20 20"
fill="none"
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
style="flex-shrink:0"
>
<circle
cx="7.5"
cy="6"
r="3"
stroke="currentColor"
stroke-width="1.2"
/>
<path
d="M1 17v-1.5A4.5 4.5 0 0 1 5.5 11h4A4.5 4.5 0 0 1 14 15.5V17"
stroke="currentColor"
stroke-width="1.2"
stroke-linecap="round"
/>
<path
d="M13.5 4.2a3 3 0 0 1 0 5.6"
stroke="currentColor"
stroke-width="1.2"
stroke-linecap="round"
/>
<path
d="M16 11.5a4.5 4.5 0 0 1 3 4.2V17"
stroke="currentColor"
stroke-width="1.2"
stroke-linecap="round"
/>
</svg>
<span class="text">{data.participant_count}</span>
</li>
{/if}
{#each data.tags ?? [] as tag}
<li class="web-tag">
<span class="text">{tag}</span>
Expand Down
24 changes: 23 additions & 1 deletion src/routes/threads/[id]/MessageCard.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,14 @@
<div class="author-img">
<img src={message.author_avatar} alt="" class="h-full w-full rounded-[inherit]" />
</div>
<span class="text-sub-body text-primary font-medium">{message.author}</span>
{#if message.author_id}
<a
href="/threads/authors/{message.author_id}"
class="text-sub-body text-primary web-link font-medium">{message.author}</a
>
{:else}
<span class="text-sub-body text-primary font-medium">{message.author}</span>
{/if}
</div>
<span class="timestamp text-caption">
{formatTimestamp(message.timestamp)}
Expand All @@ -45,6 +52,12 @@
}}
/>
</div>
{#if message.reaction_count}
<div class="reactions">
<span class="web-icon-heart" aria-hidden="true"></span>
<span class="text-caption">{message.reaction_count}</span>
</div>
{/if}
<slot />
</div>

Expand All @@ -71,6 +84,15 @@
gap: 0.5rem;
}

.reactions {
display: flex;
align-items: center;
gap: 0.25rem;
margin-block-start: 0.75rem;
opacity: 0.7;
font-size: 0.875rem;
}

.author-img {
--p-size: 1.5rem; // 24px

Expand Down
Loading
Loading