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.
Files changed (116) hide show
  1. package/CHANGELOG.md +92 -1
  2. package/README.md +75 -5
  3. package/package.json +8 -2
  4. package/scripts/browser-perf.mjs +1 -0
  5. package/scripts/start-test-server.mjs +16 -3
  6. package/src/cli.ts +812 -37
  7. package/src/components/AccountSwitcher.tsx +186 -0
  8. package/src/components/AppNav.tsx +37 -7
  9. package/src/components/AvatarChip.tsx +9 -3
  10. package/src/components/DmWorkspace.tsx +18 -8
  11. package/src/components/LinkPreviewCard.tsx +40 -18
  12. package/src/components/MarkdownViewer.tsx +818 -0
  13. package/src/components/ProfileAnalysisStream.tsx +428 -0
  14. package/src/components/ProfilePreview.tsx +120 -9
  15. package/src/components/SavedTimelineView.tsx +30 -8
  16. package/src/components/SyncNowButton.tsx +60 -25
  17. package/src/components/ThemeSlider.tsx +55 -50
  18. package/src/components/TimelineCard.tsx +240 -92
  19. package/src/components/TimelineRouteFrame.tsx +38 -8
  20. package/src/components/TweetMediaGrid.tsx +87 -38
  21. package/src/components/TweetRichText.tsx +45 -17
  22. package/src/components/account-selection.ts +64 -0
  23. package/src/components/useTimelineRouteData.ts +97 -13
  24. package/src/lib/account-sync-job.ts +666 -0
  25. package/src/lib/actions-transport.ts +216 -146
  26. package/src/lib/api-client.ts +128 -53
  27. package/src/lib/archive-finder.ts +78 -63
  28. package/src/lib/archive-import.ts +1593 -1291
  29. package/src/lib/authored-live.ts +262 -204
  30. package/src/lib/avatar-cache.ts +208 -43
  31. package/src/lib/backup.ts +1536 -954
  32. package/src/lib/bird-actions.ts +139 -57
  33. package/src/lib/bird-command.ts +101 -28
  34. package/src/lib/bird.ts +582 -194
  35. package/src/lib/blocklist.ts +40 -23
  36. package/src/lib/blocks-write.ts +129 -80
  37. package/src/lib/blocks.ts +165 -97
  38. package/src/lib/bookmark-sync-job.ts +250 -160
  39. package/src/lib/config.ts +35 -2
  40. package/src/lib/conversation-surface.ts +79 -48
  41. package/src/lib/data-sources.ts +219 -0
  42. package/src/lib/db.ts +95 -4
  43. package/src/lib/dms-live.ts +720 -66
  44. package/src/lib/effect-runtime.ts +45 -0
  45. package/src/lib/follow-graph.ts +224 -180
  46. package/src/lib/geocoding.ts +296 -0
  47. package/src/lib/http-effect.ts +222 -0
  48. package/src/lib/inbox.ts +74 -43
  49. package/src/lib/link-index.ts +88 -76
  50. package/src/lib/link-insights.ts +24 -0
  51. package/src/lib/link-preview-metadata.ts +472 -52
  52. package/src/lib/location.ts +137 -0
  53. package/src/lib/media-fetch.ts +286 -213
  54. package/src/lib/mention-threads-live.ts +445 -288
  55. package/src/lib/mentions-live.ts +549 -354
  56. package/src/lib/moderation-target.ts +102 -65
  57. package/src/lib/moderation-write.ts +77 -18
  58. package/src/lib/mutes-write.ts +129 -80
  59. package/src/lib/mutes.ts +8 -1
  60. package/src/lib/network-map.ts +382 -0
  61. package/src/lib/openai.ts +84 -53
  62. package/src/lib/period-digest.ts +1317 -0
  63. package/src/lib/profile-affiliation-hydration.ts +93 -54
  64. package/src/lib/profile-analysis.ts +1272 -0
  65. package/src/lib/profile-bio-entities.ts +1 -1
  66. package/src/lib/profile-hydration.ts +124 -72
  67. package/src/lib/profile-replies.ts +60 -43
  68. package/src/lib/profile-resolver.ts +402 -294
  69. package/src/lib/queries.ts +983 -203
  70. package/src/lib/research.ts +165 -120
  71. package/src/lib/search-discussion.ts +1016 -0
  72. package/src/lib/sqlite.ts +1 -0
  73. package/src/lib/timeline-collections-live.ts +204 -167
  74. package/src/lib/timeline-live.ts +325 -51
  75. package/src/lib/tweet-account-edges.ts +2 -0
  76. package/src/lib/tweet-lookup.ts +30 -19
  77. package/src/lib/tweet-render.ts +141 -1
  78. package/src/lib/tweet-search-live.ts +565 -0
  79. package/src/lib/types.ts +75 -3
  80. package/src/lib/ui.ts +31 -8
  81. package/src/lib/url-expansion.ts +226 -55
  82. package/src/lib/url-safety.ts +220 -0
  83. package/src/lib/web-sync.ts +222 -149
  84. package/src/lib/whois.ts +166 -147
  85. package/src/lib/x-web.ts +102 -71
  86. package/src/lib/xurl-rate-limits.ts +267 -0
  87. package/src/lib/xurl.ts +1185 -405
  88. package/src/routeTree.gen.ts +273 -0
  89. package/src/routes/__root.tsx +24 -5
  90. package/src/routes/api/action.tsx +127 -78
  91. package/src/routes/api/avatar.tsx +39 -30
  92. package/src/routes/api/blocks.tsx +26 -23
  93. package/src/routes/api/conversation.tsx +25 -14
  94. package/src/routes/api/data-sources.tsx +24 -0
  95. package/src/routes/api/inbox.tsx +27 -21
  96. package/src/routes/api/link-insights.tsx +31 -25
  97. package/src/routes/api/link-preview.tsx +25 -21
  98. package/src/routes/api/network-map.tsx +55 -0
  99. package/src/routes/api/period-digest.tsx +133 -0
  100. package/src/routes/api/profile-analysis.tsx +152 -0
  101. package/src/routes/api/profile-hydrate.tsx +31 -28
  102. package/src/routes/api/query.tsx +80 -55
  103. package/src/routes/api/search-discussion.tsx +169 -0
  104. package/src/routes/api/status.tsx +18 -10
  105. package/src/routes/api/sync.tsx +75 -29
  106. package/src/routes/api/xurl-rate-limits.tsx +24 -0
  107. package/src/routes/data-sources.tsx +255 -0
  108. package/src/routes/discuss.tsx +419 -0
  109. package/src/routes/dms.tsx +95 -28
  110. package/src/routes/inbox.tsx +32 -19
  111. package/src/routes/network-map.tsx +1035 -0
  112. package/src/routes/profile-analyze.tsx +112 -0
  113. package/src/routes/profiles.$handle.tsx +228 -0
  114. package/src/routes/rate-limits.tsx +309 -0
  115. package/src/routes/today.tsx +455 -0
  116. 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
+ }