birdclaw 0.5.0 → 0.5.1
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 +20 -0
- package/README.md +5 -0
- package/bin/birdclaw.mjs +50 -11
- package/package.json +7 -6
- package/public/birdclaw-mark.png +0 -0
- package/scripts/browser-perf.mjs +399 -0
- package/scripts/build-docs-site.mjs +940 -0
- package/scripts/docs-site-assets.mjs +311 -0
- package/scripts/run-vitest.mjs +21 -0
- package/scripts/sanitize-node-options.mjs +23 -0
- package/scripts/start-test-server.mjs +29 -0
- package/src/cli.ts +46 -1
- package/src/components/AppNav.tsx +3 -3
- package/src/components/BrandMark.tsx +67 -0
- package/src/components/ConversationThread.tsx +11 -10
- package/src/components/DmWorkspace.tsx +24 -9
- package/src/components/FeedState.tsx +147 -0
- package/src/components/InboxCard.tsx +14 -2
- package/src/components/SavedTimelineView.tsx +64 -56
- package/src/components/SyncNowButton.tsx +105 -0
- package/src/components/ThemeSlider.tsx +10 -59
- package/src/components/TimelineCard.tsx +157 -61
- package/src/components/TimelineRouteFrame.tsx +156 -0
- package/src/components/TweetMediaGrid.tsx +120 -23
- package/src/components/TweetRichText.tsx +13 -2
- package/src/components/useTimelineRouteData.ts +137 -0
- package/src/lib/api-client.ts +229 -0
- package/src/lib/archive-finder.ts +24 -20
- package/src/lib/archive-import.ts +18 -3
- package/src/lib/conversation-surface.ts +174 -0
- package/src/lib/db.ts +2 -0
- package/src/lib/queries.ts +93 -28
- package/src/lib/ui.ts +11 -3
- package/src/lib/web-sync.ts +443 -0
- package/src/routeTree.gen.ts +21 -0
- package/src/routes/api/sync.tsx +59 -0
- package/src/routes/dms.tsx +100 -27
- package/src/routes/index.tsx +21 -127
- package/src/routes/links.tsx +50 -5
- package/src/routes/mentions.tsx +21 -127
- package/src/styles.css +74 -11
- package/vite.config.ts +8 -0
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
export function css() {
|
|
2
|
+
return `
|
|
3
|
+
:root{
|
|
4
|
+
--ink:#0f1419;
|
|
5
|
+
--text:#0f1419;
|
|
6
|
+
--muted:#536471;
|
|
7
|
+
--subtle:#8b98a5;
|
|
8
|
+
--bg:#ffffff;
|
|
9
|
+
--paper:#f7f9f9;
|
|
10
|
+
--paper-2:#eff3f4;
|
|
11
|
+
--accent:#1d9bf0;
|
|
12
|
+
--accent-soft:rgba(29,155,240,.10);
|
|
13
|
+
--accent-strong:#1a8cd8;
|
|
14
|
+
--bird-blue:#1d9bf0;
|
|
15
|
+
--bird-sky:#71c9f8;
|
|
16
|
+
--bird-coral:#f4212e;
|
|
17
|
+
--bird-gold:#ffd400;
|
|
18
|
+
--line:#eff3f4;
|
|
19
|
+
--line-strong:#cfd9de;
|
|
20
|
+
--line-soft:#f7f9f9;
|
|
21
|
+
--code-bg:#0b1622;
|
|
22
|
+
--code-fg:#e7eef6;
|
|
23
|
+
--code-inline-bg:#eff3f4;
|
|
24
|
+
--code-inline-fg:#0f1419;
|
|
25
|
+
--code-border:#13283f;
|
|
26
|
+
--pill-border:#cfd9de;
|
|
27
|
+
--shadow-card:0 4px 14px rgba(15,20,25,.05);
|
|
28
|
+
--scrollbar:#cfd9de;
|
|
29
|
+
--hl-keyword:#1d9bf0;
|
|
30
|
+
--hl-string:#0a8a4e;
|
|
31
|
+
--hl-number:#a35200;
|
|
32
|
+
--hl-comment:#8b98a5;
|
|
33
|
+
--hl-flag:#7856ff;
|
|
34
|
+
--hl-meta:#f4212e;
|
|
35
|
+
--hl-prompt:#71c9f8;
|
|
36
|
+
}
|
|
37
|
+
:root[data-theme="dark"]{
|
|
38
|
+
--ink:#e7e9ea;
|
|
39
|
+
--text:#e7e9ea;
|
|
40
|
+
--muted:#8b98a5;
|
|
41
|
+
--subtle:#71767b;
|
|
42
|
+
--bg:#000000;
|
|
43
|
+
--paper:#16181c;
|
|
44
|
+
--paper-2:#1d2125;
|
|
45
|
+
--accent:#1d9bf0;
|
|
46
|
+
--accent-soft:rgba(29,155,240,.18);
|
|
47
|
+
--accent-strong:#4fb1f3;
|
|
48
|
+
--line:#2f3336;
|
|
49
|
+
--line-strong:#3e4144;
|
|
50
|
+
--line-soft:#1c1f23;
|
|
51
|
+
--code-bg:#04090f;
|
|
52
|
+
--code-fg:#e7eef6;
|
|
53
|
+
--code-inline-bg:#1d2125;
|
|
54
|
+
--code-inline-fg:#e7e9ea;
|
|
55
|
+
--code-border:#1c2a3a;
|
|
56
|
+
--pill-border:#2f3336;
|
|
57
|
+
--shadow-card:0 4px 18px rgba(0,0,0,.55);
|
|
58
|
+
--scrollbar:#3e4144;
|
|
59
|
+
--hl-keyword:#4fb1f3;
|
|
60
|
+
--hl-string:#5fd29a;
|
|
61
|
+
--hl-number:#f0a868;
|
|
62
|
+
--hl-comment:#71767b;
|
|
63
|
+
--hl-flag:#a48cff;
|
|
64
|
+
--hl-meta:#ff6e7a;
|
|
65
|
+
--hl-prompt:#71c9f8;
|
|
66
|
+
}
|
|
67
|
+
:root{color-scheme:light}
|
|
68
|
+
:root[data-theme="dark"]{color-scheme:dark}
|
|
69
|
+
*{box-sizing:border-box}
|
|
70
|
+
html{scroll-behavior:smooth;scroll-padding-top:24px}
|
|
71
|
+
body{margin:0;background:var(--bg);color:var(--text);font-family:"Inter","Chirp","Twitter Chirp",ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif;line-height:1.6;overflow-x:hidden;-webkit-font-smoothing:antialiased;font-feature-settings:"cv02","cv03","cv04","cv11";transition:background-color .18s,color .18s}
|
|
72
|
+
::selection{background:var(--accent);color:#fff}
|
|
73
|
+
a{color:var(--accent);text-decoration:none;transition:color .12s}
|
|
74
|
+
a:hover{text-decoration:underline;text-underline-offset:.2em;color:var(--accent-strong)}
|
|
75
|
+
.shell{display:grid;grid-template-columns:272px minmax(0,1fr);min-height:100vh}
|
|
76
|
+
.sidebar{position:sticky;top:0;height:100vh;overflow:auto;padding:22px 20px;background:var(--paper);border-right:1px solid var(--line);scrollbar-width:thin;scrollbar-color:var(--line-strong) transparent;transition:background-color .18s,border-color .18s}
|
|
77
|
+
.sidebar::-webkit-scrollbar{width:6px}
|
|
78
|
+
.sidebar::-webkit-scrollbar-thumb{background:var(--line-strong);border-radius:6px}
|
|
79
|
+
.sidebar-head{display:flex;align-items:center;gap:10px;margin-bottom:22px}
|
|
80
|
+
.brand{display:flex;align-items:center;gap:11px;color:var(--ink);text-decoration:none;flex:1;min-width:0}
|
|
81
|
+
.brand:hover{text-decoration:none}
|
|
82
|
+
.brand .mark{display:inline-flex;align-items:center;justify-content:center;flex:0 0 36px;width:36px;height:36px;border-radius:10px;background:#020507;box-shadow:0 6px 16px rgba(29,155,240,.24);overflow:hidden}
|
|
83
|
+
.brand .mark img{width:30px;height:30px;display:block;object-fit:contain}
|
|
84
|
+
.brand strong{display:block;font-size:1.08rem;line-height:1.1;font-weight:700;letter-spacing:-.01em;color:var(--ink)}
|
|
85
|
+
.brand small{display:block;color:var(--muted);font-size:.74rem;margin-top:3px;font-weight:400}
|
|
86
|
+
.theme-toggle{display:inline-flex;align-items:center;justify-content:center;flex:0 0 auto;width:34px;height:34px;border-radius:999px;border:1px solid var(--line-strong);background:var(--paper);color:var(--muted);cursor:pointer;padding:0;transition:border-color .15s,color .15s,background-color .15s,transform .12s}
|
|
87
|
+
.theme-toggle:hover{border-color:var(--accent);color:var(--accent)}
|
|
88
|
+
.theme-toggle:active{transform:scale(.94)}
|
|
89
|
+
.theme-toggle svg{width:16px;height:16px;display:block}
|
|
90
|
+
.theme-icon-sun{display:none}
|
|
91
|
+
:root[data-theme="dark"] .theme-icon-sun{display:block}
|
|
92
|
+
:root[data-theme="dark"] .theme-icon-moon{display:none}
|
|
93
|
+
.search{display:block;margin:0 0 22px}
|
|
94
|
+
.search span{display:block;color:var(--muted);font-size:.7rem;font-weight:600;text-transform:uppercase;letter-spacing:.04em;margin-bottom:7px}
|
|
95
|
+
.search input{width:100%;border:1px solid var(--line-strong);background:var(--bg);border-radius:999px;padding:9px 14px;font:inherit;font-size:.9rem;color:var(--text);outline:none;transition:border-color .15s,box-shadow .15s,background-color .18s}
|
|
96
|
+
.search input:focus{border-color:var(--accent);box-shadow:0 0 0 3px var(--accent-soft)}
|
|
97
|
+
nav section{margin:0 0 18px}
|
|
98
|
+
nav h2{font-size:.66rem;color:var(--muted);text-transform:uppercase;letter-spacing:.06em;margin:0 0 6px;font-weight:700}
|
|
99
|
+
.nav-link{display:block;color:var(--text);text-decoration:none;border-radius:999px;padding:6px 12px;margin:1px 0;font-size:.9rem;line-height:1.4;transition:background .12s,color .12s}
|
|
100
|
+
.nav-link:hover{background:var(--paper-2);color:var(--ink);text-decoration:none}
|
|
101
|
+
.nav-link.active{background:var(--accent);color:#fff;font-weight:600}
|
|
102
|
+
:root[data-theme="dark"] .nav-link.active{background:var(--accent-soft);color:var(--accent-strong)}
|
|
103
|
+
main{min-width:0;padding:32px clamp(20px,4.5vw,56px) 80px;max-width:1180px;margin:0 auto;width:100%}
|
|
104
|
+
.hero{display:flex;align-items:flex-end;justify-content:space-between;gap:22px;border-bottom:1px solid var(--line);padding:8px 0 22px;margin-bottom:8px;flex-wrap:wrap}
|
|
105
|
+
.hero-text{min-width:0;flex:1 1 320px}
|
|
106
|
+
.eyebrow{margin:0 0 8px;color:var(--accent);font-weight:700;text-transform:uppercase;letter-spacing:.06em;font-size:.7rem}
|
|
107
|
+
.hero h1{font-size:2.25rem;line-height:1.1;letter-spacing:-.02em;margin:0;font-weight:800;color:var(--ink)}
|
|
108
|
+
.hero-meta{display:flex;gap:8px;flex:0 0 auto;flex-wrap:wrap}
|
|
109
|
+
.repo,.edit,.btn-ghost{border:1px solid var(--line-strong);color:var(--text);text-decoration:none;border-radius:999px;padding:7px 14px;font-weight:600;font-size:.83rem;background:var(--bg);transition:border-color .15s,color .15s,background .15s}
|
|
110
|
+
.repo:hover,.edit:hover,.btn-ghost:hover{border-color:var(--accent);color:var(--accent);text-decoration:none}
|
|
111
|
+
.edit{color:var(--muted)}
|
|
112
|
+
.home-hero{padding:18px 0 32px;margin-bottom:8px;border-bottom:1px solid var(--line);position:relative;overflow:hidden}
|
|
113
|
+
.home-hero .home-mark{position:absolute;right:-24px;top:-12px;width:310px;height:310px;object-fit:contain;opacity:.08;pointer-events:none;filter:drop-shadow(0 18px 32px rgba(29,155,240,.18))}
|
|
114
|
+
:root[data-theme="dark"] .home-hero .home-mark{opacity:.16}
|
|
115
|
+
.home-hero h1{font-size:3.4rem;line-height:1.02;letter-spacing:-.03em;margin:0 0 .35em;font-weight:800;color:var(--ink)}
|
|
116
|
+
.home-hero h1 .accent{color:var(--accent)}
|
|
117
|
+
.home-hero .lede{font-size:1.16rem;line-height:1.55;color:var(--text);margin:0 0 1.3em;max-width:60ch}
|
|
118
|
+
.home-cta{display:flex;flex-wrap:wrap;gap:10px;align-items:center;margin:0 0 18px}
|
|
119
|
+
.home-cta .btn{display:inline-flex;align-items:center;gap:7px;border-radius:999px;padding:11px 20px;font-weight:700;font-size:.92rem;text-decoration:none;transition:background .15s,border-color .15s,color .15s,transform .12s}
|
|
120
|
+
.home-cta .btn-primary{background:var(--accent);color:#fff;border:1px solid var(--accent)}
|
|
121
|
+
.home-cta .btn-primary:hover{background:var(--accent-strong);border-color:var(--accent-strong);text-decoration:none}
|
|
122
|
+
.home-cta .btn-ghost{padding:11px 20px}
|
|
123
|
+
.home-install{display:flex;align-items:center;gap:12px;background:var(--code-bg);color:var(--code-fg);border-radius:14px;padding:11px 11px 11px 17px;font:500 .9rem/1.2 "JetBrains Mono","SF Mono",ui-monospace,monospace;max-width:34em;border:1px solid var(--code-border)}
|
|
124
|
+
.home-install .prompt{color:var(--hl-prompt);user-select:none;flex:0 0 auto}
|
|
125
|
+
.home-install code{flex:1;background:transparent;border:0;color:var(--code-fg);font:inherit;padding:0;white-space:pre;overflow:hidden;text-overflow:ellipsis}
|
|
126
|
+
.home-install .copy{flex:0 0 auto;background:rgba(255,255,255,.08);color:var(--code-fg);border:1px solid rgba(255,255,255,.16);border-radius:999px;padding:5px 13px;font:600 .72rem/1 "Inter",sans-serif;cursor:pointer;transition:background .15s,border-color .15s}
|
|
127
|
+
.home-install .copy:hover{background:rgba(255,255,255,.16)}
|
|
128
|
+
.home-install .copy.copied{background:var(--accent);border-color:var(--accent)}
|
|
129
|
+
.home-services{display:flex;flex-wrap:wrap;gap:6px;margin:6px 0 18px}
|
|
130
|
+
.home-services span{display:inline-block;padding:4px 11px;border:1px solid var(--line-strong);border-radius:999px;font-size:.78rem;color:var(--muted);background:var(--paper);font-weight:500}
|
|
131
|
+
.feature-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:14px;margin:24px 0 8px}
|
|
132
|
+
.feature-grid .card{border:1px solid var(--line);background:var(--paper);border-radius:14px;padding:16px 18px;transition:border-color .15s,transform .12s}
|
|
133
|
+
.feature-grid .card:hover{border-color:var(--accent);transform:translateY(-1px)}
|
|
134
|
+
.feature-grid .card h3{margin:0 0 6px;font-size:1rem;font-weight:700;color:var(--ink);letter-spacing:-.01em}
|
|
135
|
+
.feature-grid .card p{margin:0;color:var(--muted);font-size:.9rem;line-height:1.5}
|
|
136
|
+
.doc-grid{display:grid;grid-template-columns:minmax(0,1fr);gap:48px;margin-top:24px}
|
|
137
|
+
.doc-grid-home{margin-top:8px}
|
|
138
|
+
@media(min-width:1180px){.doc-grid{grid-template-columns:minmax(0,72ch) 200px;justify-content:start}.doc-grid-home{grid-template-columns:minmax(0,76ch);justify-content:start}}
|
|
139
|
+
.doc{min-width:0;max-width:72ch;overflow-wrap:break-word}
|
|
140
|
+
.doc-home{max-width:76ch}
|
|
141
|
+
.doc h1{font-size:2.5rem;line-height:1.08;letter-spacing:-.02em;margin:0 0 .4em;font-weight:800;color:var(--ink)}
|
|
142
|
+
body:not(.home) .doc>h1:first-child{display:none}
|
|
143
|
+
.doc h2{font-size:1.45rem;line-height:1.2;margin:2em 0 .5em;font-weight:700;letter-spacing:-.01em;color:var(--ink);position:relative}
|
|
144
|
+
.doc h3{font-size:1.1rem;margin:1.7em 0 .35em;position:relative;font-weight:700;color:var(--ink);letter-spacing:-.005em}
|
|
145
|
+
.doc h4{font-size:.98rem;margin:1.4em 0 .25em;color:var(--ink);position:relative;font-weight:600}
|
|
146
|
+
.doc h2:first-child,.doc h3:first-child,.doc h4:first-child{margin-top:.2em}
|
|
147
|
+
.doc :is(h2,h3,h4) .anchor{position:absolute;left:-1.05em;top:0;color:var(--subtle);opacity:0;text-decoration:none;font-weight:400;padding-right:.3em;transition:opacity .12s,color .12s}
|
|
148
|
+
.doc :is(h2,h3,h4):hover .anchor{opacity:.7}
|
|
149
|
+
.doc :is(h2,h3,h4) .anchor:hover{opacity:1;color:var(--accent);text-decoration:none}
|
|
150
|
+
.doc p{margin:0 0 1.05em}
|
|
151
|
+
.doc ul,.doc ol{padding-left:1.3rem;margin:0 0 1.15em}
|
|
152
|
+
.doc li{margin:.25em 0}
|
|
153
|
+
.doc li>p{margin:0 0 .4em}
|
|
154
|
+
.doc strong{font-weight:700;color:var(--ink)}
|
|
155
|
+
.doc em{font-style:italic}
|
|
156
|
+
.doc code{font-family:"JetBrains Mono","SF Mono",ui-monospace,monospace;font-size:.84em;background:var(--code-inline-bg);border:1px solid var(--line);border-radius:6px;padding:.08em .42em;color:var(--code-inline-fg)}
|
|
157
|
+
.doc pre{position:relative;overflow:auto;background:var(--code-bg);color:var(--code-fg);border-radius:12px;padding:14px 18px;margin:1.3em 0;font-size:.85em;line-height:1.6;scrollbar-width:thin;scrollbar-color:#334155 transparent;border:1px solid var(--code-border)}
|
|
158
|
+
.doc pre::-webkit-scrollbar{height:8px;width:8px}
|
|
159
|
+
.doc pre::-webkit-scrollbar-thumb{background:#334155;border-radius:8px}
|
|
160
|
+
.doc pre code{display:block;background:transparent;border:0;color:inherit;padding:0;font-size:1em;white-space:pre}
|
|
161
|
+
.doc pre .copy{position:absolute;top:8px;right:8px;background:rgba(255,255,255,.06);color:var(--code-fg);border:1px solid rgba(255,255,255,.16);border-radius:999px;padding:3px 11px;font:600 .7rem/1 "Inter",sans-serif;cursor:pointer;opacity:0;transition:opacity .15s,background .15s,border-color .15s}
|
|
162
|
+
.doc pre:hover .copy,.doc pre .copy:focus{opacity:1}
|
|
163
|
+
.doc pre .copy:hover{background:rgba(255,255,255,.12)}
|
|
164
|
+
.doc pre .copy.copied{background:var(--accent);border-color:var(--accent);opacity:1}
|
|
165
|
+
.doc pre .hl-c{color:var(--hl-comment);font-style:italic}
|
|
166
|
+
.doc pre .hl-s{color:var(--hl-string)}
|
|
167
|
+
.doc pre .hl-n{color:var(--hl-number)}
|
|
168
|
+
.doc pre .hl-k{color:var(--hl-keyword);font-weight:600}
|
|
169
|
+
.doc pre .hl-f{color:var(--hl-flag)}
|
|
170
|
+
.doc pre .hl-m{color:var(--hl-meta);font-weight:600}
|
|
171
|
+
.doc pre .hl-p{color:var(--hl-prompt);user-select:none}
|
|
172
|
+
.doc pre .hl-cmd{color:var(--hl-keyword);font-weight:600}
|
|
173
|
+
.doc blockquote{margin:1.4em 0;padding:12px 18px;border-left:3px solid var(--accent);background:var(--accent-soft);border-radius:0 12px 12px 0;color:var(--text)}
|
|
174
|
+
.doc blockquote p:last-child{margin-bottom:0}
|
|
175
|
+
.doc table{width:100%;border-collapse:collapse;margin:1.2em 0;font-size:.92em}
|
|
176
|
+
.doc th,.doc td{border-bottom:1px solid var(--line);padding:9px 10px;text-align:left;vertical-align:top}
|
|
177
|
+
.doc th{font-weight:700;color:var(--ink);background:var(--paper);border-bottom:1px solid var(--line-strong)}
|
|
178
|
+
.doc hr{border:0;border-top:1px solid var(--line);margin:2.2em 0}
|
|
179
|
+
.toc{position:sticky;top:24px;align-self:start;font-size:.84rem;padding-left:14px;border-left:1px solid var(--line);max-height:calc(100vh - 48px);overflow:auto;scrollbar-width:thin;scrollbar-color:var(--line-strong) transparent}
|
|
180
|
+
.toc::-webkit-scrollbar{width:5px}
|
|
181
|
+
.toc::-webkit-scrollbar-thumb{background:var(--line-strong);border-radius:5px}
|
|
182
|
+
.toc h2{font-size:.66rem;color:var(--muted);text-transform:uppercase;letter-spacing:.06em;margin:0 0 10px;font-weight:700}
|
|
183
|
+
.toc a{display:block;color:var(--muted);text-decoration:none;padding:4px 0 4px 10px;line-height:1.35;border-left:2px solid transparent;margin-left:-12px;transition:color .12s,border-color .12s}
|
|
184
|
+
.toc a:hover{color:var(--ink);text-decoration:none}
|
|
185
|
+
.toc a.active{color:var(--accent);border-left-color:var(--accent);font-weight:600}
|
|
186
|
+
.toc-l3{padding-left:22px!important;font-size:.94em}
|
|
187
|
+
@media(max-width:1179px){.toc{display:none}}
|
|
188
|
+
.page-nav{display:grid;grid-template-columns:1fr 1fr;gap:14px;margin-top:48px;border-top:1px solid var(--line);padding-top:20px}
|
|
189
|
+
.page-nav>a{display:block;border:1px solid var(--line);background:var(--paper);border-radius:14px;padding:14px 18px;text-decoration:none;color:var(--text);transition:border-color .15s,transform .15s,box-shadow .15s,background-color .18s}
|
|
190
|
+
.page-nav>a:hover{border-color:var(--accent);text-decoration:none;color:var(--ink)}
|
|
191
|
+
.page-nav small{display:block;color:var(--muted);font-size:.7rem;text-transform:uppercase;letter-spacing:.06em;margin-bottom:5px;font-weight:700}
|
|
192
|
+
.page-nav span{display:block;font-weight:700;line-height:1.3;color:var(--ink)}
|
|
193
|
+
.page-nav-prev{text-align:left}
|
|
194
|
+
.page-nav-next{text-align:right;grid-column:2}
|
|
195
|
+
.page-nav-prev:only-child{grid-column:1}
|
|
196
|
+
.nav-toggle{display:none;position:fixed;top:14px;right:14px;top:calc(14px + env(safe-area-inset-top, 0px));right:calc(14px + env(safe-area-inset-right, 0px));z-index:20;width:40px;height:40px;border-radius:999px;background:var(--paper);border:1px solid var(--line-strong);color:var(--ink);cursor:pointer;padding:10px 9px;flex-direction:column;align-items:stretch;justify-content:space-between;box-shadow:var(--shadow-card)}
|
|
197
|
+
.nav-toggle span{display:block;width:100%;height:2px;flex:0 0 2px;background:currentColor;border-radius:2px;transition:transform .2s,opacity .2s}
|
|
198
|
+
.nav-toggle[aria-expanded="true"] span:nth-child(1){transform:translateY(8px) rotate(45deg)}
|
|
199
|
+
.nav-toggle[aria-expanded="true"] span:nth-child(2){opacity:0}
|
|
200
|
+
.nav-toggle[aria-expanded="true"] span:nth-child(3){transform:translateY(-8px) rotate(-45deg)}
|
|
201
|
+
@media(max-width:900px){
|
|
202
|
+
.shell{display:block}
|
|
203
|
+
.sidebar{position:fixed;inset:0 30% 0 0;max-width:320px;height:100vh;z-index:15;transform:translateX(-100%);transition:transform .25s ease,background-color .18s,border-color .18s;box-shadow:0 18px 40px rgba(0,0,0,.18);background:var(--paper);pointer-events:none}
|
|
204
|
+
.sidebar.open{transform:translateX(0);pointer-events:auto}
|
|
205
|
+
.nav-toggle{display:flex}
|
|
206
|
+
main{padding:64px 18px 56px}
|
|
207
|
+
.hero{padding-top:6px}
|
|
208
|
+
.hero h1{font-size:1.8rem}
|
|
209
|
+
.home-hero h1{font-size:2.4rem}
|
|
210
|
+
.doc h1{font-size:2.05rem}
|
|
211
|
+
.hero-meta{width:100%;justify-content:flex-start}
|
|
212
|
+
.home-hero{padding-top:8px}
|
|
213
|
+
.home-hero .home-mark{width:230px;height:230px;right:-64px;top:-34px}
|
|
214
|
+
.doc{padding:0}
|
|
215
|
+
.doc-grid{margin-top:18px;gap:24px}
|
|
216
|
+
.doc :is(h2,h3,h4) .anchor{display:none}
|
|
217
|
+
}
|
|
218
|
+
@media(max-width:520px){
|
|
219
|
+
main{padding:60px 14px 48px}
|
|
220
|
+
.doc pre{margin-left:-14px;margin-right:-14px;border-radius:0;border-left:0;border-right:0}
|
|
221
|
+
.home-install{flex-wrap:wrap}
|
|
222
|
+
}
|
|
223
|
+
`;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
export function js() {
|
|
227
|
+
return `
|
|
228
|
+
const themeRoot=document.documentElement;
|
|
229
|
+
function applyTheme(mode){themeRoot.dataset.theme=mode;document.querySelectorAll('[data-theme-toggle]').forEach(b=>b.setAttribute('aria-pressed',mode==='dark'?'true':'false'))}
|
|
230
|
+
function storedTheme(){try{return localStorage.getItem('theme')}catch(e){return null}}
|
|
231
|
+
function persistTheme(mode){try{localStorage.setItem('theme',mode)}catch(e){}}
|
|
232
|
+
applyTheme(themeRoot.dataset.theme==='dark'?'dark':'light');
|
|
233
|
+
document.querySelectorAll('[data-theme-toggle]').forEach(btn=>{btn.addEventListener('click',()=>{const next=themeRoot.dataset.theme==='dark'?'light':'dark';applyTheme(next);persistTheme(next)})});
|
|
234
|
+
const systemDark=window.matchMedia&&matchMedia('(prefers-color-scheme: dark)');
|
|
235
|
+
function onSystemChange(e){if(storedTheme())return;applyTheme(e.matches?'dark':'light')}
|
|
236
|
+
if(systemDark){if(systemDark.addEventListener)systemDark.addEventListener('change',onSystemChange);else if(systemDark.addListener)systemDark.addListener(onSystemChange)}
|
|
237
|
+
const sidebar=document.querySelector('.sidebar');
|
|
238
|
+
const toggle=document.querySelector('.nav-toggle');
|
|
239
|
+
const mobileNav=window.matchMedia('(max-width: 900px)');
|
|
240
|
+
const sidebarFocusable='a[href],button,input,select,textarea,[tabindex]';
|
|
241
|
+
function setSidebarFocusable(enabled){
|
|
242
|
+
sidebar?.querySelectorAll(sidebarFocusable).forEach((el)=>{
|
|
243
|
+
if(enabled){
|
|
244
|
+
if(el.dataset.sidebarTabindex!==undefined){
|
|
245
|
+
if(el.dataset.sidebarTabindex)el.setAttribute('tabindex',el.dataset.sidebarTabindex);
|
|
246
|
+
else el.removeAttribute('tabindex');
|
|
247
|
+
delete el.dataset.sidebarTabindex;
|
|
248
|
+
}
|
|
249
|
+
}else if(el.dataset.sidebarTabindex===undefined){
|
|
250
|
+
el.dataset.sidebarTabindex=el.getAttribute('tabindex')??'';
|
|
251
|
+
el.setAttribute('tabindex','-1');
|
|
252
|
+
}
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
function setSidebarOpen(open){
|
|
256
|
+
if(!sidebar||!toggle)return;
|
|
257
|
+
sidebar.classList.toggle('open',open);
|
|
258
|
+
toggle.setAttribute('aria-expanded',open?'true':'false');
|
|
259
|
+
if(mobileNav.matches){
|
|
260
|
+
sidebar.inert=!open;
|
|
261
|
+
if(open)sidebar.removeAttribute('aria-hidden');
|
|
262
|
+
else sidebar.setAttribute('aria-hidden','true');
|
|
263
|
+
setSidebarFocusable(open);
|
|
264
|
+
}else{
|
|
265
|
+
sidebar.inert=false;
|
|
266
|
+
sidebar.removeAttribute('aria-hidden');
|
|
267
|
+
setSidebarFocusable(true);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
setSidebarOpen(false);
|
|
271
|
+
toggle?.addEventListener('click',()=>setSidebarOpen(!sidebar?.classList.contains('open')));
|
|
272
|
+
document.addEventListener('click',(e)=>{if(!sidebar?.classList.contains('open'))return;if(sidebar.contains(e.target)||toggle?.contains(e.target))return;setSidebarOpen(false)});
|
|
273
|
+
document.addEventListener('keydown',(e)=>{if(e.key==='Escape')setSidebarOpen(false)});
|
|
274
|
+
const syncSidebarForViewport=()=>setSidebarOpen(sidebar?.classList.contains('open')??false);
|
|
275
|
+
if(mobileNav.addEventListener)mobileNav.addEventListener('change',syncSidebarForViewport);
|
|
276
|
+
else mobileNav.addListener?.(syncSidebarForViewport);
|
|
277
|
+
const input=document.getElementById('doc-search');
|
|
278
|
+
input?.addEventListener('input',()=>{const q=input.value.trim().toLowerCase();document.querySelectorAll('nav section').forEach(sec=>{let any=false;sec.querySelectorAll('.nav-link').forEach(a=>{const m=!q||a.textContent.toLowerCase().includes(q);a.style.display=m?'block':'none';if(m)any=true});sec.style.display=any?'block':'none'})});
|
|
279
|
+
function attachCopy(target,getText){const btn=document.createElement('button');btn.type='button';btn.className='copy';btn.textContent='Copy';btn.addEventListener('click',async()=>{try{await navigator.clipboard.writeText(getText());btn.textContent='Copied';btn.classList.add('copied');setTimeout(()=>{btn.textContent='Copy';btn.classList.remove('copied')},1400)}catch{btn.textContent='Failed';setTimeout(()=>{btn.textContent='Copy'},1400)}});target.appendChild(btn)}
|
|
280
|
+
document.querySelectorAll('.doc pre').forEach(pre=>attachCopy(pre,()=>pre.querySelector('code')?.textContent??''));
|
|
281
|
+
document.querySelectorAll('.home-install').forEach(el=>attachCopy(el,()=>el.querySelector('code')?.textContent??''));
|
|
282
|
+
const tocLinks=document.querySelectorAll('.toc a');
|
|
283
|
+
if(tocLinks.length){const map=new Map();tocLinks.forEach(a=>{const id=a.getAttribute('href').slice(1);const el=document.getElementById(id);if(el)map.set(el,a)});const setActive=l=>{tocLinks.forEach(x=>x.classList.remove('active'));l.classList.add('active')};const obs=new IntersectionObserver(entries=>{const visible=entries.filter(e=>e.isIntersecting).sort((a,b)=>a.boundingClientRect.top-b.boundingClientRect.top);if(visible.length){const link=map.get(visible[0].target);if(link)setActive(link)}},{rootMargin:'-15% 0px -65% 0px',threshold:0});map.forEach((_,el)=>obs.observe(el))}
|
|
284
|
+
`;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
export function preThemeScript() {
|
|
288
|
+
return `(function(){var s;try{s=localStorage.getItem('theme')}catch(e){}var d=window.matchMedia&&matchMedia('(prefers-color-scheme: dark)').matches;document.documentElement.dataset.theme=s||(d?'dark':'light')})();`;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
export function themeToggleHtml() {
|
|
292
|
+
return `<button class="theme-toggle" type="button" aria-label="Toggle dark mode" aria-pressed="false" data-theme-toggle>
|
|
293
|
+
<svg class="theme-icon-moon" viewBox="0 0 20 20" aria-hidden="true"><path d="M14.6 12.1A6.5 6.5 0 0 1 7.4 2.7a6.5 6.5 0 1 0 7.2 9.4z" fill="currentColor"/></svg>
|
|
294
|
+
<svg class="theme-icon-sun" viewBox="0 0 20 20" aria-hidden="true"><circle cx="10" cy="10" r="3.4" fill="currentColor"/><g stroke="currentColor" stroke-width="1.6" stroke-linecap="round"><line x1="10" y1="2" x2="10" y2="4"/><line x1="10" y1="16" x2="10" y2="18"/><line x1="2" y1="10" x2="4" y2="10"/><line x1="16" y1="10" x2="18" y2="10"/><line x1="4.2" y1="4.2" x2="5.6" y2="5.6"/><line x1="14.4" y1="14.4" x2="15.8" y2="15.8"/><line x1="4.2" y1="15.8" x2="5.6" y2="14.4"/><line x1="14.4" y1="5.6" x2="15.8" y2="4.2"/></g></svg>
|
|
295
|
+
</button>`;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
export function brandIconSvg() {
|
|
299
|
+
return `<svg viewBox="2 1 20 20" aria-hidden="true" xmlns="http://www.w3.org/2000/svg"><path fill="currentColor" d="M21.3 4.2c-.7.3-1.5.5-2.3.6.8-.5 1.5-1.3 1.8-2.2-.8.5-1.7.8-2.6 1A4.1 4.1 0 0 0 11.1 7c0 .3 0 .6.1.9A11.7 11.7 0 0 1 2.7 3.6c-.4.7-.6 1.5-.6 2.3 0 1.4.7 2.7 1.8 3.4-.7 0-1.3-.2-1.9-.5v.1c0 2 1.4 3.7 3.3 4.1-.4.1-.7.1-1.1.1-.3 0-.5 0-.8-.1.5 1.6 2 2.8 3.8 2.8a8.3 8.3 0 0 1-5.1 1.7c-.3 0-.7 0-1-.1A11.7 11.7 0 0 0 7.5 19c7.6 0 11.7-6.3 11.7-11.7v-.5c.8-.6 1.5-1.3 2.1-2.1z"/></svg>`;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
export function featherSvg() {
|
|
303
|
+
return `<svg viewBox="0 0 200 200" aria-hidden="true" xmlns="http://www.w3.org/2000/svg"><path fill="currentColor" d="M163 22c-30 4-58 19-78 41C53 88 41 117 47 144l-21 21c-3 3-3 7 0 10s7 3 10 0l21-21c27 6 56-6 81-32 22-22 36-49 41-79 1-9-7-15-16-21zM84 130c-1 0-2 0-3-1-1-2-1-4 1-5l60-60c1-1 4-1 5 1 1 2 1 4-1 5l-60 60c-1 1-2 1-2 0z"/></svg>`;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
export function faviconSvg() {
|
|
307
|
+
return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" role="img" aria-label="birdclaw">
|
|
308
|
+
<rect width="64" height="64" rx="14" fill="#1d9bf0"/>
|
|
309
|
+
<path fill="#ffffff" d="M50 18c-2 1-4 1.5-6 1.7 2.2-1.3 3.8-3.4 4.6-5.8-2 1.2-4.3 2-6.6 2.5a10.4 10.4 0 0 0-17.7 9.5c-8.6-.4-16.2-4.5-21.3-10.8-3 5.2-1.5 12 3.6 15.4-1.7 0-3.3-.5-4.7-1.2v.1c0 5 3.6 9.3 8.4 10.3-1 .3-1.9.4-2.9.4-.7 0-1.4-.1-2.1-.2 1.4 4.2 5.3 7.2 10 7.3a20.9 20.9 0 0 1-13 4.4c-.8 0-1.7 0-2.6-.1A29.5 29.5 0 0 0 16 56c19.2 0 29.7-15.9 29.7-29.7v-1.4C48 23.4 50 21 50 18z"/>
|
|
310
|
+
</svg>`;
|
|
311
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { spawn } from "node:child_process";
|
|
3
|
+
import { withSanitizedNodeOptions } from "./sanitize-node-options.mjs";
|
|
4
|
+
|
|
5
|
+
const cwd = process.cwd();
|
|
6
|
+
const vitestBin = path.join(cwd, "node_modules", "vitest", "vitest.mjs");
|
|
7
|
+
|
|
8
|
+
const child = spawn(process.execPath, [vitestBin, ...process.argv.slice(2)], {
|
|
9
|
+
cwd,
|
|
10
|
+
stdio: "inherit",
|
|
11
|
+
env: withSanitizedNodeOptions(process.env),
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
child.on("exit", (code, signal) => {
|
|
15
|
+
if (signal) {
|
|
16
|
+
process.kill(process.pid, signal);
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
process.exit(code ?? 0);
|
|
21
|
+
});
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export function sanitizeNodeOptions(value) {
|
|
2
|
+
if (!value?.trim()) {
|
|
3
|
+
return undefined;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
const sanitized = value
|
|
7
|
+
.split(/\s+/)
|
|
8
|
+
.filter((part) => part && !part.startsWith("--localstorage-file"))
|
|
9
|
+
.join(" ")
|
|
10
|
+
.trim();
|
|
11
|
+
|
|
12
|
+
return sanitized || undefined;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function withSanitizedNodeOptions(env = process.env) {
|
|
16
|
+
const nodeOptions = sanitizeNodeOptions(env.NODE_OPTIONS);
|
|
17
|
+
return {
|
|
18
|
+
...env,
|
|
19
|
+
...(nodeOptions
|
|
20
|
+
? { NODE_OPTIONS: nodeOptions }
|
|
21
|
+
: { NODE_OPTIONS: undefined }),
|
|
22
|
+
};
|
|
23
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { rmSync } from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { spawn } from "node:child_process";
|
|
4
|
+
import { withSanitizedNodeOptions } from "./sanitize-node-options.mjs";
|
|
5
|
+
|
|
6
|
+
const cwd = process.cwd();
|
|
7
|
+
const home = process.env.BIRDCLAW_HOME || path.join(cwd, ".playwright-home");
|
|
8
|
+
const port = process.env.BIRDCLAW_PLAYWRIGHT_PORT || "3000";
|
|
9
|
+
const viteBin = path.join(cwd, "node_modules", "vite", "bin", "vite.js");
|
|
10
|
+
|
|
11
|
+
rmSync(home, { recursive: true, force: true });
|
|
12
|
+
|
|
13
|
+
const child = spawn(
|
|
14
|
+
process.execPath,
|
|
15
|
+
[viteBin, "dev", "--port", port, "--host", "127.0.0.1"],
|
|
16
|
+
{
|
|
17
|
+
cwd,
|
|
18
|
+
stdio: "inherit",
|
|
19
|
+
env: {
|
|
20
|
+
...withSanitizedNodeOptions(process.env),
|
|
21
|
+
BIRDCLAW_HOME: home,
|
|
22
|
+
BIRDCLAW_DISABLE_LIVE_WRITES: "1",
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
child.on("exit", (code) => {
|
|
28
|
+
process.exit(code ?? 0);
|
|
29
|
+
});
|
package/src/cli.ts
CHANGED
|
@@ -1474,9 +1474,54 @@ program
|
|
|
1474
1474
|
{
|
|
1475
1475
|
cwd: packageRoot,
|
|
1476
1476
|
stdio: "inherit",
|
|
1477
|
+
detached: process.platform !== "win32",
|
|
1477
1478
|
},
|
|
1478
1479
|
);
|
|
1479
|
-
|
|
1480
|
+
const forwardedSignals = [
|
|
1481
|
+
"SIGINT",
|
|
1482
|
+
"SIGTERM",
|
|
1483
|
+
"SIGHUP",
|
|
1484
|
+
"SIGQUIT",
|
|
1485
|
+
] as const;
|
|
1486
|
+
const forwardSignal = (signal: NodeJS.Signals) => {
|
|
1487
|
+
if (child.exitCode === null && child.signalCode === null) {
|
|
1488
|
+
signalChild(signal);
|
|
1489
|
+
}
|
|
1490
|
+
};
|
|
1491
|
+
const signalChild = (signal: NodeJS.Signals) => {
|
|
1492
|
+
if (child.pid === undefined) {
|
|
1493
|
+
return;
|
|
1494
|
+
}
|
|
1495
|
+
const targetPid = process.platform === "win32" ? child.pid : -child.pid;
|
|
1496
|
+
try {
|
|
1497
|
+
process.kill(targetPid, signal);
|
|
1498
|
+
} catch (error) {
|
|
1499
|
+
if (
|
|
1500
|
+
!(
|
|
1501
|
+
typeof error === "object" &&
|
|
1502
|
+
error !== null &&
|
|
1503
|
+
"code" in error &&
|
|
1504
|
+
error.code === "ESRCH"
|
|
1505
|
+
)
|
|
1506
|
+
) {
|
|
1507
|
+
throw error;
|
|
1508
|
+
}
|
|
1509
|
+
}
|
|
1510
|
+
};
|
|
1511
|
+
const removeSignalHandlers = () => {
|
|
1512
|
+
for (const signal of forwardedSignals) {
|
|
1513
|
+
process.removeListener(signal, forwardSignal);
|
|
1514
|
+
}
|
|
1515
|
+
};
|
|
1516
|
+
for (const signal of forwardedSignals) {
|
|
1517
|
+
process.on(signal, forwardSignal);
|
|
1518
|
+
}
|
|
1519
|
+
child.on("exit", (code, signal) => {
|
|
1520
|
+
removeSignalHandlers();
|
|
1521
|
+
if (signal) {
|
|
1522
|
+
process.kill(process.pid, signal);
|
|
1523
|
+
return;
|
|
1524
|
+
}
|
|
1480
1525
|
process.exit(code ?? 0);
|
|
1481
1526
|
});
|
|
1482
1527
|
});
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { Link, useRouterState } from "@tanstack/react-router";
|
|
2
2
|
import {
|
|
3
3
|
Bell,
|
|
4
|
-
Bird,
|
|
5
4
|
Bookmark,
|
|
6
5
|
Heart,
|
|
7
6
|
Home,
|
|
@@ -25,6 +24,7 @@ import {
|
|
|
25
24
|
sidebarNavClass,
|
|
26
25
|
sidebarShellClass,
|
|
27
26
|
} from "#/lib/ui";
|
|
27
|
+
import { BirdclawMark } from "./BrandMark";
|
|
28
28
|
import { ThemeSlider } from "./ThemeSlider";
|
|
29
29
|
|
|
30
30
|
const links = [
|
|
@@ -48,12 +48,12 @@ export function AppNav() {
|
|
|
48
48
|
<div className="flex flex-col">
|
|
49
49
|
<Link to="/" className={sidebarBrandClass}>
|
|
50
50
|
<span className={sidebarBrandMarkClass}>
|
|
51
|
-
<
|
|
51
|
+
<BirdclawMark className="size-10" />
|
|
52
52
|
</span>
|
|
53
53
|
<span className={sidebarBrandCopyClass}>
|
|
54
54
|
<span className={sidebarBrandTitleClass}>birdclaw</span>
|
|
55
55
|
<span className={sidebarBrandTaglineClass}>
|
|
56
|
-
|
|
56
|
+
Fast search for your Twitter archive.
|
|
57
57
|
</span>
|
|
58
58
|
</span>
|
|
59
59
|
</Link>
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { cx, emptyStateClass } from "#/lib/ui";
|
|
2
|
+
|
|
3
|
+
export function BirdclawMark({
|
|
4
|
+
animated = false,
|
|
5
|
+
className,
|
|
6
|
+
}: {
|
|
7
|
+
animated?: boolean;
|
|
8
|
+
className?: string;
|
|
9
|
+
}) {
|
|
10
|
+
return (
|
|
11
|
+
<span
|
|
12
|
+
aria-hidden="true"
|
|
13
|
+
className={cx(
|
|
14
|
+
"birdclaw-mark relative inline-grid shrink-0 place-items-center",
|
|
15
|
+
animated && "birdclaw-mark-animated",
|
|
16
|
+
className,
|
|
17
|
+
)}
|
|
18
|
+
>
|
|
19
|
+
<img
|
|
20
|
+
alt=""
|
|
21
|
+
className="size-full object-contain drop-shadow-[0_10px_22px_var(--brand-shadow)]"
|
|
22
|
+
draggable={false}
|
|
23
|
+
src="/birdclaw-mark.png"
|
|
24
|
+
/>
|
|
25
|
+
</span>
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function BirdclawLoading({
|
|
30
|
+
label,
|
|
31
|
+
detail,
|
|
32
|
+
}: {
|
|
33
|
+
label: string;
|
|
34
|
+
detail?: string;
|
|
35
|
+
}) {
|
|
36
|
+
return (
|
|
37
|
+
<div className={cx(emptyStateClass, "birdclaw-state")}>
|
|
38
|
+
<BirdclawMark animated className="size-16" />
|
|
39
|
+
<div className="mt-3 text-[14px] font-semibold text-[var(--ink)]">
|
|
40
|
+
{label}
|
|
41
|
+
</div>
|
|
42
|
+
{detail ? (
|
|
43
|
+
<div className="mt-1 text-[13px] text-[var(--ink-soft)]">{detail}</div>
|
|
44
|
+
) : null}
|
|
45
|
+
</div>
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function BirdclawEmpty({
|
|
50
|
+
label,
|
|
51
|
+
detail,
|
|
52
|
+
}: {
|
|
53
|
+
label: string;
|
|
54
|
+
detail?: string;
|
|
55
|
+
}) {
|
|
56
|
+
return (
|
|
57
|
+
<div className={cx(emptyStateClass, "birdclaw-state")}>
|
|
58
|
+
<BirdclawMark className="size-12 opacity-75" />
|
|
59
|
+
<div className="mt-3 text-[14px] font-semibold text-[var(--ink)]">
|
|
60
|
+
{label}
|
|
61
|
+
</div>
|
|
62
|
+
{detail ? (
|
|
63
|
+
<div className="mt-1 text-[13px] text-[var(--ink-soft)]">{detail}</div>
|
|
64
|
+
) : null}
|
|
65
|
+
</div>
|
|
66
|
+
);
|
|
67
|
+
}
|
|
@@ -3,13 +3,13 @@ import { formatShortTimestamp } from "#/lib/present";
|
|
|
3
3
|
import type { EmbeddedTweet } from "#/lib/types";
|
|
4
4
|
import {
|
|
5
5
|
cx,
|
|
6
|
-
emptyStateClass,
|
|
7
6
|
feedActionIconClass,
|
|
8
7
|
feedRowHandleClass,
|
|
9
8
|
feedRowNameClass,
|
|
10
9
|
feedRowTimestampClass,
|
|
11
10
|
} from "#/lib/ui";
|
|
12
11
|
import { AvatarChip } from "./AvatarChip";
|
|
12
|
+
import { BirdclawEmpty, BirdclawLoading } from "./BrandMark";
|
|
13
13
|
import { ProfilePreview } from "./ProfilePreview";
|
|
14
14
|
import { TweetMediaGrid } from "./TweetMediaGrid";
|
|
15
15
|
import { TweetRichText } from "./TweetRichText";
|
|
@@ -27,8 +27,11 @@ export function ConversationThread({
|
|
|
27
27
|
}) {
|
|
28
28
|
if (loading) {
|
|
29
29
|
return (
|
|
30
|
-
<section className="mt-3 rounded-2xl border border-[var(--line)] bg-[var(--bg-card)]
|
|
31
|
-
|
|
30
|
+
<section className="mt-3 rounded-2xl border border-[var(--line)] bg-[var(--bg-card)]">
|
|
31
|
+
<BirdclawLoading
|
|
32
|
+
detail="Finding archived replies around this post"
|
|
33
|
+
label="Loading conversation"
|
|
34
|
+
/>
|
|
32
35
|
</section>
|
|
33
36
|
);
|
|
34
37
|
}
|
|
@@ -43,13 +46,11 @@ export function ConversationThread({
|
|
|
43
46
|
|
|
44
47
|
if (items.length <= 1) {
|
|
45
48
|
return (
|
|
46
|
-
<section
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
"
|
|
50
|
-
|
|
51
|
-
>
|
|
52
|
-
No other archived replies in this conversation.
|
|
49
|
+
<section className="mt-3 rounded-2xl border border-[var(--line)]">
|
|
50
|
+
<BirdclawEmpty
|
|
51
|
+
detail="This post has no other archived replies locally."
|
|
52
|
+
label="No thread context yet"
|
|
53
|
+
/>
|
|
53
54
|
</section>
|
|
54
55
|
);
|
|
55
56
|
}
|