@@ -55,7 +71,7 @@ export function Orbit({
style={{ '--a': `${(index * 360) / members.length}deg` } as CSSProperties}
>
-
+
{member.name.split(' ')[0]}
diff --git a/site/app/components/kit/page.tsx b/site/app/components/kit/page.tsx
index cb5741d..80e8361 100644
--- a/site/app/components/kit/page.tsx
+++ b/site/app/components/kit/page.tsx
@@ -87,5 +87,3 @@ export function StandardRail({ running, lines }: { running: Running[]; lines: Co
>
);
}
-
-export const RAIL_TOPICS = ['deploys', 'releases', 'projects', 'uptime'];
diff --git a/site/app/components/kit/panels.tsx b/site/app/components/kit/panels.tsx
index b7c9e12..ae3edbc 100644
--- a/site/app/components/kit/panels.tsx
+++ b/site/app/components/kit/panels.tsx
@@ -1,5 +1,6 @@
import { Link } from 'react-router';
import { day, shortTag } from '../../format';
+import { useOnline } from '../../live';
import { changelogPath, releaseLine } from '../../model';
import type { Member, Message, Project, Release, Running } from '../../types';
import Status from '../Status';
@@ -12,12 +13,13 @@ export function MembersPanel({
members: Member[];
projectsOf: (member: Member) => number;
}) {
+ const online = useOnline();
return (
{members.map((member) => (
-
+
{member.name}
{member.headline ?? `@${member.github}`}
diff --git a/site/app/components/kit/posts.tsx b/site/app/components/kit/posts.tsx
index 62b2d68..dc5f94b 100644
--- a/site/app/components/kit/posts.tsx
+++ b/site/app/components/kit/posts.tsx
@@ -6,6 +6,7 @@ import { useMotion, useSpotlight, useTilt } from '../../motion/motion';
import type { PostSummary } from '../../posts';
import TagList from '../TagList';
import { accentStyle, Avatar, Cover } from './basics';
+import { Views } from './live';
export function PostCard({ post, compact = false }: { post: PostSummary; compact?: boolean }) {
const hub = useHub();
@@ -38,6 +39,7 @@ export function PostCard({ post, compact = false }: { post: PostSummary; compact
{post.readingMinutes} min read
+
{project && (
{project.name}
diff --git a/site/app/components/kit/projects.tsx b/site/app/components/kit/projects.tsx
index d4882a5..25753ea 100644
--- a/site/app/components/kit/projects.tsx
+++ b/site/app/components/kit/projects.tsx
@@ -7,6 +7,7 @@ import type { Member, Project } from '../../types';
import Status from '../Status';
import TagList from '../TagList';
import { accentStyle, Avatar, Cover } from './basics';
+import { Views } from './live';
interface Props {
project: Project;
@@ -65,12 +66,15 @@ export function Window({ project, owner }: Props) {
{project.summary}
- {owner && (
-
-
- {owner.name}
-
- )}
+
+ {owner && (
+
+
+ {owner.name}
+
+ )}
+
+
diff --git a/site/app/context.ts b/site/app/context.ts
index 7b60476..997f4cc 100644
--- a/site/app/context.ts
+++ b/site/app/context.ts
@@ -5,6 +5,7 @@ import type { Collab } from '../server/collab.ts';
import type { Capture } from '../server/errors.ts';
import type { Messages } from '../server/messages.ts';
import type { Posts } from '../server/posts.ts';
+import type { Presence } from '../server/presence.ts';
import type { Presentations } from '../server/presentation.ts';
import type { Profiles } from '../server/profiles.ts';
import type { Writeback } from '../server/writeback.ts';
@@ -12,6 +13,7 @@ import type { Uploads } from '../server/uploads.ts';
import type { Share } from '../server/share.ts';
import type { Reads } from '../server/reads.ts';
import type { Subscribers } from '../server/subscribers.ts';
+import type { Views } from '../server/views.ts';
import type { Values } from './settings';
import type { Directory, Viewer } from './types';
@@ -29,6 +31,8 @@ export interface Hub {
posts: Posts;
subscribers: Subscribers;
collab: Collab;
+ presence: Presence;
+ views: Views;
address: string;
origin: string;
viewer: Viewer | null;
diff --git a/site/app/live.ts b/site/app/live.ts
index 4c83332..d9020f9 100644
--- a/site/app/live.ts
+++ b/site/app/live.ts
@@ -1,6 +1,7 @@
-import { useEffect, useEffectEvent } from 'react';
+import { useEffect, useEffectEvent, useSyncExternalStore } from 'react';
import { useMatches, useRevalidator } from 'react-router';
-import type { ConsoleLine } from './types';
+import { revalidates, subscription, type LiveHandle } from './topics';
+import type { ConsoleLine, Presence } from './types';
const COALESCE_MS = 100;
@@ -10,6 +11,141 @@ export interface LiveMessage {
key?: string;
kind: string;
console: Omit