birdclaw 0.8.3 → 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 (284) hide show
  1. package/CHANGELOG.md +17 -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 +26 -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/command-context.ts +0 -17
  100. package/src/cli/register-analysis.ts +0 -500
  101. package/src/cli/register-compose.ts +0 -40
  102. package/src/cli/register-graph.ts +0 -132
  103. package/src/cli/register-inbox.ts +0 -41
  104. package/src/cli/register-storage.ts +0 -106
  105. package/src/cli-moderation.ts +0 -210
  106. package/src/cli.ts +0 -1616
  107. package/src/components/AccountSwitcher.tsx +0 -178
  108. package/src/components/AppNav.tsx +0 -118
  109. package/src/components/AvatarChip.tsx +0 -58
  110. package/src/components/AvatarPreload.ts +0 -149
  111. package/src/components/BrandMark.tsx +0 -67
  112. package/src/components/ConversationThread.tsx +0 -131
  113. package/src/components/DmWorkspace.tsx +0 -286
  114. package/src/components/EmbeddedTweetCard.tsx +0 -55
  115. package/src/components/FeedState.tsx +0 -147
  116. package/src/components/FloatingPreview.tsx +0 -382
  117. package/src/components/InboxCard.tsx +0 -152
  118. package/src/components/LinkPreviewCard.tsx +0 -292
  119. package/src/components/MarkdownCitations.tsx +0 -680
  120. package/src/components/MarkdownViewer.tsx +0 -111
  121. package/src/components/ProfileAnalysisClient.ts +0 -191
  122. package/src/components/ProfileAnalysisStream.tsx +0 -259
  123. package/src/components/ProfilePreview.tsx +0 -131
  124. package/src/components/SavedTimelineView.tsx +0 -155
  125. package/src/components/SmartTimestamp.tsx +0 -72
  126. package/src/components/SyncNowButton.tsx +0 -140
  127. package/src/components/ThemeSlider.tsx +0 -80
  128. package/src/components/TimelineCard.tsx +0 -533
  129. package/src/components/TimelineRouteFrame.tsx +0 -186
  130. package/src/components/TweetArticleCard.tsx +0 -66
  131. package/src/components/TweetMediaGrid.tsx +0 -185
  132. package/src/components/TweetRichText.tsx +0 -159
  133. package/src/components/account-selection.ts +0 -64
  134. package/src/components/links-controller.ts +0 -162
  135. package/src/components/links-model.ts +0 -198
  136. package/src/components/network-map-controller.ts +0 -84
  137. package/src/components/network-map-model.ts +0 -255
  138. package/src/components/useTimelineRouteData.ts +0 -193
  139. package/src/lib/account-sync-job.ts +0 -666
  140. package/src/lib/actions-transport.ts +0 -300
  141. package/src/lib/analysis-runtime.ts +0 -238
  142. package/src/lib/api-client.ts +0 -220
  143. package/src/lib/api-contracts.ts +0 -328
  144. package/src/lib/archive-finder.ts +0 -185
  145. package/src/lib/archive-import-plan.ts +0 -102
  146. package/src/lib/archive-import.ts +0 -2695
  147. package/src/lib/authored-live.ts +0 -1027
  148. package/src/lib/avatar-cache.ts +0 -349
  149. package/src/lib/backup.ts +0 -2652
  150. package/src/lib/bird-actions.ts +0 -294
  151. package/src/lib/bird-command.ts +0 -130
  152. package/src/lib/bird.ts +0 -1140
  153. package/src/lib/blocklist.ts +0 -136
  154. package/src/lib/blocks-write.ts +0 -171
  155. package/src/lib/blocks.ts +0 -387
  156. package/src/lib/bookmark-sync-job.ts +0 -536
  157. package/src/lib/config.ts +0 -224
  158. package/src/lib/conversation-surface.ts +0 -205
  159. package/src/lib/data-sources.ts +0 -219
  160. package/src/lib/database-metrics.ts +0 -88
  161. package/src/lib/database-migrations.ts +0 -34
  162. package/src/lib/database-schema.ts +0 -312
  163. package/src/lib/database-writer.ts +0 -69
  164. package/src/lib/db.ts +0 -889
  165. package/src/lib/dm-read-model.ts +0 -533
  166. package/src/lib/dms-live.ts +0 -870
  167. package/src/lib/effect-runtime.ts +0 -45
  168. package/src/lib/follow-graph.ts +0 -1087
  169. package/src/lib/geocoding.ts +0 -296
  170. package/src/lib/http-effect.ts +0 -222
  171. package/src/lib/identity-search-index.ts +0 -369
  172. package/src/lib/import-repository.ts +0 -138
  173. package/src/lib/inbox.ts +0 -242
  174. package/src/lib/link-index.ts +0 -641
  175. package/src/lib/link-insights.ts +0 -860
  176. package/src/lib/link-preview-metadata.ts +0 -754
  177. package/src/lib/live-sync-engine.ts +0 -209
  178. package/src/lib/live-transport-gateway.ts +0 -128
  179. package/src/lib/location.ts +0 -137
  180. package/src/lib/media-fetch.ts +0 -860
  181. package/src/lib/media-includes.ts +0 -165
  182. package/src/lib/mention-threads-live.ts +0 -830
  183. package/src/lib/mentions-export.ts +0 -147
  184. package/src/lib/mentions-live.ts +0 -1114
  185. package/src/lib/moderation-target.ts +0 -227
  186. package/src/lib/moderation-write.ts +0 -131
  187. package/src/lib/mutes-write.ts +0 -171
  188. package/src/lib/mutes.ts +0 -84
  189. package/src/lib/ndjson-stream.ts +0 -106
  190. package/src/lib/network-map.ts +0 -382
  191. package/src/lib/openai-response-runtime.ts +0 -251
  192. package/src/lib/openai.ts +0 -117
  193. package/src/lib/paginated-sync.ts +0 -93
  194. package/src/lib/period-digest.ts +0 -1364
  195. package/src/lib/present.ts +0 -118
  196. package/src/lib/profile-affiliation-hydration.ts +0 -228
  197. package/src/lib/profile-affiliations.ts +0 -262
  198. package/src/lib/profile-analysis.ts +0 -1198
  199. package/src/lib/profile-bio-entities.ts +0 -318
  200. package/src/lib/profile-history.ts +0 -164
  201. package/src/lib/profile-hydration.ts +0 -180
  202. package/src/lib/profile-replies.ts +0 -77
  203. package/src/lib/profile-resolver.ts +0 -650
  204. package/src/lib/queries.ts +0 -6
  205. package/src/lib/query-actions.ts +0 -437
  206. package/src/lib/query-client.tsx +0 -47
  207. package/src/lib/query-read-model-shared.ts +0 -52
  208. package/src/lib/query-read-models.ts +0 -5
  209. package/src/lib/query-resource.ts +0 -41
  210. package/src/lib/query-status.ts +0 -164
  211. package/src/lib/research.ts +0 -652
  212. package/src/lib/runtime-services.ts +0 -20
  213. package/src/lib/search-discussion.ts +0 -812
  214. package/src/lib/seed.ts +0 -613
  215. package/src/lib/server-runtime-services.ts +0 -30
  216. package/src/lib/sqlite.ts +0 -183
  217. package/src/lib/streaming-ingestion.ts +0 -240
  218. package/src/lib/sync-cache.ts +0 -70
  219. package/src/lib/sync-plan.ts +0 -175
  220. package/src/lib/theme-transition.ts +0 -113
  221. package/src/lib/theme.tsx +0 -165
  222. package/src/lib/timeline-collections-live.ts +0 -407
  223. package/src/lib/timeline-live.ts +0 -302
  224. package/src/lib/timeline-read-model.ts +0 -1191
  225. package/src/lib/tweet-account-edges.ts +0 -48
  226. package/src/lib/tweet-lookup.ts +0 -46
  227. package/src/lib/tweet-render.ts +0 -430
  228. package/src/lib/tweet-repository.ts +0 -156
  229. package/src/lib/tweet-search-live.ts +0 -462
  230. package/src/lib/types.ts +0 -723
  231. package/src/lib/ui.ts +0 -529
  232. package/src/lib/url-expansion-store.ts +0 -110
  233. package/src/lib/url-expansion.ts +0 -343
  234. package/src/lib/url-safety.ts +0 -220
  235. package/src/lib/web-sync.ts +0 -533
  236. package/src/lib/whois.ts +0 -1080
  237. package/src/lib/x-profile.ts +0 -365
  238. package/src/lib/x-web.ts +0 -199
  239. package/src/lib/xurl-rate-limits.ts +0 -267
  240. package/src/lib/xurl.ts +0 -1677
  241. package/src/routeTree.gen.ts +0 -723
  242. package/src/router.tsx +0 -20
  243. package/src/routes/__root.tsx +0 -82
  244. package/src/routes/api/action.tsx +0 -147
  245. package/src/routes/api/avatar.tsx +0 -50
  246. package/src/routes/api/blocks.tsx +0 -35
  247. package/src/routes/api/conversation.tsx +0 -45
  248. package/src/routes/api/data-sources.tsx +0 -24
  249. package/src/routes/api/inbox.tsx +0 -43
  250. package/src/routes/api/link-insights.tsx +0 -85
  251. package/src/routes/api/link-preview.tsx +0 -47
  252. package/src/routes/api/network-map.tsx +0 -55
  253. package/src/routes/api/period-digest.tsx +0 -101
  254. package/src/routes/api/profile-analysis.tsx +0 -97
  255. package/src/routes/api/profile-hydrate.tsx +0 -54
  256. package/src/routes/api/query.tsx +0 -121
  257. package/src/routes/api/search-discussion.tsx +0 -113
  258. package/src/routes/api/status.tsx +0 -30
  259. package/src/routes/api/sync.tsx +0 -108
  260. package/src/routes/api/xurl-rate-limits.tsx +0 -24
  261. package/src/routes/blocks.tsx +0 -343
  262. package/src/routes/bookmarks.tsx +0 -18
  263. package/src/routes/data-sources.tsx +0 -249
  264. package/src/routes/discuss.tsx +0 -419
  265. package/src/routes/dms.tsx +0 -415
  266. package/src/routes/inbox.tsx +0 -232
  267. package/src/routes/index.tsx +0 -32
  268. package/src/routes/likes.tsx +0 -18
  269. package/src/routes/links.tsx +0 -631
  270. package/src/routes/mentions.tsx +0 -32
  271. package/src/routes/network-map.tsx +0 -732
  272. package/src/routes/profile-analyze.tsx +0 -112
  273. package/src/routes/profiles.$handle.tsx +0 -228
  274. package/src/routes/rate-limits.tsx +0 -305
  275. package/src/routes/today.tsx +0 -511
  276. package/src/styles.css +0 -257
  277. package/tsconfig.json +0 -29
  278. package/vite.config.ts +0 -29
  279. /package/{public → dist/client}/birdclaw-mark.png +0 -0
  280. /package/{public → dist/client}/favicon.ico +0 -0
  281. /package/{public → dist/client}/logo192.png +0 -0
  282. /package/{public → dist/client}/logo512.png +0 -0
  283. /package/{public → dist/client}/manifest.json +0 -0
  284. /package/{public → dist/client}/robots.txt +0 -0
package/src/lib/ui.ts DELETED
@@ -1,529 +0,0 @@
1
- export function cx(...values: Array<string | false | null | undefined>) {
2
- return values.filter(Boolean).join(" ");
3
- }
4
-
5
- export const bodyClass =
6
- "min-h-screen bg-[var(--bg)] font-sans text-[var(--ink)] antialiased text-[15px] leading-[1.4]";
7
-
8
- /* App shell — sidebar left, single feed column center, optional aside right. */
9
- export const siteShellClass =
10
- "mx-auto flex min-h-screen w-full max-w-[1280px] gap-0";
11
-
12
- export const siteShellDmClass =
13
- "mx-auto flex min-h-screen w-full max-w-[1280px] gap-0";
14
-
15
- export const sidebarShellClass =
16
- "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";
17
-
18
- export const sidebarShellCompactClass =
19
- "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";
20
-
21
- export const sidebarBrandClass =
22
- "flex items-center gap-2.5 px-2 py-2 text-[var(--ink)] min-[1100px]:px-3";
23
-
24
- export const sidebarBrandMarkClass = "grid size-10 place-items-center";
25
-
26
- export const sidebarBrandCopyClass =
27
- "hidden flex-col leading-tight min-[1100px]:flex";
28
-
29
- export const sidebarBrandCopyCompactClass = "sr-only";
30
-
31
- export const sidebarBrandTitleClass = "text-[15px] font-bold tracking-tight";
32
-
33
- export const sidebarBrandTaglineClass = "text-[12px] text-[var(--ink-soft)]";
34
-
35
- export const sidebarNavClass = "mt-2 flex flex-col gap-0.5";
36
-
37
- export const sidebarFooterClass = "flex flex-col gap-2 pb-1";
38
-
39
- export const navLinkClass =
40
- "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";
41
-
42
- export const navLinkCompactClass =
43
- "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)]";
44
-
45
- export const navLinkActiveClass = "nav-link-active font-bold";
46
-
47
- export const navLinkIconClass = "shrink-0";
48
-
49
- export const navLinkLabelClass = "sr-only min-[1100px]:not-sr-only";
50
-
51
- export const navLinkLabelCompactClass = "sr-only";
52
-
53
- /* Main column. */
54
- export const mainColumnClass =
55
- "flex w-full min-w-0 max-w-[680px] flex-1 flex-col border-x border-[var(--line)] bg-[var(--bg)]";
56
-
57
- export const mainColumnDmClass =
58
- "flex w-full min-w-0 flex-1 flex-col border-x border-[var(--line)] bg-[var(--bg)]";
59
-
60
- export const pageWrapClass = "flex w-full min-w-0 flex-1 justify-start";
61
-
62
- /* Sticky page header at top of the main column. */
63
- export const pageHeaderClass =
64
- "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";
65
-
66
- export const pageHeaderRowClass =
67
- "flex items-center justify-between gap-3 px-4 pt-3 pb-2";
68
-
69
- export const pageTitleClass = "m-0 text-[20px] font-bold tracking-tight";
70
-
71
- export const pageSubtitleClass = "text-[13px] text-[var(--ink-soft)]";
72
-
73
- export const pageHeaderActionsClass = "flex items-center gap-2";
74
-
75
- /* Sticky tab strip Twitter-style. */
76
- export const tabStripClass =
77
- "flex w-full items-stretch border-b border-[var(--line)] overflow-x-auto";
78
-
79
- export const tabButtonClass =
80
- "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)]";
81
-
82
- export const tabButtonActiveClass = "text-[var(--ink)] font-bold";
83
-
84
- export const tabButtonIndicatorClass =
85
- "pointer-events-none absolute bottom-0 left-1/2 h-[3px] w-12 -translate-x-1/2 rounded-full bg-[var(--accent)]";
86
-
87
- /* Feed rows: flat, hairline divider, no boxed cards. */
88
- export const feedClass = "flex flex-col";
89
-
90
- export const feedRowClass =
91
- "flex w-full gap-3 border-b border-[var(--line)] px-4 py-3 transition-colors duration-150 hover:bg-[var(--bg-hover)]";
92
-
93
- export const feedRowBodyClass = "flex min-w-0 flex-1 flex-col gap-1";
94
-
95
- export const feedRowHeaderClass =
96
- "flex min-w-0 items-center gap-1.5 text-[15px]";
97
-
98
- export const feedRowNameClass =
99
- "truncate font-bold text-[var(--ink)] hover:underline";
100
-
101
- export const feedRowHandleClass = "truncate text-[var(--ink-soft)]";
102
-
103
- export const feedRowDotClass = "text-[var(--ink-soft)]";
104
-
105
- export const feedRowTimestampClass =
106
- "shrink-0 text-[var(--ink-soft)] hover:underline";
107
-
108
- export const feedRowMetaClass =
109
- "flex flex-wrap items-center gap-2 text-[13px] text-[var(--ink-soft)]";
110
-
111
- export const feedRowActionsClass =
112
- "mt-2 flex max-w-md items-center justify-between text-[var(--ink-soft)]";
113
-
114
- export const feedActionButtonClass =
115
- "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";
116
-
117
- export const feedActionLikeClass = "hover:text-[var(--like)]";
118
-
119
- export const feedActionIconWrapClass =
120
- "grid size-[34px] place-items-center rounded-full transition-colors duration-150 group-hover/action:bg-[var(--accent-soft)]";
121
-
122
- export const feedActionIconWrapLikeClass =
123
- "group-hover/action:bg-[var(--like-soft)]";
124
-
125
- export const feedActionIconClass = "size-[18px]";
126
-
127
- export const feedRowTextClass =
128
- "whitespace-pre-wrap break-words text-[15px] leading-[1.45] text-[var(--ink)] [overflow-wrap:anywhere]";
129
-
130
- export const feedRowBadgeClass =
131
- "inline-flex items-center gap-1 rounded-full bg-[var(--accent-soft)] px-2 py-0.5 text-[12px] font-semibold text-[var(--accent)]";
132
-
133
- export const feedRowBadgeAlertClass =
134
- "bg-[var(--alert-soft)] text-[var(--alert)]";
135
-
136
- export const feedRowBadgeNeutralClass =
137
- "bg-[var(--bg-active)] text-[var(--ink-soft)]";
138
-
139
- export const feedRowStatePillClass =
140
- "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)]";
141
-
142
- export const feedRowStatePillActiveClass =
143
- "border-[color:color-mix(in_srgb,var(--accent)_35%,var(--line))] bg-[var(--accent-soft)] text-[var(--accent)]";
144
-
145
- export const feedRowStatePillOpenClass =
146
- "border-[color:color-mix(in_srgb,var(--ink-soft)_34%,var(--line))] bg-transparent text-[var(--ink-soft)]";
147
-
148
- /* Forms / inputs. */
149
- export const searchFieldShellClass =
150
- "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)]";
151
-
152
- export const searchFieldIconClass =
153
- "size-[18px] text-[var(--ink-soft)] shrink-0";
154
-
155
- export const searchFieldInputClass =
156
- "min-w-0 flex-1 border-0 bg-transparent text-[14px] text-[var(--ink)] outline-none placeholder:text-[var(--ink-soft)]";
157
-
158
- export const textFieldClass =
159
- "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";
160
-
161
- export const textFieldWideClass = "w-full";
162
-
163
- export const textFieldShortClass = "w-[120px]";
164
-
165
- export const selectFieldClass = cx(textFieldClass, "appearance-none pr-8");
166
-
167
- /* Buttons. */
168
- export const primaryButtonClass =
169
- "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";
170
-
171
- export const primaryButtonLargeClass =
172
- "inline-flex items-center justify-center gap-1.5 rounded-full border-0 bg-[var(--accent)] px-5 py-2.5 text-[15px] 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";
173
-
174
- export const secondaryButtonClass =
175
- "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";
176
-
177
- export const dangerButtonClass =
178
- "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";
179
-
180
- export const actionButtonClass = primaryButtonClass;
181
-
182
- /* Legacy segmented control retained for routes that still use it (compact pill). */
183
- export const segmentedClass =
184
- "inline-flex shrink-0 items-center gap-1 rounded-full border border-[var(--line)] bg-[var(--bg)] p-1";
185
-
186
- export const segmentClass =
187
- "rounded-full border-0 bg-transparent px-3 py-1 text-[13px] font-medium text-[var(--ink-soft)] capitalize transition-colors duration-150";
188
-
189
- export const segmentActiveClass = "bg-[var(--bg-active)] text-[var(--ink)]";
190
-
191
- /* Composer (reply textarea). */
192
- export const composerShellClass =
193
- "mt-2 flex flex-col gap-2 border-t border-[var(--line)] pt-3";
194
-
195
- export const composerInputClass =
196
- "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)]";
197
-
198
- export const composerBarClass = "flex items-center justify-between gap-3";
199
-
200
- /* Empty / loading. */
201
- export const emptyStateClass = "px-6 py-10 text-center text-[var(--ink-soft)]";
202
-
203
- export const errorCopyClass =
204
- "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)]";
205
-
206
- export const statusCopyClass =
207
- "mx-4 my-3 rounded-md border border-[var(--line)] bg-[var(--bg-elevated)] px-3 py-2 text-[13px] text-[var(--ink-soft)]";
208
-
209
- /* Avatars. */
210
- export const avatarChipClass =
211
- "avatar-chip inline-grid size-10 shrink-0 place-items-center overflow-hidden rounded-full text-[14px] font-bold text-white";
212
-
213
- export const avatarChipLargeClass = "avatar-chip-large size-16 text-[18px]";
214
-
215
- export const avatarChipSmallClass = "avatar-chip-small size-8 text-[12px]";
216
-
217
- /* Embedded tweet / link preview (rounded box, used inside feed row body). */
218
- export const embeddedCardClass =
219
- "mt-2 overflow-hidden rounded-2xl border border-[var(--line)] transition-colors duration-150 hover:bg-[var(--bg-hover)]";
220
-
221
- export const embeddedCardBodyClass = "flex flex-col gap-2 px-3 py-2.5";
222
-
223
- export const embeddedCardHeaderClass = "flex items-center gap-1.5 text-[14px]";
224
-
225
- export const embeddedCardNameClass = "font-bold text-[var(--ink)] truncate";
226
-
227
- export const embeddedCardHandleClass = "text-[var(--ink-soft)] truncate";
228
-
229
- export const embeddedCardLabelClass =
230
- "text-[12px] uppercase tracking-wide text-[var(--ink-soft)]";
231
-
232
- export const embeddedCardCopyClass =
233
- "whitespace-pre-wrap text-[14px] leading-[1.4] text-[var(--ink)] [overflow-wrap:anywhere]";
234
-
235
- export const linkPreviewCardClass =
236
- "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)]";
237
-
238
- export const linkPreviewTitleClass =
239
- "line-clamp-2 font-bold leading-tight text-[var(--ink)]";
240
-
241
- export const linkPreviewDescClass =
242
- "line-clamp-2 text-[14px] leading-snug text-[var(--ink-soft)]";
243
-
244
- export const linkPreviewHostClass =
245
- "text-[13px] text-[var(--ink-soft)] truncate";
246
-
247
- /* DM grid. */
248
- export const dmShellClass =
249
- "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)]";
250
-
251
- export const dmListClass =
252
- "flex flex-col overflow-y-auto border-r border-[var(--line)]";
253
-
254
- export const dmListItemClass =
255
- "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)]";
256
-
257
- export const dmListItemActiveClass = "bg-[var(--bg-active)]";
258
-
259
- export const dmListBodyClass = "flex min-w-0 flex-1 flex-col gap-1";
260
-
261
- export const dmListHeaderClass = "flex items-center justify-between gap-2";
262
-
263
- export const dmListNameClass =
264
- "truncate text-[14px] font-bold text-[var(--ink)]";
265
-
266
- export const dmListHandleClass = "truncate text-[13px] text-[var(--ink-soft)]";
267
-
268
- export const dmListPreviewClass = "truncate text-[13px] text-[var(--ink-soft)]";
269
-
270
- export const dmListTimestampClass =
271
- "shrink-0 text-[12px] text-[var(--ink-soft)]";
272
-
273
- export const dmThreadClass = "flex min-w-0 flex-col";
274
-
275
- export const dmThreadHeaderClass =
276
- "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";
277
-
278
- export const dmThreadTitleClass = "flex items-center gap-3 min-w-0";
279
-
280
- export const dmThreadNameClass = "truncate text-[16px] font-bold";
281
-
282
- export const dmThreadSubtitleClass =
283
- "truncate text-[13px] text-[var(--ink-soft)]";
284
-
285
- export const dmMessagesClass =
286
- "flex flex-1 flex-col gap-2 overflow-y-auto px-4 py-4";
287
-
288
- export const dmMessageRowClass = "flex max-w-[78%] flex-col gap-1";
289
-
290
- export const dmMessageRowOutboundClass = "self-end items-end";
291
-
292
- export const dmMessageBubbleClass =
293
- "rounded-2xl rounded-bl-md px-3.5 py-2 text-[15px] leading-[1.4]";
294
-
295
- export const dmMessageBubbleInboundClass =
296
- "bg-[var(--bg-active)] text-[var(--ink)]";
297
-
298
- export const dmMessageBubbleOutboundClass =
299
- "rounded-bl-2xl rounded-br-md bg-[var(--accent)] text-[var(--accent-text)]";
300
-
301
- export const dmMessageMetaClass =
302
- "flex items-center gap-2 text-[12px] text-[var(--ink-soft)]";
303
-
304
- export const dmComposerShellClass =
305
- "flex flex-col gap-2 border-t border-[var(--line)] px-4 py-3";
306
-
307
- /* Profile hovercard. */
308
- export const profilePreviewClass = "relative inline-flex";
309
-
310
- export const profilePreviewTriggerClass =
311
- "profile-preview-trigger inline-flex text-inherit";
312
-
313
- export const profilePreviewCardClass =
314
- "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)]";
315
-
316
- export const profilePreviewHeaderClass = "flex items-center gap-3";
317
-
318
- export const profilePreviewNameClass =
319
- "block text-[15px] font-bold text-[var(--ink)]";
320
-
321
- export const profilePreviewHandleClass =
322
- "block text-[14px] text-[var(--ink-soft)]";
323
-
324
- export const profilePreviewBioClass =
325
- "block text-[14px] leading-[1.4] text-[var(--ink)]";
326
-
327
- export const profilePreviewMetaClass =
328
- "block text-[13px] text-[var(--ink-soft)]";
329
-
330
- /* Rich tweet text. */
331
- export const tweetLinkClass = "text-[var(--accent)] hover:underline";
332
-
333
- export const tweetMentionClass = "text-[var(--accent)] hover:underline";
334
-
335
- export const tweetHashtagClass = tweetMentionClass;
336
-
337
- /* Media grid (Twitter-style with rounded corners). */
338
- export function tweetMediaGridClass(count: number) {
339
- const capped = Math.min(count, 4);
340
- const layout =
341
- count === 1
342
- ? "grid-cols-1"
343
- : count === 3
344
- ? "grid-cols-[1.3fr_1fr]"
345
- : count === 4
346
- ? "grid-cols-2 grid-rows-2"
347
- : "grid-cols-2";
348
-
349
- return cx(
350
- `tweet-media-grid tweet-media-grid-${String(capped)}`,
351
- "mt-2 grid gap-0.5 overflow-hidden rounded-2xl border border-[var(--line)]",
352
- layout,
353
- );
354
- }
355
-
356
- export function tweetMediaTileClass(index: number, count: number) {
357
- return cx(
358
- "tweet-media-tile relative block overflow-hidden border-0 bg-[var(--bg-active)] p-0 text-left",
359
- count === 1 && "aspect-[16/10]",
360
- count === 2 && "aspect-square",
361
- count === 3 && index === 0 && "row-span-2 aspect-[3/4]",
362
- count === 3 && index !== 0 && "aspect-square",
363
- count === 4 && "aspect-square",
364
- );
365
- }
366
-
367
- /* Inbox / triage card. */
368
- export const inboxAnalysisClass =
369
- "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";
370
-
371
- /* Block list item. */
372
- export const blockRowClass =
373
- "flex items-start gap-3 border-b border-[var(--line)] px-4 py-3 transition-colors duration-150 hover:bg-[var(--bg-hover)]";
374
-
375
- export const blockRowBodyClass = "flex min-w-0 flex-1 flex-col gap-1";
376
-
377
- /* Aside (optional right column for stats). Hidden on small screens. */
378
- export const asideShellClass =
379
- "sticky top-0 hidden h-screen w-[300px] shrink-0 flex-col gap-4 overflow-y-auto px-4 py-4 min-[1180px]:flex";
380
-
381
- export const asideCardClass =
382
- "rounded-2xl border border-[var(--line)] bg-[var(--bg-elevated)] p-4";
383
-
384
- export const asideTitleClass = "text-[15px] font-bold text-[var(--ink)]";
385
-
386
- export const asideStatRowClass =
387
- "flex items-baseline justify-between border-t border-[var(--line)] py-2 first:border-t-0 first:pt-0";
388
-
389
- export const asideStatTermClass = "text-[13px] text-[var(--ink-soft)]";
390
-
391
- export const asideStatValueClass = "text-[14px] font-bold text-[var(--ink)]";
392
-
393
- /* Misc legacy aliases kept for compatibility while routes/components migrate. */
394
- export const eyebrowClass =
395
- "text-[12px] font-semibold uppercase tracking-wide text-[var(--ink-soft)]";
396
-
397
- export const brandMarkClass = sidebarBrandTitleClass;
398
-
399
- export const navClass = sidebarShellClass;
400
-
401
- export const navLinksClass = sidebarNavClass;
402
-
403
- export const heroShellClass = "flex flex-col gap-1 px-4 pt-3 pb-2";
404
-
405
- export const heroShellDmClass = "";
406
-
407
- export const heroControlsClass = "flex flex-wrap items-center gap-2 px-4 py-2";
408
-
409
- export const heroControlsDmClass = "";
410
-
411
- export const heroControlsBlocksClass = "";
412
-
413
- export const heroTitleClass = pageTitleClass;
414
-
415
- export const heroCopyClass = pageSubtitleClass;
416
-
417
- export const surfaceCardClass =
418
- "rounded-2xl border border-[var(--line)] bg-[var(--bg-elevated)]";
419
-
420
- export const contentCardClass = feedRowClass;
421
-
422
- export const cardHeaderClass = "flex items-start justify-between gap-3";
423
-
424
- export const cardFooterClass = "flex items-center justify-between gap-3";
425
-
426
- export const identityBlockClass = "flex items-start gap-3";
427
-
428
- export const identityRowClass =
429
- "flex flex-wrap items-center gap-1.5 text-[15px] text-[var(--ink-soft)]";
430
-
431
- export const metaRowClass =
432
- "flex flex-wrap items-center gap-1.5 text-[13px] text-[var(--ink-soft)]";
433
-
434
- export const mutedDotClass =
435
- "inline-block size-1 shrink-0 rounded-full bg-[var(--ink-soft)]";
436
-
437
- export const metaStackClass = "flex flex-col items-end gap-1";
438
-
439
- export const pillClass =
440
- "inline-flex items-center rounded-full px-2 py-0.5 text-[12px] font-semibold";
441
-
442
- export const pillSoftClass = "bg-[var(--accent-soft)] text-[var(--accent)]";
443
-
444
- export const pillAlertClass = "bg-[var(--alert-soft)] text-[var(--alert)]";
445
-
446
- export const timestampClass = "text-[13px] text-[var(--ink-soft)]";
447
-
448
- export const bodyCopyClass = feedRowTextClass;
449
-
450
- export const inboxTitleClass = "text-[16px] font-bold text-[var(--ink)]";
451
-
452
- export const metricRowClass =
453
- "flex flex-wrap items-center gap-3 text-[13px] text-[var(--ink-soft)]";
454
-
455
- export const actionRowClass = "flex items-center gap-2";
456
-
457
- export const feedPageClass = "flex w-full min-w-0 flex-col";
458
-
459
- export const timelineLaneClass = feedClass;
460
-
461
- export const inboxLaneClass = feedClass;
462
-
463
- export const dmPageClass = "flex w-full min-w-0 flex-col";
464
-
465
- export const stackGridClass = "flex flex-col";
466
-
467
- /* Embedded tweet legacy aliases. */
468
- export const embeddedTweetCardClass = embeddedCardClass;
469
-
470
- export const embeddedTweetLabelClass = embeddedCardLabelClass;
471
-
472
- export const embeddedTweetHeaderClass = embeddedCardHeaderClass;
473
-
474
- export const embeddedTweetAuthorClass = "flex flex-wrap items-center gap-1.5";
475
-
476
- export const embeddedTweetCopyClass = embeddedCardCopyClass;
477
-
478
- /* DM legacy aliases (still used by DmWorkspace until rewrite). */
479
- export const dmGridClass = dmShellClass;
480
-
481
- export const dmListItemNonActive = "";
482
-
483
- export const dmListCopyClass = dmListBodyClass;
484
-
485
- export const dmPreviewTextClass = dmListPreviewClass;
486
-
487
- export const threadShellClass = dmThreadClass;
488
-
489
- export const threadHeaderClass = dmThreadHeaderClass;
490
-
491
- export const threadTitleClass = dmThreadNameClass;
492
-
493
- export const threadSubtitleClass = dmThreadSubtitleClass;
494
-
495
- export const threadBioClass = "text-[14px] text-[var(--ink-soft)]";
496
-
497
- export const threadDetailColumnClass =
498
- "flex flex-col gap-3 rounded-2xl border border-[var(--line)] bg-[var(--bg-elevated)] p-4";
499
-
500
- export const threadDetailHeaderClass = "flex items-center gap-3";
501
-
502
- export const messageStackClass = dmMessagesClass;
503
-
504
- export const messageRowClass = dmMessageRowClass;
505
-
506
- export const messageRowOutboundClass = dmMessageRowOutboundClass;
507
-
508
- export const messageMetaClass = dmMessageMetaClass;
509
-
510
- export const messageBubbleClass = dmMessageBubbleClass;
511
-
512
- export const messageBubbleInboundClass = dmMessageBubbleInboundClass;
513
-
514
- export const messageBubbleOutboundClass = dmMessageBubbleOutboundClass;
515
-
516
- export const contextHandleClass = "text-[14px] text-[var(--ink-soft)]";
517
-
518
- export const contextBioClass = "text-[14px] leading-[1.4] text-[var(--ink)]";
519
-
520
- export const contextStatsClass = "flex flex-col gap-0";
521
-
522
- export const contextStatRowClass =
523
- "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";
524
-
525
- export const contextStatTermClass =
526
- "min-w-0 text-[13px] text-[var(--ink-soft)]";
527
-
528
- export const contextStatValueClass =
529
- "shrink-0 whitespace-nowrap text-right text-[14px] font-bold text-[var(--ink)]";
@@ -1,110 +0,0 @@
1
- import type { Database } from "./sqlite";
2
- import type { LinkIndexItem } from "./types";
3
-
4
- export interface UrlExpansionRecordInput {
5
- url: string;
6
- expandedUrl: string;
7
- finalUrl: string;
8
- status: "hit" | "miss" | "error";
9
- title?: string | null;
10
- description?: string | null;
11
- imageUrl?: string | null;
12
- siteName?: string | null;
13
- error?: string;
14
- source: string;
15
- updatedAt: string;
16
- }
17
-
18
- function getTweetTarget(url: string) {
19
- try {
20
- const parsed = new URL(url);
21
- const host = parsed.hostname.toLowerCase();
22
- if (
23
- host !== "x.com" &&
24
- host !== "twitter.com" &&
25
- host !== "mobile.twitter.com" &&
26
- host !== "www.x.com" &&
27
- host !== "www.twitter.com"
28
- ) {
29
- return {};
30
- }
31
-
32
- const parts = parsed.pathname.split("/").filter(Boolean);
33
- const statusIndex = parts.findIndex(
34
- (part) => part === "status" || part === "statuses",
35
- );
36
- if (statusIndex === -1 || !parts[statusIndex + 1]) {
37
- return {};
38
- }
39
-
40
- const tweetId = parts[statusIndex + 1]?.match(/^\d+/)?.[0];
41
- const handle =
42
- parts[statusIndex - 1] && parts[statusIndex - 1] !== "i"
43
- ? parts[statusIndex - 1]
44
- : undefined;
45
- return {
46
- expandedTweetId: tweetId,
47
- expandedHandle: handle,
48
- };
49
- } catch {
50
- return {};
51
- }
52
- }
53
-
54
- export function normalizeUrlExpansionForIndex(
55
- item: UrlExpansionRecordInput,
56
- ): LinkIndexItem {
57
- const target = getTweetTarget(item.finalUrl);
58
- return {
59
- shortUrl: item.url,
60
- expandedUrl: item.expandedUrl,
61
- finalUrl: item.finalUrl,
62
- status: item.status,
63
- expandedTweetId: target.expandedTweetId ?? null,
64
- expandedHandle: target.expandedHandle ?? null,
65
- title: item.title ?? null,
66
- description: item.description ?? null,
67
- imageUrl: item.imageUrl ?? null,
68
- siteName: item.siteName ?? null,
69
- error: item.error ?? null,
70
- source: item.source,
71
- updatedAt: item.updatedAt,
72
- };
73
- }
74
-
75
- export function upsertUrlExpansion(db: Database, item: LinkIndexItem) {
76
- db.prepare(`
77
- insert into url_expansions (
78
- short_url, expanded_url, final_url, status, expanded_tweet_id,
79
- expanded_handle, title, description, image_url, site_name, error, source,
80
- updated_at
81
- ) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
82
- on conflict(short_url) do update set
83
- expanded_url = excluded.expanded_url,
84
- final_url = excluded.final_url,
85
- status = excluded.status,
86
- expanded_tweet_id = excluded.expanded_tweet_id,
87
- expanded_handle = excluded.expanded_handle,
88
- title = excluded.title,
89
- description = excluded.description,
90
- image_url = excluded.image_url,
91
- site_name = excluded.site_name,
92
- error = excluded.error,
93
- source = excluded.source,
94
- updated_at = excluded.updated_at
95
- `).run(
96
- item.shortUrl,
97
- item.expandedUrl,
98
- item.finalUrl,
99
- item.status,
100
- item.expandedTweetId,
101
- item.expandedHandle,
102
- item.title,
103
- item.description,
104
- item.imageUrl,
105
- item.siteName,
106
- item.error,
107
- item.source,
108
- item.updatedAt,
109
- );
110
- }