birdclaw 0.8.2 → 0.8.3
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 +16 -0
- package/package.json +2 -1
- package/src/cli/command-context.ts +17 -0
- package/src/cli/register-analysis.ts +500 -0
- package/src/cli/register-compose.ts +40 -0
- package/src/cli/register-graph.ts +132 -0
- package/src/cli/register-inbox.ts +41 -0
- package/src/cli/register-storage.ts +106 -0
- package/src/cli.ts +30 -750
- package/src/components/AccountSwitcher.tsx +7 -15
- package/src/components/AvatarChip.tsx +1 -1
- package/src/components/AvatarPreload.ts +149 -0
- package/src/components/MarkdownCitations.tsx +680 -0
- package/src/components/MarkdownViewer.tsx +8 -674
- package/src/components/ProfileAnalysisClient.ts +191 -0
- package/src/components/ProfileAnalysisStream.tsx +16 -185
- package/src/components/ProfilePreview.tsx +2 -0
- package/src/components/links-controller.ts +162 -0
- package/src/components/links-model.ts +198 -0
- package/src/components/network-map-controller.ts +84 -0
- package/src/components/network-map-model.ts +255 -0
- package/src/components/useTimelineRouteData.ts +105 -235
- package/src/lib/analysis-runtime.ts +238 -0
- package/src/lib/api-client.ts +16 -215
- package/src/lib/api-contracts.ts +328 -0
- package/src/lib/archive-import-plan.ts +102 -0
- package/src/lib/archive-import.ts +170 -239
- package/src/lib/authored-live.ts +75 -120
- package/src/lib/backup.ts +335 -424
- package/src/lib/blocks-write.ts +30 -26
- package/src/lib/blocks.ts +18 -20
- package/src/lib/database-metrics.ts +88 -0
- package/src/lib/database-migrations.ts +34 -0
- package/src/lib/database-schema.ts +312 -0
- package/src/lib/database-writer.ts +69 -0
- package/src/lib/db.ts +84 -330
- package/src/lib/dm-read-model.ts +533 -0
- package/src/lib/dms-live.ts +34 -97
- package/src/lib/follow-graph.ts +17 -27
- package/src/lib/import-repository.ts +138 -0
- package/src/lib/inbox.ts +2 -1
- package/src/lib/live-sync-engine.ts +209 -0
- package/src/lib/live-transport-gateway.ts +128 -0
- package/src/lib/mention-threads-live.ts +90 -177
- package/src/lib/mentions-export.ts +1 -1
- package/src/lib/mentions-live.ts +57 -181
- package/src/lib/moderation-target.ts +15 -4
- package/src/lib/moderation-write.ts +1 -1
- package/src/lib/mutes-write.ts +30 -26
- package/src/lib/openai-response-runtime.ts +251 -0
- package/src/lib/paginated-sync.ts +93 -0
- package/src/lib/period-digest.ts +116 -304
- package/src/lib/profile-analysis.ts +36 -110
- package/src/lib/queries.ts +6 -2381
- package/src/lib/query-actions.ts +437 -0
- package/src/lib/query-client.tsx +47 -0
- package/src/lib/query-read-model-shared.ts +52 -0
- package/src/lib/query-read-models.ts +5 -0
- package/src/lib/query-resource.ts +41 -0
- package/src/lib/query-status.ts +164 -0
- package/src/lib/research.ts +1 -1
- package/src/lib/runtime-services.ts +20 -0
- package/src/lib/search-discussion.ts +75 -279
- package/src/lib/server-runtime-services.ts +30 -0
- package/src/lib/sqlite.ts +48 -12
- package/src/lib/streaming-ingestion.ts +240 -0
- package/src/lib/sync-cache.ts +6 -1
- package/src/lib/sync-plan.ts +175 -0
- package/src/lib/timeline-collections-live.ts +83 -257
- package/src/lib/timeline-live.ts +86 -236
- package/src/lib/timeline-read-model.ts +1191 -0
- package/src/lib/tweet-repository.ts +156 -0
- package/src/lib/tweet-search-live.ts +63 -167
- package/src/lib/web-sync.ts +67 -50
- package/src/lib/whois.ts +2 -1
- package/src/routes/__root.tsx +11 -8
- package/src/routes/api/action.tsx +1 -1
- package/src/routes/api/conversation.tsx +1 -1
- package/src/routes/api/query.tsx +32 -26
- package/src/routes/api/status.tsx +6 -4
- package/src/routes/api/sync.tsx +5 -2
- package/src/routes/blocks.tsx +97 -131
- package/src/routes/data-sources.tsx +17 -25
- package/src/routes/dms.tsx +167 -184
- package/src/routes/inbox.tsx +63 -57
- package/src/routes/links.tsx +31 -394
- package/src/routes/network-map.tsx +41 -344
- package/src/routes/rate-limits.tsx +17 -21
- package/src/lib/client-cache.ts +0 -109
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { Check, ChevronDown } from "lucide-react";
|
|
2
|
+
import { useQuery } from "@tanstack/react-query";
|
|
2
3
|
import type { ReactNode } from "react";
|
|
3
4
|
import { useEffect, useMemo, useRef, useState } from "react";
|
|
4
5
|
import { fetchQueryEnvelope } from "#/lib/api-client";
|
|
6
|
+
import { queryKeys } from "#/lib/query-client";
|
|
5
7
|
import type { AccountRecord } from "#/lib/types";
|
|
6
8
|
import { cx } from "#/lib/ui";
|
|
7
9
|
import { AvatarChip } from "./AvatarChip";
|
|
@@ -25,29 +27,19 @@ function avatarName(account: AccountRecord) {
|
|
|
25
27
|
}
|
|
26
28
|
|
|
27
29
|
export function AccountSwitcher({ action }: { action?: ReactNode } = {}) {
|
|
28
|
-
const [accounts, setAccounts] = useState<AccountRecord[]>([]);
|
|
29
30
|
const [open, setOpen] = useState(false);
|
|
30
31
|
const switcherRef = useRef<HTMLDivElement>(null);
|
|
32
|
+
const statusQuery = useQuery({
|
|
33
|
+
queryKey: queryKeys.status,
|
|
34
|
+
queryFn: ({ signal }) => fetchQueryEnvelope({ signal }),
|
|
35
|
+
});
|
|
36
|
+
const accounts = statusQuery.data?.accounts ?? [];
|
|
31
37
|
const selectedAccountId = useSelectedAccountId(accounts);
|
|
32
38
|
const selectedAccount = useMemo(
|
|
33
39
|
() => accounts.find((account) => account.id === selectedAccountId),
|
|
34
40
|
[accounts, selectedAccountId],
|
|
35
41
|
);
|
|
36
42
|
|
|
37
|
-
useEffect(() => {
|
|
38
|
-
let active = true;
|
|
39
|
-
fetchQueryEnvelope()
|
|
40
|
-
.then((meta) => {
|
|
41
|
-
if (active) setAccounts(meta.accounts);
|
|
42
|
-
})
|
|
43
|
-
.catch(() => {
|
|
44
|
-
if (active) setAccounts([]);
|
|
45
|
-
});
|
|
46
|
-
return () => {
|
|
47
|
-
active = false;
|
|
48
|
-
};
|
|
49
|
-
}, []);
|
|
50
|
-
|
|
51
43
|
useEffect(() => {
|
|
52
44
|
if (!open) return;
|
|
53
45
|
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { type RefObject, useEffect } from "react";
|
|
2
|
+
import { avatarPath } from "./AvatarChip";
|
|
3
|
+
|
|
4
|
+
const MAX_CONCURRENT_PRELOADS = 4;
|
|
5
|
+
const IDLE_TIMEOUT_MS = 2500;
|
|
6
|
+
const PRELOAD_ROOT_MARGIN = "800px 0px";
|
|
7
|
+
|
|
8
|
+
const queuedSources: string[] = [];
|
|
9
|
+
const knownSources = new Set<string>();
|
|
10
|
+
const activeImages = new Set<HTMLImageElement>();
|
|
11
|
+
|
|
12
|
+
let activePreloads = 0;
|
|
13
|
+
let listeningForLoad = false;
|
|
14
|
+
let idleId: number | null = null;
|
|
15
|
+
let fallbackTimerId: number | null = null;
|
|
16
|
+
|
|
17
|
+
function finishPreload(image: HTMLImageElement) {
|
|
18
|
+
if (!activeImages.delete(image)) return;
|
|
19
|
+
activePreloads -= 1;
|
|
20
|
+
drainPreloads();
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function drainPreloads() {
|
|
24
|
+
while (activePreloads < MAX_CONCURRENT_PRELOADS && queuedSources.length > 0) {
|
|
25
|
+
const src = queuedSources.shift();
|
|
26
|
+
if (!src) return;
|
|
27
|
+
|
|
28
|
+
const image = new Image();
|
|
29
|
+
activePreloads += 1;
|
|
30
|
+
activeImages.add(image);
|
|
31
|
+
image.decoding = "async";
|
|
32
|
+
image.onerror = () => finishPreload(image);
|
|
33
|
+
image.onload = () => {
|
|
34
|
+
if (typeof image.decode !== "function") {
|
|
35
|
+
finishPreload(image);
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
void image
|
|
39
|
+
.decode()
|
|
40
|
+
.catch(() => undefined)
|
|
41
|
+
.finally(() => finishPreload(image));
|
|
42
|
+
};
|
|
43
|
+
image.src = src;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function runIdlePreloads() {
|
|
48
|
+
idleId = null;
|
|
49
|
+
fallbackTimerId = null;
|
|
50
|
+
drainPreloads();
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function scheduleIdlePreloads() {
|
|
54
|
+
if (
|
|
55
|
+
queuedSources.length === 0 ||
|
|
56
|
+
idleId !== null ||
|
|
57
|
+
fallbackTimerId !== null
|
|
58
|
+
) {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const requestIdle = window.requestIdleCallback?.bind(window);
|
|
63
|
+
if (requestIdle) {
|
|
64
|
+
idleId = requestIdle(runIdlePreloads, {
|
|
65
|
+
timeout: IDLE_TIMEOUT_MS,
|
|
66
|
+
});
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
fallbackTimerId = window.setTimeout(runIdlePreloads, 0);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function handleWindowLoad() {
|
|
74
|
+
listeningForLoad = false;
|
|
75
|
+
scheduleIdlePreloads();
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function scheduleAfterPageLoad() {
|
|
79
|
+
if (document.readyState === "complete") {
|
|
80
|
+
scheduleIdlePreloads();
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
if (listeningForLoad) return;
|
|
84
|
+
listeningForLoad = true;
|
|
85
|
+
window.addEventListener("load", handleWindowLoad, { once: true });
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function queueAvatarPreload(profileId?: string, avatarUrl?: string) {
|
|
89
|
+
if (!profileId || !avatarUrl || typeof window === "undefined") return;
|
|
90
|
+
const src = avatarPath(profileId, avatarUrl);
|
|
91
|
+
if (knownSources.has(src)) return;
|
|
92
|
+
knownSources.add(src);
|
|
93
|
+
queuedSources.push(src);
|
|
94
|
+
scheduleAfterPageLoad();
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export function useAvatarPreload(
|
|
98
|
+
reference: RefObject<Element | null>,
|
|
99
|
+
profileId?: string,
|
|
100
|
+
avatarUrl?: string,
|
|
101
|
+
) {
|
|
102
|
+
useEffect(() => {
|
|
103
|
+
if (!profileId || !avatarUrl) return;
|
|
104
|
+
const node = reference.current;
|
|
105
|
+
if (!node || typeof IntersectionObserver === "undefined") {
|
|
106
|
+
queueAvatarPreload(profileId, avatarUrl);
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const observer = new IntersectionObserver(
|
|
111
|
+
(entries) => {
|
|
112
|
+
if (!entries.some((entry) => entry.isIntersecting)) return;
|
|
113
|
+
observer.disconnect();
|
|
114
|
+
queueAvatarPreload(profileId, avatarUrl);
|
|
115
|
+
},
|
|
116
|
+
{ rootMargin: PRELOAD_ROOT_MARGIN },
|
|
117
|
+
);
|
|
118
|
+
observer.observe(node);
|
|
119
|
+
return () => observer.disconnect();
|
|
120
|
+
}, [avatarUrl, profileId, reference]);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function resetPreloader() {
|
|
124
|
+
if (listeningForLoad) {
|
|
125
|
+
window.removeEventListener("load", handleWindowLoad);
|
|
126
|
+
}
|
|
127
|
+
if (idleId !== null && "cancelIdleCallback" in window) {
|
|
128
|
+
window.cancelIdleCallback(idleId);
|
|
129
|
+
}
|
|
130
|
+
if (fallbackTimerId !== null) {
|
|
131
|
+
window.clearTimeout(fallbackTimerId);
|
|
132
|
+
}
|
|
133
|
+
for (const image of activeImages) {
|
|
134
|
+
image.onload = null;
|
|
135
|
+
image.onerror = null;
|
|
136
|
+
}
|
|
137
|
+
queuedSources.length = 0;
|
|
138
|
+
knownSources.clear();
|
|
139
|
+
activeImages.clear();
|
|
140
|
+
activePreloads = 0;
|
|
141
|
+
listeningForLoad = false;
|
|
142
|
+
idleId = null;
|
|
143
|
+
fallbackTimerId = null;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export const __test__ = {
|
|
147
|
+
queueAvatarPreload,
|
|
148
|
+
resetPreloader,
|
|
149
|
+
};
|