birdclaw 0.5.1 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +92 -1
- package/README.md +75 -5
- package/package.json +8 -2
- package/scripts/browser-perf.mjs +1 -0
- package/scripts/start-test-server.mjs +16 -3
- package/src/cli.ts +812 -37
- package/src/components/AccountSwitcher.tsx +186 -0
- package/src/components/AppNav.tsx +37 -7
- package/src/components/AvatarChip.tsx +9 -3
- package/src/components/DmWorkspace.tsx +18 -8
- package/src/components/LinkPreviewCard.tsx +40 -18
- package/src/components/MarkdownViewer.tsx +818 -0
- package/src/components/ProfileAnalysisStream.tsx +428 -0
- package/src/components/ProfilePreview.tsx +120 -9
- package/src/components/SavedTimelineView.tsx +30 -8
- package/src/components/SyncNowButton.tsx +60 -25
- package/src/components/ThemeSlider.tsx +55 -50
- package/src/components/TimelineCard.tsx +240 -92
- package/src/components/TimelineRouteFrame.tsx +38 -8
- package/src/components/TweetMediaGrid.tsx +87 -38
- package/src/components/TweetRichText.tsx +45 -17
- package/src/components/account-selection.ts +64 -0
- package/src/components/useTimelineRouteData.ts +97 -13
- package/src/lib/account-sync-job.ts +666 -0
- package/src/lib/actions-transport.ts +216 -146
- package/src/lib/api-client.ts +128 -53
- package/src/lib/archive-finder.ts +78 -63
- package/src/lib/archive-import.ts +1593 -1291
- package/src/lib/authored-live.ts +262 -204
- package/src/lib/avatar-cache.ts +208 -43
- package/src/lib/backup.ts +1536 -954
- package/src/lib/bird-actions.ts +139 -57
- package/src/lib/bird-command.ts +101 -28
- package/src/lib/bird.ts +582 -194
- package/src/lib/blocklist.ts +40 -23
- package/src/lib/blocks-write.ts +129 -80
- package/src/lib/blocks.ts +165 -97
- package/src/lib/bookmark-sync-job.ts +250 -160
- package/src/lib/config.ts +35 -2
- package/src/lib/conversation-surface.ts +79 -48
- package/src/lib/data-sources.ts +219 -0
- package/src/lib/db.ts +95 -4
- package/src/lib/dms-live.ts +720 -66
- package/src/lib/effect-runtime.ts +45 -0
- package/src/lib/follow-graph.ts +224 -180
- package/src/lib/geocoding.ts +296 -0
- package/src/lib/http-effect.ts +222 -0
- package/src/lib/inbox.ts +74 -43
- package/src/lib/link-index.ts +88 -76
- package/src/lib/link-insights.ts +24 -0
- package/src/lib/link-preview-metadata.ts +472 -52
- package/src/lib/location.ts +137 -0
- package/src/lib/media-fetch.ts +286 -213
- package/src/lib/mention-threads-live.ts +445 -288
- package/src/lib/mentions-live.ts +549 -354
- package/src/lib/moderation-target.ts +102 -65
- package/src/lib/moderation-write.ts +77 -18
- package/src/lib/mutes-write.ts +129 -80
- package/src/lib/mutes.ts +8 -1
- package/src/lib/network-map.ts +382 -0
- package/src/lib/openai.ts +84 -53
- package/src/lib/period-digest.ts +1317 -0
- package/src/lib/profile-affiliation-hydration.ts +93 -54
- package/src/lib/profile-analysis.ts +1272 -0
- package/src/lib/profile-bio-entities.ts +1 -1
- package/src/lib/profile-hydration.ts +124 -72
- package/src/lib/profile-replies.ts +60 -43
- package/src/lib/profile-resolver.ts +402 -294
- package/src/lib/queries.ts +983 -203
- package/src/lib/research.ts +165 -120
- package/src/lib/search-discussion.ts +1016 -0
- package/src/lib/sqlite.ts +1 -0
- package/src/lib/timeline-collections-live.ts +204 -167
- package/src/lib/timeline-live.ts +325 -51
- package/src/lib/tweet-account-edges.ts +2 -0
- package/src/lib/tweet-lookup.ts +30 -19
- package/src/lib/tweet-render.ts +141 -1
- package/src/lib/tweet-search-live.ts +565 -0
- package/src/lib/types.ts +75 -3
- package/src/lib/ui.ts +31 -8
- package/src/lib/url-expansion.ts +226 -55
- package/src/lib/url-safety.ts +220 -0
- package/src/lib/web-sync.ts +222 -149
- package/src/lib/whois.ts +166 -147
- package/src/lib/x-web.ts +102 -71
- package/src/lib/xurl-rate-limits.ts +267 -0
- package/src/lib/xurl.ts +1185 -405
- package/src/routeTree.gen.ts +273 -0
- package/src/routes/__root.tsx +24 -5
- package/src/routes/api/action.tsx +127 -78
- package/src/routes/api/avatar.tsx +39 -30
- package/src/routes/api/blocks.tsx +26 -23
- package/src/routes/api/conversation.tsx +25 -14
- package/src/routes/api/data-sources.tsx +24 -0
- package/src/routes/api/inbox.tsx +27 -21
- package/src/routes/api/link-insights.tsx +31 -25
- package/src/routes/api/link-preview.tsx +25 -21
- package/src/routes/api/network-map.tsx +55 -0
- package/src/routes/api/period-digest.tsx +133 -0
- package/src/routes/api/profile-analysis.tsx +152 -0
- package/src/routes/api/profile-hydrate.tsx +31 -28
- package/src/routes/api/query.tsx +80 -55
- package/src/routes/api/search-discussion.tsx +169 -0
- package/src/routes/api/status.tsx +18 -10
- package/src/routes/api/sync.tsx +75 -29
- package/src/routes/api/xurl-rate-limits.tsx +24 -0
- package/src/routes/data-sources.tsx +255 -0
- package/src/routes/discuss.tsx +419 -0
- package/src/routes/dms.tsx +95 -28
- package/src/routes/inbox.tsx +32 -19
- package/src/routes/network-map.tsx +1035 -0
- package/src/routes/profile-analyze.tsx +112 -0
- package/src/routes/profiles.$handle.tsx +228 -0
- package/src/routes/rate-limits.tsx +309 -0
- package/src/routes/today.tsx +455 -0
- package/src/styles.css +22 -0
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
const COMBINING_DIACRITIC_PATTERN = /[\u0300-\u036f]/g;
|
|
2
|
+
const COLLAPSED_WHITESPACE_PATTERN = /\s+/g;
|
|
3
|
+
const COORDINATE_CAPTURE_PATTERN =
|
|
4
|
+
/^\s*(-?\d+(?:\.\d+)?)\s*,\s*(-?\d+(?:\.\d+)?)\s*$/;
|
|
5
|
+
const LETTER_PATTERN = /[a-z]/i;
|
|
6
|
+
const URL_OR_HANDLE_PATTERN = /(https?:\/\/|@|[A-Z0-9._%+-]+@[A-Z0-9.-]+)/i;
|
|
7
|
+
const MONEY_PATTERN = /\$\s?\d/;
|
|
8
|
+
const EMOJI_BLOCK_PATTERN =
|
|
9
|
+
/[\u{1f1e6}-\u{1f1ff}\u{1f300}-\u{1faff}\u{2600}-\u{27bf}]/gu;
|
|
10
|
+
|
|
11
|
+
const GENERIC_LOCATIONS = new Set([
|
|
12
|
+
"earth",
|
|
13
|
+
"everywhere",
|
|
14
|
+
"internet",
|
|
15
|
+
"milky way",
|
|
16
|
+
"n/a",
|
|
17
|
+
"na",
|
|
18
|
+
"none",
|
|
19
|
+
"null",
|
|
20
|
+
"online",
|
|
21
|
+
"somewhere",
|
|
22
|
+
"space",
|
|
23
|
+
"the internet",
|
|
24
|
+
"the world",
|
|
25
|
+
"undefined",
|
|
26
|
+
"world",
|
|
27
|
+
"world wide web",
|
|
28
|
+
]);
|
|
29
|
+
|
|
30
|
+
const DROP_WORDS = new Set([
|
|
31
|
+
"a",
|
|
32
|
+
"an",
|
|
33
|
+
"and",
|
|
34
|
+
"anywhere",
|
|
35
|
+
"around",
|
|
36
|
+
"at",
|
|
37
|
+
"between",
|
|
38
|
+
"by",
|
|
39
|
+
"everywhere",
|
|
40
|
+
"global",
|
|
41
|
+
"here",
|
|
42
|
+
"in",
|
|
43
|
+
"inside",
|
|
44
|
+
"my",
|
|
45
|
+
"near",
|
|
46
|
+
"nearby",
|
|
47
|
+
"of",
|
|
48
|
+
"on",
|
|
49
|
+
"remote",
|
|
50
|
+
"somewhere",
|
|
51
|
+
"the",
|
|
52
|
+
"there",
|
|
53
|
+
"where",
|
|
54
|
+
"worldwide",
|
|
55
|
+
]);
|
|
56
|
+
|
|
57
|
+
const NON_LOCATION_PHRASES = [
|
|
58
|
+
/\b(check(out)? my|follow me|newsletter|subscribe|readers)\b/i,
|
|
59
|
+
/\b(in my (own )?head|right behind you|on my computer)\b/i,
|
|
60
|
+
/\b(dev\/null|dev null|xcode|vim|screen|framebuffer)\b/i,
|
|
61
|
+
/\b(blockchain|metaverse|omniverse|hyperamerica)\b/i,
|
|
62
|
+
];
|
|
63
|
+
|
|
64
|
+
function parseCoordinatesFromString(input: string) {
|
|
65
|
+
const match = input.match(COORDINATE_CAPTURE_PATTERN);
|
|
66
|
+
if (!match) return null;
|
|
67
|
+
const lat = Number(match[1]);
|
|
68
|
+
const lng = Number(match[2]);
|
|
69
|
+
if (!Number.isFinite(lat) || !Number.isFinite(lng)) return null;
|
|
70
|
+
if (lat < -90 || lat > 90 || lng < -180 || lng > 180) return null;
|
|
71
|
+
return { lat, lng };
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function stripJunkWords(part: string) {
|
|
75
|
+
const words = part
|
|
76
|
+
.split(" ")
|
|
77
|
+
.map((word) => word.trim())
|
|
78
|
+
.filter((word) => word.length > 0);
|
|
79
|
+
const kept = words.filter((word) => !DROP_WORDS.has(word));
|
|
80
|
+
return kept.join(" ").replace(COLLAPSED_WHITESPACE_PATTERN, " ").trim();
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export function normalizeLocationKey(input: string): string {
|
|
84
|
+
const raw = String(input || "").trim();
|
|
85
|
+
if (!raw) return "";
|
|
86
|
+
|
|
87
|
+
if (URL_OR_HANDLE_PATTERN.test(raw) || MONEY_PATTERN.test(raw)) return "";
|
|
88
|
+
for (const pattern of NON_LOCATION_PHRASES) {
|
|
89
|
+
if (pattern.test(raw)) return "";
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const coords = parseCoordinatesFromString(raw);
|
|
93
|
+
if (coords) {
|
|
94
|
+
return `coords:${Number(coords.lat.toFixed(6))},${Number(
|
|
95
|
+
coords.lng.toFixed(6),
|
|
96
|
+
)}`;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
let value = raw
|
|
100
|
+
.toLowerCase()
|
|
101
|
+
.normalize("NFKD")
|
|
102
|
+
.replace(COMBINING_DIACRITIC_PATTERN, "")
|
|
103
|
+
.replace(EMOJI_BLOCK_PATTERN, "")
|
|
104
|
+
.replace(/[|;]+/g, ",")
|
|
105
|
+
.replace(/\s+(?:and|or|\/|-)\s+/g, ",")
|
|
106
|
+
.replace(/[\\/]+/g, ",")
|
|
107
|
+
.replace(/[“”"']/g, "")
|
|
108
|
+
.replace(COLLAPSED_WHITESPACE_PATTERN, " ")
|
|
109
|
+
.replace(/\s*,\s*/g, ",")
|
|
110
|
+
.replace(/,+/g, ",")
|
|
111
|
+
.replace(/[^a-z0-9, .-]/g, "")
|
|
112
|
+
.replace(/^[,\s.-]+|[,\s.-]+$/g, "");
|
|
113
|
+
|
|
114
|
+
if (!value || GENERIC_LOCATIONS.has(value)) return "";
|
|
115
|
+
|
|
116
|
+
const parts = value
|
|
117
|
+
.split(",")
|
|
118
|
+
.map((part) => stripJunkWords(part.trim()))
|
|
119
|
+
.filter((part) => part.length > 0 && !GENERIC_LOCATIONS.has(part));
|
|
120
|
+
|
|
121
|
+
value = parts.join(",");
|
|
122
|
+
if (!value || GENERIC_LOCATIONS.has(value)) return "";
|
|
123
|
+
return value.length <= 100 ? value : "";
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export function isMeaningfulLocation(input: string): boolean {
|
|
127
|
+
const key = normalizeLocationKey(input);
|
|
128
|
+
if (!key) return false;
|
|
129
|
+
if (key.startsWith("coords:")) return true;
|
|
130
|
+
return LETTER_PATTERN.test(key) && key.length >= 2 && key.length <= 100;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export function coordinatesFromLocationKey(key: string) {
|
|
134
|
+
if (!key.startsWith("coords:")) return null;
|
|
135
|
+
const parsed = parseCoordinatesFromString(key.slice("coords:".length));
|
|
136
|
+
return parsed ? { ...parsed, provider: "coords" as const } : null;
|
|
137
|
+
}
|