birdclaw 0.8.2 → 0.8.4

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 (247) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/README.md +10 -8
  3. package/bin/birdclaw.mjs +4 -66
  4. package/dist/cli/birdclaw.js +28165 -0
  5. package/dist/client/assets/AvatarChip-DLoqM5xD.js +1 -0
  6. package/dist/client/assets/ProfilePreview-BejAuYsJ.js +1 -0
  7. package/dist/client/assets/SavedTimelineView-BIS7HKpp.js +1 -0
  8. package/dist/client/assets/SmartTimestamp-BR0-9se2.js +1 -0
  9. package/dist/client/assets/TimelineRouteFrame-CyGPj0eM.js +1 -0
  10. package/dist/client/assets/blocks-7UDKQp8H.js +1 -0
  11. package/dist/client/assets/bookmarks-CbBkMvXk.js +1 -0
  12. package/dist/client/assets/chunk-C0phanGV.js +1 -0
  13. package/dist/client/assets/circle-check-C_Vq3ElD.js +1 -0
  14. package/dist/client/assets/client-stream-contracts-ys3XhWG4.js +5 -0
  15. package/dist/client/assets/createLucideIcon-JV8isFFR.js +1 -0
  16. package/dist/client/assets/data-sources-Cm2bpNvg.js +1 -0
  17. package/dist/client/assets/discuss-DTjaOugw.js +1 -0
  18. package/dist/client/assets/dms-CGd6Eogq.js +1 -0
  19. package/dist/client/assets/inbox-BHlGuOy2.js +1 -0
  20. package/dist/client/assets/index-CEIG7S6s.js +31 -0
  21. package/dist/client/assets/index-bpFrQ5yG.css +1 -0
  22. package/dist/client/assets/likes-cLrcAQem.js +1 -0
  23. package/dist/client/assets/links-CM9QO1LC.js +1 -0
  24. package/dist/client/assets/mapbox-BxTlqXt9.js +2 -0
  25. package/dist/client/assets/mapbox-gl-dgf_hX3a.js +3215 -0
  26. package/dist/client/assets/mentions-CxKCgzIf.js +1 -0
  27. package/dist/client/assets/network-map-B5a3whqj.js +2 -0
  28. package/dist/client/assets/profile-analyze-CRtNEwlb.js +1 -0
  29. package/dist/client/assets/profile-hydration-client-YEtJsNTv.js +1 -0
  30. package/dist/client/assets/profiles._handle-C82_HoKX.js +1 -0
  31. package/dist/client/assets/query-client-fYu2rzRC.js +17 -0
  32. package/dist/client/assets/rate-limits-ScaEwdVK.js +1 -0
  33. package/dist/client/assets/react-dom-DHl6Ug7F.js +1 -0
  34. package/dist/client/assets/react-nBylq1U5.js +1 -0
  35. package/dist/client/assets/repeat-2-xCwHM9hD.js +1 -0
  36. package/dist/client/assets/routes-q7ZaW4En.js +1 -0
  37. package/dist/client/assets/schemas-I9r6F6ya.js +64 -0
  38. package/dist/client/assets/search-Dq5kYsxA.js +1 -0
  39. package/dist/client/assets/sparkles-CQVlvHsZ.js +1 -0
  40. package/dist/client/assets/styles-B8s3tt3r.css +2 -0
  41. package/dist/client/assets/today-CeG1O7Py.js +1 -0
  42. package/dist/client/assets/triangle-alert-CBL7s2pf.js +1 -0
  43. package/dist/client/assets/useTimelineRouteData-1HsqgvUi.js +1 -0
  44. package/dist/client/assets/users-CHbKlqUr.js +1 -0
  45. package/dist/server/assets/AvatarChip-zUDAEmjL.js +105 -0
  46. package/dist/server/assets/FeedState-ntW0Q2GB.js +214 -0
  47. package/dist/server/assets/ProfileAnalysisStream-BFvhVdhn.js +198 -0
  48. package/dist/server/assets/ProfilePreview-C6gAor_j.js +752 -0
  49. package/dist/server/assets/SavedTimelineView-BG1jnBp1.js +64 -0
  50. package/dist/server/assets/SmartTimestamp-DfwE1NGf.js +45 -0
  51. package/dist/server/assets/SyncNowButton-BOn_IBXS.js +76 -0
  52. package/dist/server/assets/TimelineRouteFrame-CU7ilM7U.js +85 -0
  53. package/dist/server/assets/TweetRichText-DhCVv1-6.js +74 -0
  54. package/dist/server/assets/_tanstack-start-manifest_v-C-ZRnG_n.js +148 -0
  55. package/dist/server/assets/account-selection-CTdFGWRk.js +44 -0
  56. package/dist/server/assets/blocks-C1mgGTB3.js +299 -0
  57. package/dist/server/assets/blocks-DSgQghqP.js +17 -0
  58. package/dist/server/assets/bookmarks-BJN8tykY.js +14 -0
  59. package/dist/server/assets/client-stream-contracts-C2xbWVjm.js +769 -0
  60. package/dist/server/assets/data-sources-BnA1Q94p.js +180 -0
  61. package/dist/server/assets/discuss-CJ1dau1j.js +15 -0
  62. package/dist/server/assets/discuss-QTo08NCN.js +338 -0
  63. package/dist/server/assets/dms-BuLj4bp8.js +368 -0
  64. package/dist/server/assets/dms-DAyQrVM2.js +240 -0
  65. package/dist/server/assets/empty-plugin-adapters-DzrEkdBF.js +5 -0
  66. package/dist/server/assets/inbox-CkGOaW1q.js +214 -0
  67. package/dist/server/assets/inbox-DpWd1EzC.js +146 -0
  68. package/dist/server/assets/likes-DBOAtsJU.js +14 -0
  69. package/dist/server/assets/links-DAhSjynu.js +240 -0
  70. package/dist/server/assets/links-DmH5lxHO.js +468 -0
  71. package/dist/server/assets/mentions-BH2MzKtB.js +26 -0
  72. package/dist/server/assets/network-map-D3CzRcC4.js +224 -0
  73. package/dist/server/assets/network-map-D5DoLOwR.js +582 -0
  74. package/dist/server/assets/profile-analyze-BP65PED0.js +90 -0
  75. package/dist/server/assets/profile-analyze-C1NoM0tV.js +9 -0
  76. package/dist/server/assets/profile-hydration-client-GJ4kwJS8.js +133 -0
  77. package/dist/server/assets/profiles._handle-DjwlN_ac.js +184 -0
  78. package/dist/server/assets/profiles._handle-DsLNj2Ca.js +12 -0
  79. package/dist/server/assets/query-client-BdauHBgd.js +923 -0
  80. package/dist/server/assets/rate-limits-VAr2qzYD.js +262 -0
  81. package/dist/server/assets/route-search-BUlQ5lGM.js +117 -0
  82. package/dist/server/assets/router-DqkwSPiM.js +17026 -0
  83. package/dist/server/assets/routes-A9c9ZhwN.js +26 -0
  84. package/dist/server/assets/start-FaDOTmto.js +4 -0
  85. package/dist/server/assets/today-B5hVM57Z.js +17 -0
  86. package/dist/server/assets/today-fvGiZ7ud.js +303 -0
  87. package/dist/server/assets/ui-qbmcMSSX.js +137 -0
  88. package/{src/components/useDebouncedValue.ts → dist/server/assets/useDebouncedValue-DOnA6Xcy.js} +4 -4
  89. package/dist/server/assets/useTimelineRouteData-27cxynxa.js +1101 -0
  90. package/dist/server/server.js +5520 -0
  91. package/package.json +27 -34
  92. package/playwright.config.ts +0 -35
  93. package/scripts/browser-perf.mjs +0 -427
  94. package/scripts/build-docs-site.mjs +0 -967
  95. package/scripts/docs-site-assets.mjs +0 -311
  96. package/scripts/run-vitest.mjs +0 -21
  97. package/scripts/sanitize-node-options.mjs +0 -23
  98. package/scripts/start-test-server.mjs +0 -42
  99. package/src/cli-moderation.ts +0 -210
  100. package/src/cli.ts +0 -2336
  101. package/src/components/AccountSwitcher.tsx +0 -186
  102. package/src/components/AppNav.tsx +0 -118
  103. package/src/components/AvatarChip.tsx +0 -58
  104. package/src/components/BrandMark.tsx +0 -67
  105. package/src/components/ConversationThread.tsx +0 -131
  106. package/src/components/DmWorkspace.tsx +0 -286
  107. package/src/components/EmbeddedTweetCard.tsx +0 -55
  108. package/src/components/FeedState.tsx +0 -147
  109. package/src/components/FloatingPreview.tsx +0 -382
  110. package/src/components/InboxCard.tsx +0 -152
  111. package/src/components/LinkPreviewCard.tsx +0 -292
  112. package/src/components/MarkdownViewer.tsx +0 -777
  113. package/src/components/ProfileAnalysisStream.tsx +0 -428
  114. package/src/components/ProfilePreview.tsx +0 -129
  115. package/src/components/SavedTimelineView.tsx +0 -155
  116. package/src/components/SmartTimestamp.tsx +0 -72
  117. package/src/components/SyncNowButton.tsx +0 -140
  118. package/src/components/ThemeSlider.tsx +0 -80
  119. package/src/components/TimelineCard.tsx +0 -533
  120. package/src/components/TimelineRouteFrame.tsx +0 -186
  121. package/src/components/TweetArticleCard.tsx +0 -66
  122. package/src/components/TweetMediaGrid.tsx +0 -185
  123. package/src/components/TweetRichText.tsx +0 -159
  124. package/src/components/account-selection.ts +0 -64
  125. package/src/components/useTimelineRouteData.ts +0 -323
  126. package/src/lib/account-sync-job.ts +0 -666
  127. package/src/lib/actions-transport.ts +0 -300
  128. package/src/lib/api-client.ts +0 -419
  129. package/src/lib/archive-finder.ts +0 -185
  130. package/src/lib/archive-import.ts +0 -2764
  131. package/src/lib/authored-live.ts +0 -1072
  132. package/src/lib/avatar-cache.ts +0 -349
  133. package/src/lib/backup.ts +0 -2741
  134. package/src/lib/bird-actions.ts +0 -294
  135. package/src/lib/bird-command.ts +0 -130
  136. package/src/lib/bird.ts +0 -1140
  137. package/src/lib/blocklist.ts +0 -136
  138. package/src/lib/blocks-write.ts +0 -167
  139. package/src/lib/blocks.ts +0 -389
  140. package/src/lib/bookmark-sync-job.ts +0 -536
  141. package/src/lib/client-cache.ts +0 -109
  142. package/src/lib/config.ts +0 -224
  143. package/src/lib/conversation-surface.ts +0 -205
  144. package/src/lib/data-sources.ts +0 -219
  145. package/src/lib/db.ts +0 -1135
  146. package/src/lib/dms-live.ts +0 -933
  147. package/src/lib/effect-runtime.ts +0 -45
  148. package/src/lib/follow-graph.ts +0 -1097
  149. package/src/lib/geocoding.ts +0 -296
  150. package/src/lib/http-effect.ts +0 -222
  151. package/src/lib/identity-search-index.ts +0 -369
  152. package/src/lib/inbox.ts +0 -241
  153. package/src/lib/link-index.ts +0 -641
  154. package/src/lib/link-insights.ts +0 -860
  155. package/src/lib/link-preview-metadata.ts +0 -754
  156. package/src/lib/location.ts +0 -137
  157. package/src/lib/media-fetch.ts +0 -860
  158. package/src/lib/media-includes.ts +0 -165
  159. package/src/lib/mention-threads-live.ts +0 -917
  160. package/src/lib/mentions-export.ts +0 -147
  161. package/src/lib/mentions-live.ts +0 -1238
  162. package/src/lib/moderation-target.ts +0 -216
  163. package/src/lib/moderation-write.ts +0 -131
  164. package/src/lib/mutes-write.ts +0 -167
  165. package/src/lib/mutes.ts +0 -84
  166. package/src/lib/ndjson-stream.ts +0 -106
  167. package/src/lib/network-map.ts +0 -382
  168. package/src/lib/openai.ts +0 -117
  169. package/src/lib/period-digest.ts +0 -1552
  170. package/src/lib/present.ts +0 -118
  171. package/src/lib/profile-affiliation-hydration.ts +0 -228
  172. package/src/lib/profile-affiliations.ts +0 -262
  173. package/src/lib/profile-analysis.ts +0 -1272
  174. package/src/lib/profile-bio-entities.ts +0 -318
  175. package/src/lib/profile-history.ts +0 -164
  176. package/src/lib/profile-hydration.ts +0 -180
  177. package/src/lib/profile-replies.ts +0 -77
  178. package/src/lib/profile-resolver.ts +0 -650
  179. package/src/lib/queries.ts +0 -2381
  180. package/src/lib/research.ts +0 -652
  181. package/src/lib/search-discussion.ts +0 -1016
  182. package/src/lib/seed.ts +0 -613
  183. package/src/lib/sqlite.ts +0 -147
  184. package/src/lib/sync-cache.ts +0 -65
  185. package/src/lib/theme-transition.ts +0 -113
  186. package/src/lib/theme.tsx +0 -165
  187. package/src/lib/timeline-collections-live.ts +0 -581
  188. package/src/lib/timeline-live.ts +0 -452
  189. package/src/lib/tweet-account-edges.ts +0 -48
  190. package/src/lib/tweet-lookup.ts +0 -46
  191. package/src/lib/tweet-render.ts +0 -430
  192. package/src/lib/tweet-search-live.ts +0 -566
  193. package/src/lib/types.ts +0 -723
  194. package/src/lib/ui.ts +0 -529
  195. package/src/lib/url-expansion-store.ts +0 -110
  196. package/src/lib/url-expansion.ts +0 -343
  197. package/src/lib/url-safety.ts +0 -220
  198. package/src/lib/web-sync.ts +0 -516
  199. package/src/lib/whois.ts +0 -1079
  200. package/src/lib/x-profile.ts +0 -365
  201. package/src/lib/x-web.ts +0 -199
  202. package/src/lib/xurl-rate-limits.ts +0 -267
  203. package/src/lib/xurl.ts +0 -1677
  204. package/src/routeTree.gen.ts +0 -723
  205. package/src/router.tsx +0 -20
  206. package/src/routes/__root.tsx +0 -79
  207. package/src/routes/api/action.tsx +0 -147
  208. package/src/routes/api/avatar.tsx +0 -50
  209. package/src/routes/api/blocks.tsx +0 -35
  210. package/src/routes/api/conversation.tsx +0 -45
  211. package/src/routes/api/data-sources.tsx +0 -24
  212. package/src/routes/api/inbox.tsx +0 -43
  213. package/src/routes/api/link-insights.tsx +0 -85
  214. package/src/routes/api/link-preview.tsx +0 -47
  215. package/src/routes/api/network-map.tsx +0 -55
  216. package/src/routes/api/period-digest.tsx +0 -101
  217. package/src/routes/api/profile-analysis.tsx +0 -97
  218. package/src/routes/api/profile-hydrate.tsx +0 -54
  219. package/src/routes/api/query.tsx +0 -115
  220. package/src/routes/api/search-discussion.tsx +0 -113
  221. package/src/routes/api/status.tsx +0 -28
  222. package/src/routes/api/sync.tsx +0 -105
  223. package/src/routes/api/xurl-rate-limits.tsx +0 -24
  224. package/src/routes/blocks.tsx +0 -377
  225. package/src/routes/bookmarks.tsx +0 -18
  226. package/src/routes/data-sources.tsx +0 -257
  227. package/src/routes/discuss.tsx +0 -419
  228. package/src/routes/dms.tsx +0 -432
  229. package/src/routes/inbox.tsx +0 -226
  230. package/src/routes/index.tsx +0 -32
  231. package/src/routes/likes.tsx +0 -18
  232. package/src/routes/links.tsx +0 -994
  233. package/src/routes/mentions.tsx +0 -32
  234. package/src/routes/network-map.tsx +0 -1035
  235. package/src/routes/profile-analyze.tsx +0 -112
  236. package/src/routes/profiles.$handle.tsx +0 -228
  237. package/src/routes/rate-limits.tsx +0 -309
  238. package/src/routes/today.tsx +0 -511
  239. package/src/styles.css +0 -257
  240. package/tsconfig.json +0 -29
  241. package/vite.config.ts +0 -29
  242. /package/{public → dist/client}/birdclaw-mark.png +0 -0
  243. /package/{public → dist/client}/favicon.ico +0 -0
  244. /package/{public → dist/client}/logo192.png +0 -0
  245. /package/{public → dist/client}/logo512.png +0 -0
  246. /package/{public → dist/client}/manifest.json +0 -0
  247. /package/{public → dist/client}/robots.txt +0 -0
@@ -0,0 +1,26 @@
1
+ import { t as TimelineRouteFrame } from "./TimelineRouteFrame-CU7ilM7U.js";
2
+ import { jsx } from "react/jsx-runtime";
3
+ //#region src/routes/index.tsx?tsr-split=component
4
+ function homeSubtitle(meta) {
5
+ if (!meta) return "Loading local context...";
6
+ return `${String(meta.stats.home)} items · ${String(meta.stats.needsReply)} waiting · ${meta.transport.statusText}`;
7
+ }
8
+ function HomeRoute() {
9
+ return /* @__PURE__ */ jsx(TimelineRouteFrame, {
10
+ emptyDetail: "Try a different filter or sync the timeline again.",
11
+ emptyLabel: "No posts in this view",
12
+ errorFallback: "Timeline unavailable",
13
+ errorTitle: "Could not load posts",
14
+ initialReplyFilter: "all",
15
+ loadingDetail: "Reading the local timeline store",
16
+ loadingLabel: "Loading posts",
17
+ resource: "home",
18
+ searchPlaceholder: "Search local timeline",
19
+ subtitle: homeSubtitle,
20
+ syncKind: "timeline",
21
+ syncLabel: "Sync timeline",
22
+ title: "Home"
23
+ });
24
+ }
25
+ //#endregion
26
+ export { HomeRoute as component };
@@ -0,0 +1,4 @@
1
+ //#region node_modules/.pnpm/@tanstack+react-start@1.168.26_react-dom@19.2.7_react@19.2.7__react@19.2.7_vite@8.0.16__465b94eb4096202468ea7bb8157eb6f6/node_modules/@tanstack/react-start/dist/plugin/default-entry/start.ts
2
+ var startInstance = void 0;
3
+ //#endregion
4
+ export { startInstance };
@@ -0,0 +1,17 @@
1
+ import "./ui-qbmcMSSX.js";
2
+ import "./client-stream-contracts-C2xbWVjm.js";
3
+ import "./profile-hydration-client-GJ4kwJS8.js";
4
+ import { s as validateTodaySearch } from "./route-search-BUlQ5lGM.js";
5
+ import "react";
6
+ import { createFileRoute, lazyRouteComponent } from "@tanstack/react-router";
7
+ import "react/jsx-runtime";
8
+ import "lucide-react";
9
+ import "@tanstack/react-query";
10
+ //#region src/routes/today.tsx
11
+ var $$splitComponentImporter = () => import("./today-fvGiZ7ud.js");
12
+ var Route$1 = createFileRoute("/today")({
13
+ component: lazyRouteComponent($$splitComponentImporter, "component"),
14
+ validateSearch: validateTodaySearch
15
+ });
16
+ //#endregion
17
+ export { Route$1 as t };
@@ -0,0 +1,303 @@
1
+ import { Dt as pageSubtitleClass, Et as pageHeaderRowClass, Kt as segmentClass, Ot as pageTitleClass, Tt as pageHeaderClass, Wt as secondaryButtonClass, m as cx, q as errorCopyClass, qt as segmentedClass, wt as pageHeaderActionsClass } from "./ui-qbmcMSSX.js";
2
+ import { a as useNdjsonRun, n as periodDigestStreamEventSchema, o as MarkdownViewer, t as isTerminalStreamEvent } from "./client-stream-contracts-C2xbWVjm.js";
3
+ import { n as normalizeProfileHydrationHandle, t as hydrateProfileHandles } from "./profile-hydration-client-GJ4kwJS8.js";
4
+ import { s as validateTodaySearch } from "./route-search-BUlQ5lGM.js";
5
+ import { t as Route$1 } from "./today-B5hVM57Z.js";
6
+ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
7
+ import { jsx, jsxs } from "react/jsx-runtime";
8
+ import { CheckCircle2, Loader2, RefreshCw, Sparkles } from "lucide-react";
9
+ import { useQueryClient } from "@tanstack/react-query";
10
+ //#region src/routes/today.tsx?tsr-split=component
11
+ var PROFILE_HYDRATION_LIMIT = 12;
12
+ var PROFILE_HYDRATION_DELAY_MS = 300;
13
+ var DIGEST_STATUS_MESSAGES = { 524: "Digest startup timed out at Cloudflare (524). Retry to open a new stream." };
14
+ var periods = [
15
+ {
16
+ value: "today",
17
+ label: "Today"
18
+ },
19
+ {
20
+ value: "24h",
21
+ label: "24h"
22
+ },
23
+ {
24
+ value: "yesterday",
25
+ label: "Yesterday"
26
+ },
27
+ {
28
+ value: "week",
29
+ label: "Week"
30
+ }
31
+ ];
32
+ function digestUrl(period, includeDms, refresh) {
33
+ const url = new URL("/api/period-digest", window.location.origin);
34
+ url.searchParams.set("period", period);
35
+ url.searchParams.set("includeDms", String(includeDms));
36
+ url.searchParams.set("maxTweets", "5000");
37
+ url.searchParams.set("maxLinks", "20");
38
+ url.searchParams.set("liveSync", "false");
39
+ if (refresh) url.searchParams.set("refresh", "true");
40
+ return url;
41
+ }
42
+ function digestStreamError(cause, phase) {
43
+ const message = cause instanceof Error ? cause.message : String(cause);
44
+ if (cause instanceof TypeError && /network error|failed to fetch|load failed/i.test(message)) return `Digest connection was interrupted while ${phase.toLowerCase()}. Retry to continue.`;
45
+ if (cause instanceof SyntaxError) return `Digest stream returned invalid data while ${phase.toLowerCase()}. Retry to continue.`;
46
+ return message || "Digest failed";
47
+ }
48
+ function formatCounts(context) {
49
+ if (!context) return "Local Twitter memory, summarized as it streams.";
50
+ const counts = context.counts;
51
+ return [
52
+ `${String(counts.home)} home`,
53
+ `${String(counts.mentions)} mentions`,
54
+ `${String(counts.links)} links`,
55
+ context.includeDms ? `${String(counts.dms)} DMs` : null
56
+ ].filter(Boolean).join(" · ");
57
+ }
58
+ function collectProfilesForHydration(result) {
59
+ const handles = /* @__PURE__ */ new Set();
60
+ const tweetIds = /* @__PURE__ */ new Set();
61
+ for (const id of result.digest.sourceTweetIds) tweetIds.add(id);
62
+ for (const topic of result.digest.keyTopics) for (const id of topic.tweetIds) tweetIds.add(id);
63
+ for (const link of result.digest.notableLinks) for (const id of link.sourceTweetIds) tweetIds.add(id);
64
+ for (const item of result.digest.actionItems) if (item.tweetId) tweetIds.add(item.tweetId);
65
+ const tweetsById = new Map(result.context.tweets.flatMap((tweet) => [[tweet.id, tweet], [`tweet_${tweet.id}`, tweet]]));
66
+ for (const id of tweetIds) {
67
+ const tweet = tweetsById.get(id);
68
+ if (!tweet) continue;
69
+ const handle = normalizeProfileHydrationHandle(tweet.author);
70
+ if (handle) handles.add(handle);
71
+ }
72
+ for (const tweet of result.context.tweets) {
73
+ const handle = normalizeProfileHydrationHandle(tweet.author);
74
+ if (handle) handles.add(handle);
75
+ }
76
+ return [...handles];
77
+ }
78
+ function applyHydratedProfilesToContext(context, profilesByHandle) {
79
+ let changed = false;
80
+ const tweets = context.tweets.map((tweet) => {
81
+ const profile = profilesByHandle.get(normalizeProfileHydrationHandle(tweet.author));
82
+ if (!profile || profile === tweet.authorProfile) return tweet;
83
+ changed = true;
84
+ return {
85
+ ...tweet,
86
+ author: profile.handle,
87
+ name: profile.displayName,
88
+ authorProfile: profile
89
+ };
90
+ });
91
+ return changed ? {
92
+ ...context,
93
+ tweets
94
+ } : context;
95
+ }
96
+ function applyHydratedProfilesToResult(result, profiles) {
97
+ const profilesByHandle = new Map(profiles.map((profile) => [normalizeProfileHydrationHandle(profile.handle), profile]));
98
+ if (profilesByHandle.size === 0) return result;
99
+ const context = applyHydratedProfilesToContext(result.context, profilesByHandle);
100
+ return context === result.context ? result : {
101
+ ...result,
102
+ context
103
+ };
104
+ }
105
+ function useDigestStream(period, includeDms) {
106
+ const queryClient = useQueryClient();
107
+ const [markdown, setMarkdown] = useState("");
108
+ const [context, setContext] = useState(null);
109
+ const [result, setResult] = useState(null);
110
+ const [status, setStatus] = useState("Starting digest");
111
+ const latestStatusRef = useRef("Starting digest");
112
+ const onStart = useCallback(() => {
113
+ setMarkdown("");
114
+ setContext(null);
115
+ setResult(null);
116
+ setStatus("Starting digest");
117
+ latestStatusRef.current = "Starting digest";
118
+ }, []);
119
+ const { error, loading, run } = useNdjsonRun({
120
+ schema: periodDigestStreamEventSchema,
121
+ request: useCallback((signal, refresh) => fetch(digestUrl(period, includeDms, refresh), {
122
+ cache: "no-store",
123
+ signal
124
+ }), [includeDms, period]),
125
+ onStart,
126
+ onEvent: useCallback((event) => {
127
+ if (event.type === "status") {
128
+ latestStatusRef.current = event.detail ? `${event.label} · ${event.detail}` : event.label;
129
+ setStatus(latestStatusRef.current);
130
+ } else if (event.type === "start") setContext(event.context);
131
+ else if (event.type === "delta") {
132
+ latestStatusRef.current = "Streaming AI summary";
133
+ setStatus(latestStatusRef.current);
134
+ setMarkdown((current) => current + event.delta);
135
+ } else if (event.type === "done") {
136
+ setResult(event.result);
137
+ setContext(event.result.context);
138
+ setMarkdown(event.result.markdown);
139
+ setStatus(event.result.cached ? "Loaded cached report" : "Ready");
140
+ } else if (event.type === "error") throw new Error(event.error);
141
+ }, []),
142
+ onError: useCallback(() => setStatus("Digest failed"), []),
143
+ isTerminal: isTerminalStreamEvent,
144
+ errorLabel: "Digest request failed",
145
+ emptyBodyMessage: "Digest request failed: empty response body",
146
+ prematureEofError: useCallback(() => /* @__PURE__ */ new Error(`Digest connection closed while ${latestStatusRef.current.toLowerCase()}. Retry to continue.`), []),
147
+ formatError: useCallback((cause) => digestStreamError(cause, latestStatusRef.current), []),
148
+ statusMessages: DIGEST_STATUS_MESSAGES
149
+ });
150
+ useEffect(() => {
151
+ run(false);
152
+ }, [run]);
153
+ useEffect(() => {
154
+ if (!result) return;
155
+ const handles = collectProfilesForHydration(result);
156
+ if (handles.length === 0) return;
157
+ let active = true;
158
+ let idleId = null;
159
+ const runHydration = () => {
160
+ hydrateProfileHandles(queryClient, handles, { limit: PROFILE_HYDRATION_LIMIT }).then((response) => {
161
+ if (!active) return;
162
+ const { profiles } = response;
163
+ if (profiles.length === 0) return;
164
+ setResult((current) => current ? applyHydratedProfilesToResult(current, profiles) : current);
165
+ const profilesByHandle = new Map(profiles.map((profile) => [normalizeProfileHydrationHandle(profile.handle), profile]));
166
+ setContext((current) => current ? applyHydratedProfilesToContext(current, profilesByHandle) : current);
167
+ }).catch((error) => {
168
+ if (!active) return;
169
+ console.warn("Profile hydration failed", error);
170
+ });
171
+ };
172
+ const timer = window.setTimeout(() => {
173
+ if ("requestIdleCallback" in window) idleId = window.requestIdleCallback(runHydration, { timeout: 2500 });
174
+ else runHydration();
175
+ }, PROFILE_HYDRATION_DELAY_MS);
176
+ return () => {
177
+ active = false;
178
+ window.clearTimeout(timer);
179
+ if (idleId !== null && "cancelIdleCallback" in window) window.cancelIdleCallback(idleId);
180
+ };
181
+ }, [queryClient, result]);
182
+ return {
183
+ context,
184
+ error,
185
+ loading,
186
+ markdown,
187
+ result,
188
+ run,
189
+ status
190
+ };
191
+ }
192
+ function TodayRoute() {
193
+ const search = Route$1.useSearch();
194
+ const navigate = Route$1.useNavigate();
195
+ return /* @__PURE__ */ jsx(TodayRouteView, {
196
+ searchState: search,
197
+ onSearchChange: (next, options) => void navigate({
198
+ search: next,
199
+ replace: options?.replace
200
+ })
201
+ });
202
+ }
203
+ function TodayRouteView({ searchState: controlledSearch, onSearchChange } = {}) {
204
+ const [localSearch, setLocalSearch] = useState(() => validateTodaySearch({}));
205
+ const searchState = controlledSearch ?? localSearch;
206
+ const updateSearch = (next, options) => onSearchChange ? onSearchChange(next, options) : setLocalSearch(next);
207
+ const { period, includeDms } = searchState;
208
+ const { context, error, loading, markdown, result, run, status } = useDigestStream(period, includeDms);
209
+ const sourceLabel = useMemo(() => formatCounts(result?.context ?? context), [context, result]);
210
+ return /* @__PURE__ */ jsxs("div", {
211
+ className: "flex min-h-screen flex-col",
212
+ children: [
213
+ /* @__PURE__ */ jsxs("header", {
214
+ className: pageHeaderClass,
215
+ children: [/* @__PURE__ */ jsxs("div", {
216
+ className: pageHeaderRowClass,
217
+ children: [/* @__PURE__ */ jsxs("div", {
218
+ className: "min-w-0",
219
+ children: [/* @__PURE__ */ jsx("h1", {
220
+ className: pageTitleClass,
221
+ children: "What happened"
222
+ }), /* @__PURE__ */ jsx("p", {
223
+ className: pageSubtitleClass,
224
+ children: sourceLabel
225
+ })]
226
+ }), /* @__PURE__ */ jsx("div", {
227
+ className: pageHeaderActionsClass,
228
+ children: /* @__PURE__ */ jsxs("button", {
229
+ type: "button",
230
+ className: secondaryButtonClass,
231
+ onClick: () => run(true),
232
+ disabled: loading,
233
+ children: [/* @__PURE__ */ jsx(RefreshCw, {
234
+ className: cx("size-4", loading && "animate-spin"),
235
+ "aria-hidden": "true"
236
+ }), "Refresh"]
237
+ })
238
+ })]
239
+ }), /* @__PURE__ */ jsxs("div", {
240
+ className: "flex flex-wrap items-center gap-2 px-4 pb-3",
241
+ children: [/* @__PURE__ */ jsx("div", {
242
+ className: segmentedClass,
243
+ "aria-label": "Digest period",
244
+ children: periods.map((item) => /* @__PURE__ */ jsx("button", {
245
+ type: "button",
246
+ className: cx(segmentClass, period === item.value && "bg-[var(--bg-active)] text-[var(--ink)]"),
247
+ onClick: () => updateSearch({
248
+ ...searchState,
249
+ period: item.value
250
+ }),
251
+ children: item.label
252
+ }, item.value))
253
+ }), /* @__PURE__ */ jsxs("label", {
254
+ className: "inline-flex items-center gap-2 rounded-full border border-[var(--line)] px-3 py-1 text-[13px] font-medium text-[var(--ink-soft)]",
255
+ children: [/* @__PURE__ */ jsx("input", {
256
+ type: "checkbox",
257
+ checked: includeDms,
258
+ onChange: (event) => updateSearch({
259
+ ...searchState,
260
+ includeDms: event.currentTarget.checked
261
+ })
262
+ }), "DMs"]
263
+ })]
264
+ })]
265
+ }),
266
+ error ? /* @__PURE__ */ jsxs("div", {
267
+ className: cx(errorCopyClass, "flex items-center justify-between gap-3"),
268
+ role: "alert",
269
+ children: [/* @__PURE__ */ jsx("span", { children: error }), /* @__PURE__ */ jsx("button", {
270
+ className: "shrink-0 font-semibold underline underline-offset-2",
271
+ onClick: () => run(true),
272
+ type: "button",
273
+ children: "Retry"
274
+ })]
275
+ }) : null,
276
+ /* @__PURE__ */ jsx("div", {
277
+ className: "border-b border-[var(--line)] px-4 py-2 text-[13px] text-[var(--ink-soft)]",
278
+ children: /* @__PURE__ */ jsxs("span", {
279
+ className: "inline-flex items-center gap-1",
280
+ children: [loading ? /* @__PURE__ */ jsx(Loader2, {
281
+ className: "size-4 animate-spin",
282
+ "aria-hidden": "true"
283
+ }) : markdown ? /* @__PURE__ */ jsx(CheckCircle2, {
284
+ className: "size-4",
285
+ "aria-hidden": "true"
286
+ }) : /* @__PURE__ */ jsx(Sparkles, {
287
+ className: "size-4",
288
+ "aria-hidden": "true"
289
+ }), loading ? status : result ? `${result.cached ? "Cached" : "Ready"} · ${result.context.window.label}` : error ? "Digest failed" : "Ready"]
290
+ })
291
+ }),
292
+ markdown ? /* @__PURE__ */ jsx(MarkdownViewer, {
293
+ context: result?.context ?? context,
294
+ markdown
295
+ }) : /* @__PURE__ */ jsx("div", {
296
+ className: "px-4 py-5 text-[14px] text-[var(--ink-soft)]",
297
+ children: loading ? status : error ? "No digest was generated. Retry to start a new run." : "Waiting for the first tokens..."
298
+ })
299
+ ]
300
+ });
301
+ }
302
+ //#endregion
303
+ export { TodayRouteView, TodayRoute as component };
@@ -0,0 +1,137 @@
1
+ //#region src/lib/ui.ts
2
+ function cx(...values) {
3
+ return values.filter(Boolean).join(" ");
4
+ }
5
+ var bodyClass = "min-h-screen bg-[var(--bg)] font-sans text-[var(--ink)] antialiased text-[15px] leading-[1.4]";
6
+ var siteShellClass = "mx-auto flex min-h-screen w-full max-w-[1280px] gap-0";
7
+ var sidebarShellClass = "sticky top-0 z-30 flex h-screen w-[72px] shrink-0 flex-col justify-between border-r border-[var(--line)] bg-[var(--bg)] px-2 py-3 min-[1100px]:w-[260px] min-[1100px]:px-3";
8
+ var sidebarShellCompactClass = "sticky top-0 z-30 flex h-screen w-[72px] shrink-0 flex-col justify-between border-r border-[var(--line)] bg-[var(--bg)] px-2 py-3";
9
+ var sidebarBrandClass = "flex items-center gap-2.5 px-2 py-2 text-[var(--ink)] min-[1100px]:px-3";
10
+ var sidebarBrandMarkClass = "grid size-10 place-items-center";
11
+ var sidebarBrandCopyClass = "hidden flex-col leading-tight min-[1100px]:flex";
12
+ var sidebarBrandCopyCompactClass = "sr-only";
13
+ var sidebarBrandTitleClass = "text-[15px] font-bold tracking-tight";
14
+ var sidebarBrandTaglineClass = "text-[12px] text-[var(--ink-soft)]";
15
+ var sidebarNavClass = "mt-2 flex flex-col gap-0.5";
16
+ var sidebarFooterClass = "flex flex-col gap-2 pb-1";
17
+ var navLinkClass = "nav-link group flex items-center justify-center gap-4 rounded-full px-3 py-2.5 text-[15px] text-[var(--ink)] transition-colors duration-150 hover:bg-[var(--bg-hover)] min-[1100px]:justify-start";
18
+ var navLinkCompactClass = "nav-link group flex items-center justify-center rounded-full px-3 py-2.5 text-[15px] text-[var(--ink)] transition-colors duration-150 hover:bg-[var(--bg-hover)]";
19
+ var navLinkActiveClass = "nav-link-active font-bold";
20
+ var navLinkIconClass = "shrink-0";
21
+ var navLinkLabelClass = "sr-only min-[1100px]:not-sr-only";
22
+ var navLinkLabelCompactClass = "sr-only";
23
+ var mainColumnClass = "flex w-full min-w-0 max-w-[680px] flex-1 flex-col border-x border-[var(--line)] bg-[var(--bg)]";
24
+ var mainColumnDmClass = "flex w-full min-w-0 flex-1 flex-col border-x border-[var(--line)] bg-[var(--bg)]";
25
+ var pageHeaderClass = "sticky top-0 z-20 flex flex-col gap-0 border-b border-[var(--line)] bg-[color:color-mix(in_srgb,var(--bg)_85%,transparent)] backdrop-blur";
26
+ var pageHeaderRowClass = "flex items-center justify-between gap-3 px-4 pt-3 pb-2";
27
+ var pageTitleClass = "m-0 text-[20px] font-bold tracking-tight";
28
+ var pageSubtitleClass = "text-[13px] text-[var(--ink-soft)]";
29
+ var pageHeaderActionsClass = "flex items-center gap-2";
30
+ var tabStripClass = "flex w-full items-stretch border-b border-[var(--line)] overflow-x-auto";
31
+ var tabButtonClass = "relative flex flex-1 min-w-[88px] items-center justify-center px-3 py-3.5 text-[14px] font-medium text-[var(--ink-soft)] transition-colors duration-150 hover:bg-[var(--bg-hover)]";
32
+ var tabButtonActiveClass = "text-[var(--ink)] font-bold";
33
+ var tabButtonIndicatorClass = "pointer-events-none absolute bottom-0 left-1/2 h-[3px] w-12 -translate-x-1/2 rounded-full bg-[var(--accent)]";
34
+ var feedClass = "flex flex-col";
35
+ var feedRowClass = "flex w-full gap-3 border-b border-[var(--line)] px-4 py-3 transition-colors duration-150 hover:bg-[var(--bg-hover)]";
36
+ var feedRowBodyClass = "flex min-w-0 flex-1 flex-col gap-1";
37
+ var feedRowHeaderClass = "flex min-w-0 items-center gap-1.5 text-[15px]";
38
+ var feedRowNameClass = "truncate font-bold text-[var(--ink)] hover:underline";
39
+ var feedRowHandleClass = "truncate text-[var(--ink-soft)]";
40
+ var feedRowDotClass = "text-[var(--ink-soft)]";
41
+ var feedRowTimestampClass = "shrink-0 text-[var(--ink-soft)] hover:underline";
42
+ var feedRowActionsClass = "mt-2 flex max-w-md items-center justify-between text-[var(--ink-soft)]";
43
+ var feedActionButtonClass = "action-button group/action inline-flex items-center gap-1 rounded-full border-0 bg-transparent px-2 py-1 text-[13px] text-[var(--ink-soft)] transition-colors duration-150 hover:text-[var(--accent)] disabled:cursor-default disabled:opacity-55";
44
+ var feedActionIconWrapClass = "grid size-[34px] place-items-center rounded-full transition-colors duration-150 group-hover/action:bg-[var(--accent-soft)]";
45
+ var feedActionIconClass = "size-[18px]";
46
+ var feedRowTextClass = "whitespace-pre-wrap break-words text-[15px] leading-[1.45] text-[var(--ink)] [overflow-wrap:anywhere]";
47
+ var feedRowStatePillClass = "inline-flex shrink-0 items-center gap-1 rounded-full border border-[var(--line)] bg-[var(--bg-active)] px-2 py-0.5 text-[12px] font-semibold text-[var(--ink-soft)]";
48
+ var feedRowStatePillActiveClass = "border-[color:color-mix(in_srgb,var(--accent)_35%,var(--line))] bg-[var(--accent-soft)] text-[var(--accent)]";
49
+ var feedRowStatePillOpenClass = "border-[color:color-mix(in_srgb,var(--ink-soft)_34%,var(--line))] bg-transparent text-[var(--ink-soft)]";
50
+ var searchFieldShellClass = "flex items-center gap-2 rounded-full border border-transparent bg-[var(--bg-active)] px-4 py-2 transition-colors focus-within:border-[var(--accent)] focus-within:bg-[var(--bg)] focus-within:shadow-[0_0_0_1px_var(--accent)]";
51
+ var searchFieldIconClass = "size-[18px] text-[var(--ink-soft)] shrink-0";
52
+ var searchFieldInputClass = "min-w-0 flex-1 border-0 bg-transparent text-[14px] text-[var(--ink)] outline-none placeholder:text-[var(--ink-soft)]";
53
+ var textFieldClass = "w-full rounded-md border border-[var(--line)] bg-[var(--bg)] px-3 py-2 text-[14px] text-[var(--ink)] outline-none transition-colors duration-150 placeholder:text-[var(--ink-soft)] focus:border-[var(--accent)] focus:shadow-[0_0_0_1px_var(--accent)] disabled:cursor-default disabled:opacity-55";
54
+ var textFieldShortClass = "w-[120px]";
55
+ var selectFieldClass = cx(textFieldClass, "appearance-none pr-8");
56
+ var primaryButtonClass = "inline-flex items-center justify-center gap-1.5 rounded-full border-0 bg-[var(--accent)] px-4 py-1.5 text-[14px] font-bold text-white shadow-none transition-colors duration-150 hover:bg-[var(--accent-hover)] active:bg-[var(--accent-press)] disabled:cursor-default disabled:opacity-55";
57
+ var secondaryButtonClass = "inline-flex items-center justify-center gap-1.5 rounded-full border border-[var(--line-strong)] bg-transparent px-4 py-1.5 text-[14px] font-bold text-[var(--ink)] transition-colors duration-150 hover:bg-[var(--bg-hover)] disabled:cursor-default disabled:opacity-55";
58
+ var dangerButtonClass = "inline-flex items-center justify-center gap-1.5 rounded-full border border-[var(--alert)] bg-transparent px-4 py-1.5 text-[14px] font-bold text-[var(--alert)] transition-colors duration-150 hover:bg-[var(--alert-soft)] disabled:cursor-default disabled:opacity-55";
59
+ var segmentedClass = "inline-flex shrink-0 items-center gap-1 rounded-full border border-[var(--line)] bg-[var(--bg)] p-1";
60
+ var segmentClass = "rounded-full border-0 bg-transparent px-3 py-1 text-[13px] font-medium text-[var(--ink-soft)] capitalize transition-colors duration-150";
61
+ var segmentActiveClass = "bg-[var(--bg-active)] text-[var(--ink)]";
62
+ var composerShellClass = "mt-2 flex flex-col gap-2 border-t border-[var(--line)] pt-3";
63
+ var composerInputClass = "min-h-[88px] w-full resize-y rounded-md border border-[var(--line)] bg-[var(--bg)] px-3 py-2.5 text-[15px] text-[var(--ink)] outline-none transition-colors duration-150 placeholder:text-[var(--ink-soft)] focus:border-[var(--accent)] focus:shadow-[0_0_0_1px_var(--accent)]";
64
+ var composerBarClass = "flex items-center justify-between gap-3";
65
+ var emptyStateClass = "px-6 py-10 text-center text-[var(--ink-soft)]";
66
+ var errorCopyClass = "mx-4 my-3 whitespace-pre-wrap break-words rounded-md border border-[var(--alert)] bg-[var(--alert-soft)] px-3 py-2 text-[14px] text-[var(--alert)]";
67
+ var statusCopyClass = "mx-4 my-3 rounded-md border border-[var(--line)] bg-[var(--bg-elevated)] px-3 py-2 text-[13px] text-[var(--ink-soft)]";
68
+ var avatarChipClass = "avatar-chip inline-grid size-10 shrink-0 place-items-center overflow-hidden rounded-full text-[14px] font-bold text-white";
69
+ var avatarChipLargeClass = "avatar-chip-large size-16 text-[18px]";
70
+ var avatarChipSmallClass = "avatar-chip-small size-8 text-[12px]";
71
+ var embeddedCardClass = "mt-2 overflow-hidden rounded-2xl border border-[var(--line)] transition-colors duration-150 hover:bg-[var(--bg-hover)]";
72
+ var embeddedCardBodyClass = "flex flex-col gap-2 px-3 py-2.5";
73
+ var embeddedCardHeaderClass = "flex items-center gap-1.5 text-[14px]";
74
+ var embeddedCardNameClass = "font-bold text-[var(--ink)] truncate";
75
+ var embeddedCardHandleClass = "text-[var(--ink-soft)] truncate";
76
+ var embeddedCardLabelClass = "text-[12px] uppercase tracking-wide text-[var(--ink-soft)]";
77
+ var embeddedCardCopyClass = "whitespace-pre-wrap text-[14px] leading-[1.4] text-[var(--ink)] [overflow-wrap:anywhere]";
78
+ var linkPreviewCardClass = "group/link-preview mt-2 flex min-w-0 overflow-hidden rounded-2xl border border-[var(--line)] bg-[var(--bg-card)] transition-colors duration-150 hover:bg-[var(--bg-hover)]";
79
+ var linkPreviewTitleClass = "line-clamp-2 font-bold leading-tight text-[var(--ink)]";
80
+ var linkPreviewDescClass = "line-clamp-2 text-[14px] leading-snug text-[var(--ink-soft)]";
81
+ var linkPreviewHostClass = "text-[13px] text-[var(--ink-soft)] truncate";
82
+ var dmShellClass = "grid h-[calc(100vh-56px)] min-h-[520px] grid-cols-1 min-[960px]:grid-cols-[360px_minmax(0,1fr)] min-[1360px]:grid-cols-[400px_minmax(0,1fr)]";
83
+ var dmListClass = "flex flex-col overflow-y-auto border-r border-[var(--line)]";
84
+ var dmListItemClass = "flex w-full items-start gap-3 border-b border-[var(--line)] border-0 bg-transparent px-3 py-3 text-left transition-colors duration-150 hover:bg-[var(--bg-hover)]";
85
+ var dmListItemActiveClass = "bg-[var(--bg-active)]";
86
+ var dmListBodyClass = "flex min-w-0 flex-1 flex-col gap-1";
87
+ var dmListHeaderClass = "flex items-center justify-between gap-2";
88
+ var dmListNameClass = "truncate text-[14px] font-bold text-[var(--ink)]";
89
+ var dmListHandleClass = "truncate text-[13px] text-[var(--ink-soft)]";
90
+ var dmListPreviewClass = "truncate text-[13px] text-[var(--ink-soft)]";
91
+ var dmListTimestampClass = "shrink-0 text-[12px] text-[var(--ink-soft)]";
92
+ var dmThreadClass = "flex min-w-0 flex-col";
93
+ var dmThreadHeaderClass = "sticky top-0 z-10 flex items-center justify-between gap-3 border-b border-[var(--line)] bg-[color:color-mix(in_srgb,var(--bg)_85%,transparent)] px-4 py-3 backdrop-blur";
94
+ var dmThreadTitleClass = "flex items-center gap-3 min-w-0";
95
+ var dmThreadNameClass = "truncate text-[16px] font-bold";
96
+ var dmThreadSubtitleClass = "truncate text-[13px] text-[var(--ink-soft)]";
97
+ var dmMessagesClass = "flex flex-1 flex-col gap-2 overflow-y-auto px-4 py-4";
98
+ var dmMessageRowClass = "flex max-w-[78%] flex-col gap-1";
99
+ var dmMessageRowOutboundClass = "self-end items-end";
100
+ var dmMessageBubbleClass = "rounded-2xl rounded-bl-md px-3.5 py-2 text-[15px] leading-[1.4]";
101
+ var dmMessageBubbleInboundClass = "bg-[var(--bg-active)] text-[var(--ink)]";
102
+ var dmMessageBubbleOutboundClass = "rounded-bl-2xl rounded-br-md bg-[var(--accent)] text-[var(--accent-text)]";
103
+ var dmMessageMetaClass = "flex items-center gap-2 text-[12px] text-[var(--ink-soft)]";
104
+ var dmComposerShellClass = "flex flex-col gap-2 border-t border-[var(--line)] px-4 py-3";
105
+ var profilePreviewClass = "relative inline-flex";
106
+ var profilePreviewTriggerClass = "profile-preview-trigger inline-flex text-inherit";
107
+ var profilePreviewCardClass = "fixed z-40 w-[280px] overflow-y-auto rounded-2xl border border-[var(--line)] bg-[var(--bg-elevated)] p-3 shadow-[0_8px_28px_var(--shadow-strong)]";
108
+ var profilePreviewHeaderClass = "flex items-center gap-3";
109
+ var profilePreviewNameClass = "block text-[15px] font-bold text-[var(--ink)]";
110
+ var profilePreviewHandleClass = "block text-[14px] text-[var(--ink-soft)]";
111
+ var profilePreviewBioClass = "block text-[14px] leading-[1.4] text-[var(--ink)]";
112
+ var profilePreviewMetaClass = "block text-[13px] text-[var(--ink-soft)]";
113
+ var tweetLinkClass = "text-[var(--accent)] hover:underline";
114
+ var tweetMentionClass = "text-[var(--accent)] hover:underline";
115
+ var tweetHashtagClass = tweetMentionClass;
116
+ function tweetMediaGridClass(count) {
117
+ const capped = Math.min(count, 4);
118
+ const layout = count === 1 ? "grid-cols-1" : count === 3 ? "grid-cols-[1.3fr_1fr]" : count === 4 ? "grid-cols-2 grid-rows-2" : "grid-cols-2";
119
+ return cx(`tweet-media-grid tweet-media-grid-${String(capped)}`, "mt-2 grid gap-0.5 overflow-hidden rounded-2xl border border-[var(--line)]", layout);
120
+ }
121
+ function tweetMediaTileClass(index, count) {
122
+ return cx("tweet-media-tile relative block overflow-hidden border-0 bg-[var(--bg-active)] p-0 text-left", count === 1 && "aspect-[16/10]", count === 2 && "aspect-square", count === 3 && index === 0 && "row-span-2 aspect-[3/4]", count === 3 && index !== 0 && "aspect-square", count === 4 && "aspect-square");
123
+ }
124
+ var inboxAnalysisClass = "mt-2 rounded-xl border border-[var(--line)] bg-[var(--bg-hover)] px-3 py-2.5 text-[14px] text-[var(--ink-soft)] [&_p]:mt-1 [&_p]:mb-0";
125
+ var blockRowClass = "flex items-start gap-3 border-b border-[var(--line)] px-4 py-3 transition-colors duration-150 hover:bg-[var(--bg-hover)]";
126
+ var blockRowBodyClass = "flex min-w-0 flex-1 flex-col gap-1";
127
+ var mutedDotClass = "inline-block size-1 shrink-0 rounded-full bg-[var(--ink-soft)]";
128
+ var pillClass = "inline-flex items-center rounded-full px-2 py-0.5 text-[12px] font-semibold";
129
+ var pillSoftClass = "bg-[var(--accent-soft)] text-[var(--accent)]";
130
+ var pillAlertClass = "bg-[var(--alert-soft)] text-[var(--alert)]";
131
+ var timestampClass = "text-[13px] text-[var(--ink-soft)]";
132
+ var bodyCopyClass = feedRowTextClass;
133
+ var contextStatRowClass = "grid grid-cols-[minmax(0,1fr)_auto] items-center gap-3 border-t border-[var(--line)] py-2 first:border-t-0 first:pt-0";
134
+ var contextStatTermClass = "min-w-0 text-[13px] text-[var(--ink-soft)]";
135
+ var contextStatValueClass = "shrink-0 whitespace-nowrap text-right text-[14px] font-bold text-[var(--ink)]";
136
+ //#endregion
137
+ export { feedRowBodyClass as $, sidebarBrandTaglineClass as $t, dmMessageRowClass as A, pillClass as At, embeddedCardClass as B, profilePreviewTriggerClass as Bt, dmListNameClass as C, navLinkLabelCompactClass as Ct, dmMessageBubbleInboundClass as D, pageSubtitleClass as Dt, dmMessageBubbleClass as E, pageHeaderRowClass as Et, dmThreadHeaderClass as F, profilePreviewClass as Ft, embeddedCardNameClass as G, segmentActiveClass as Gt, embeddedCardHandleClass as H, searchFieldInputClass as Ht, dmThreadNameClass as I, profilePreviewHandleClass as It, feedActionButtonClass as J, selectFieldClass as Jt, emptyStateClass as K, segmentClass as Kt, dmThreadSubtitleClass as L, profilePreviewHeaderClass as Lt, dmMessagesClass as M, primaryButtonClass as Mt, dmShellClass as N, profilePreviewBioClass as Nt, dmMessageBubbleOutboundClass as O, pageTitleClass as Ot, dmThreadClass as P, profilePreviewCardClass as Pt, feedRowActionsClass as Q, sidebarBrandMarkClass as Qt, dmThreadTitleClass as R, profilePreviewMetaClass as Rt, dmListItemClass as S, navLinkLabelClass as St, dmListTimestampClass as T, pageHeaderClass as Tt, embeddedCardHeaderClass as U, searchFieldShellClass as Ut, embeddedCardCopyClass as V, searchFieldIconClass as Vt, embeddedCardLabelClass as W, secondaryButtonClass as Wt, feedActionIconWrapClass as X, sidebarBrandCopyClass as Xt, feedActionIconClass as Y, sidebarBrandClass as Yt, feedClass as Z, sidebarBrandCopyCompactClass as Zt, dmListBodyClass as _, tweetMediaTileClass as _n, mutedDotClass as _t, blockRowClass as a, siteShellClass as an, feedRowStatePillActiveClass as at, dmListHeaderClass as b, navLinkCompactClass as bt, composerBarClass as c, tabButtonClass as cn, feedRowTextClass as ct, contextStatRowClass as d, textFieldClass as dn, linkPreviewCardClass as dt, sidebarBrandTitleClass as en, feedRowClass as et, contextStatTermClass as f, textFieldShortClass as fn, linkPreviewDescClass as ft, dmComposerShellClass as g, tweetMediaGridClass as gn, mainColumnDmClass as gt, dangerButtonClass as h, tweetLinkClass as hn, mainColumnClass as ht, blockRowBodyClass as i, sidebarShellCompactClass as in, feedRowNameClass as it, dmMessageRowOutboundClass as j, pillSoftClass as jt, dmMessageMetaClass as k, pillAlertClass as kt, composerInputClass as l, tabButtonIndicatorClass as ln, feedRowTimestampClass as lt, cx as m, tweetHashtagClass as mn, linkPreviewTitleClass as mt, avatarChipLargeClass as n, sidebarNavClass as nn, feedRowHandleClass as nt, bodyClass as o, statusCopyClass as on, feedRowStatePillClass as ot, contextStatValueClass as p, timestampClass as pn, linkPreviewHostClass as pt, errorCopyClass as q, segmentedClass as qt, avatarChipSmallClass as r, sidebarShellClass as rn, feedRowHeaderClass as rt, bodyCopyClass as s, tabButtonActiveClass as sn, feedRowStatePillOpenClass as st, avatarChipClass as t, sidebarFooterClass as tn, feedRowDotClass as tt, composerShellClass as u, tabStripClass as un, inboxAnalysisClass as ut, dmListClass as v, tweetMentionClass as vn, navLinkActiveClass as vt, dmListPreviewClass as w, pageHeaderActionsClass as wt, dmListItemActiveClass as x, navLinkIconClass as xt, dmListHandleClass as y, navLinkClass as yt, embeddedCardBodyClass as z, profilePreviewNameClass as zt };
@@ -1,12 +1,12 @@
1
1
  import { useEffect, useState } from "react";
2
-
3
- export function useDebouncedValue<T>(value: T, delayMs: number) {
2
+ //#region src/components/useDebouncedValue.ts
3
+ function useDebouncedValue(value, delayMs) {
4
4
  const [debouncedValue, setDebouncedValue] = useState(value);
5
-
6
5
  useEffect(() => {
7
6
  const timeout = window.setTimeout(() => setDebouncedValue(value), delayMs);
8
7
  return () => window.clearTimeout(timeout);
9
8
  }, [delayMs, value]);
10
-
11
9
  return debouncedValue;
12
10
  }
11
+ //#endregion
12
+ export { useDebouncedValue as t };