Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 2 additions & 0 deletions site/app/components/Shell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { MotionContext } from '../motion/motion';
import { defaults, visible, type NAV } from '../settings';
import AccountMenu from './AccountMenu';
import { Editable, useEditing } from './kit/Editable';
import { LivePulse } from './kit/live';
import { openSearch, Palette } from './kit/Palette';
import { SubscribeForm, SubscribeLink } from './kit/subscribe';

Expand Down Expand Up @@ -70,6 +71,7 @@ export default function Shell({ children }: { children: ReactNode }) {
search
<span className="kbd">/</span>
</button>
{root && <LivePulse />}
<AccountMenu />
</nav>
</div>
Expand Down
10 changes: 8 additions & 2 deletions site/app/components/kit/basics.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,18 @@ export const accentStyle = (accent: string | undefined) =>
interface AvatarProps {
member: { name: string; avatarUrl: string; accent: string };
size?: 'xs' | 'sm' | 'md' | 'lg';
online?: boolean;
}

export function Avatar({ member, size }: AvatarProps) {
export function Avatar({ member, size, online = false }: AvatarProps) {
const pixels = size ? { xs: 20, sm: 32, md: 40, lg: 64 }[size] : 48;
return (
<span className={`avatar ${size ?? ''}`} style={accentStyle(member.accent)} aria-hidden="true">
<span
className={`avatar ${size ?? ''}`}
style={accentStyle(member.accent)}
data-online={online || undefined}
aria-hidden="true"
>
<img src={member.avatarUrl} alt="" width={pixels} height={pixels} loading="lazy" />
</span>
);
Expand Down
72 changes: 70 additions & 2 deletions site/app/components/kit/live.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,81 @@ import { useEffect, useMemo, useRef, useState, type CSSProperties } from 'react'
import { Link } from 'react-router';
import { useFolded } from '../../folds';
import { clock, day, describe } from '../../format';
import { useLiveMessages } from '../../live';
import { useRoot } from '../../hub';
import {
useConnection,
useEditing,
useLiveMessages,
usePresence,
useReading,
useViews,
type Editor,
} from '../../live';
import { releaseLine } from '../../model';
import { useCountUp, useCycle } from '../../motion/motion';
import type { COUNTERS } from '../../settings';
import type { ConsoleLine, Counts, Release } from '../../types';
import Status from '../Status';
import { Fold } from './basics';
import { accentStyle, Fold } from './basics';

export function LivePulse() {
const root = useRoot()!;
const { here } = usePresence(root.presence);
const connection = useConnection();
return (
<span className="live-pulse" data-state={connection} role="status">
<i key={`${connection}${here}`} />
{connection === 'live' ? (
<>
<b>{here}</b> here
</>
) : (
'reconnecting'
)}
</span>
);
}

export function ReadingBadge({ id, initial }: { id: string; initial: number }) {
const count = useReading(id, initial);
return count > 1 ? (
<span key={count} className="badge reading">
<i />
{count} reading now
</span>
) : null;
}

function names(editors: Editor[]) {
const first = editors.map((editor) => editor.name.split(' ')[0]);
return first.length === 1
? `${first[0]} is editing`
: `${first.slice(0, -1).join(', ')} and ${first.at(-1)} are editing`;
}

export function EditingBadge({ id, initial }: { id: string; initial: Editor[] }) {
const editors = useEditing(id, initial);
return editors.length > 0 ? (
<span className="badge editing">
<span className="pens" aria-hidden="true">
{editors.map((editor) => (
<i key={editor.handle} style={accentStyle(editor.color)} />
))}
</span>
{names(editors)}
</span>
) : null;
}

export function Views({ kind, id }: { kind: 'post' | 'project'; id: string }) {
const count = useViews(useRoot()!.views)(kind, id);
const shown = useCountUp(count);
return count > 0 ? (
<span className="views">
{shown} {count === 1 ? 'view' : 'views'}
</span>
) : null;
}

const COUNTER_LABEL: Record<(typeof COUNTERS)[number], string> = {
members: 'members',
Expand Down
220 changes: 220 additions & 0 deletions site/app/components/kit/map-data.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
export const COLUMNS = 96;
export const ROWS = 38;

export const LAND = [
'........................#######.###.####........................................................',
'....................#.##.##################........##......................#....................',
'........................##.......##########....................#.......#######..................',
'......#.........####..#.#####.....#######.............##......#...#.######################......',
'#...######################.#.##...#####.............#######..######.############################',
'..#..###################.....##...###.............###.##########################################',
'.....##....############....##....................####...#################################..#....',
'.....#.......############..#####..................##..###############################....##.....',
'..............############.######............#.#.#####################################..........',
'...............################.##.............######################################...........',
'...............################.................###.####..##..######################............',
'...............##############.................##..#.###.#####..####################..#..........',
'...............#############..................##..#....######.###################...##..........',
'.................##########...................#####......#######################...#............',
'.................######......................####################################...............',
'...................###....#.................#############.###..#################................',
'.....#..............##...##.................####################..############..................',
'....................####...##...............##############.####....###...###....................',
'.......................###.................##################.......#.....###...................',
'.........................#..##..............##################......#.....#.#...................',
'...........................#####.............################........#...........#..............',
'...........................#######.................##########.............##..#.................',
'...........................########...............#########................#.##.................',
'..........................############.............########................#....#...###.........',
'...........................############............#######...................##......#....#.....',
'...........................###########..............#######........................#............',
'............................##########.............########.#....................###..#.....#...',
'.............................########..............######...#...................#######.........',
'.............................########...............#####...#.................##########........',
'.............................######.................#####.....................###########.......',
'.............................######..................###.......................##########.......',
'.............................#####...................#.........................#.....###........',
'............................#####....................................................###.......#',
'............................###........................................................#........',
'............................##..............................................................##..',
'............................##....................................#.............................',
'............................##..................................................................',
'................................................................................................',
];

export const CENTROIDS: Record<string, [number, number]> = {
AE: [62.5, 16.1],
AF: [65.7, 13.3],
AL: [53.4, 11.6],
AM: [59.9, 11.6],
AO: [52.8, 25.6],
AR: [30.9, 31.3],
AT: [51.8, 9.7],
AU: [83.7, 28.8],
AZ: [60.6, 11.6],
BA: [52.8, 10.6],
BD: [71.9, 15.9],
BE: [49.3, 8.9],
BF: [47.6, 19],
BG: [54.7, 11.1],
BI: [56, 23.3],
BJ: [48.6, 19.6],
BN: [78.5, 21.2],
BO: [30.8, 26.8],
BR: [34.8, 25.6],
BS: [27.4, 15.4],
BT: [72, 15.1],
BW: [54.4, 28.3],
BY: [55.6, 8],
BZ: [24.3, 17.8],
CA: [20.8, 6.3],
CD: [54.3, 22.9],
CF: [53.6, 20.5],
CG: [52.2, 22.4],
CH: [50, 9.9],
CI: [46.5, 20.4],
CL: [28.7, 32.6],
CM: [51.3, 21.2],
CN: [76.4, 13.7],
CO: [28.5, 21.5],
CR: [25.6, 19.7],
CU: [27.2, 16.7],
CY: [56.8, 13.1],
CZ: [52.1, 9.1],
DE: [50.6, 8.8],
DJ: [59.3, 19.2],
DK: [50.4, 7.5],
DO: [29.2, 17.3],
DZ: [48.7, 15.1],
EC: [27.1, 22.7],
EE: [54.9, 6.7],
EG: [55.9, 15.4],
EH: [44.6, 16],
ER: [58.2, 18.2],
ES: [47.1, 11.7],
ET: [58.4, 20.3],
FI: [55.3, 5.5],
FJ: [95.5, 27.2],
FK: [32.3, 36.2],
FR: [48.7, 9.9],
GA: [51.2, 22.5],
GB: [47.4, 7.9],
GE: [59.7, 11.2],
GH: [47.7, 20.3],
GL: [37.5, 2.6],
GM: [44, 18.8],
GN: [45.3, 19.6],
GQ: [50.4, 21.8],
GR: [53.8, 11.9],
GT: [23.9, 18.4],
GW: [44.1, 19.2],
GY: [32.3, 21],
HN: [24.8, 18.5],
HR: [52.4, 10.2],
HT: [28.7, 17.3],
HU: [53.2, 9.8],
ID: [75.2, 22.7],
IE: [45.9, 8.2],
IL: [57.3, 14.2],
IN: [69.2, 16.4],
IQ: [59.5, 13.6],
IR: [62.6, 13.8],
IS: [43, 5.1],
IT: [51, 10.5],
JM: [27.4, 17.6],
JO: [57.7, 14.2],
JP: [84.9, 12.8],
KE: [58.1, 22.3],
KG: [67.9, 11.3],
KH: [75.9, 19],
KP: [81.7, 11.8],
KR: [82.2, 12.7],
KW: [60.6, 14.6],
KZ: [66.3, 9.3],
LA: [75.3, 17.2],
LB: [57.6, 13.3],
LK: [69.5, 20.4],
LR: [45.5, 20.7],
LS: [55.5, 30.3],
LT: [54.4, 7.7],
LU: [49.6, 9.1],
LV: [54.8, 7.2],
LY: [52.8, 15.3],
MA: [46.1, 14],
MD: [55.6, 9.8],
ME: [53.1, 11],
MG: [60.5, 27.4],
MK: [53.7, 11.3],
ML: [47.5, 17.4],
MM: [73.5, 16.6],
MN: [75.8, 10.1],
MR: [45.4, 17.2],
MW: [57, 26],
MX: [20.7, 16],
MY: [78.4, 21.7],
MZ: [58.1, 26.1],
NA: [52.6, 27.9],
NC: [92, 28],
NE: [50.5, 17.7],
NG: [50, 19.9],
NI: [25.3, 19],
NL: [49.5, 8.4],
NO: [50.6, 6],
NP: [70.3, 14.9],
NZ: [94.1, 33],
OM: [63.3, 16.5],
PA: [26.6, 20.1],
PE: [28.6, 25.9],
PG: [86.4, 23.9],
PH: [80.7, 19.4],
PK: [66.3, 14.6],
PL: [53.2, 8.5],
PR: [30.3, 17.5],
PS: [57.4, 13.9],
PT: [45.8, 11.8],
PY: [32, 28.2],
QA: [61.6, 15.7],
RO: [54.7, 10.2],
RS: [53.5, 10.6],
RU: [59.9, 6.9],
RW: [56, 22.9],
SA: [59.9, 16.1],
SB: [90.4, 24.5],
SD: [55.8, 18],
SE: [53.1, 4.8],
SI: [52, 10.1],
SK: [53.1, 9.4],
SL: [44.9, 20.1],
SN: [44.1, 18.4],
SO: [60.1, 21.4],
SR: [33.1, 21.3],
SS: [56.1, 20.5],
SV: [24.3, 18.8],
SY: [58.2, 13.1],
SZ: [56.4, 29.5],
TD: [53, 18.4],
TF: [66.4, 35.5],
TG: [48.3, 20.1],
TH: [75, 18.3],
TJ: [67.4, 12.2],
TL: [81.6, 24.7],
TM: [63.6, 11.8],
TN: [50.4, 13.4],
TR: [57.2, 11.9],
TT: [31.8, 19.5],
TW: [80.2, 16.1],
TZ: [57.3, 24],
UA: [56.6, 9.1],
UG: [56.8, 21.9],
US: [22, 11.9],
UY: [33.1, 31.2],
UZ: [65.1, 11.3],
VE: [30.8, 20.5],
VN: [76.1, 16.6],
VU: [92.5, 26.5],
XK: [53.6, 11],
YE: [60.2, 18.3],
ZA: [54.3, 30.3],
ZM: [55, 26.3],
ZW: [56, 27.4],
};
Loading
Loading