tangled
alpha
login
or
join now
margin.at
/
margin
86
fork
atom
Write on the margins of the internet. Powered by the AT Protocol.
margin.at
extension
web
atproto
comments
86
fork
atom
overview
issues
4
pulls
1
pipelines
fix some mobile stuff
scanash.com
1 month ago
455f9fd2
0f4e727e
+68
-27
5 changed files
expand all
collapse all
unified
split
web
src
components
feed
MasonryFeed.tsx
navigation
MobileNav.tsx
ui
LayoutToggle.tsx
views
content
Url.tsx
UserUrl.tsx
+3
-3
web/src/components/feed/MasonryFeed.tsx
···
107
107
}
108
108
109
109
return (
110
110
-
<div className="columns-1 sm:columns-2 xl:columns-3 2xl:columns-4 gap-4 animate-fade-in">
110
110
+
<div className="columns-1 sm:columns-2 md:columns-3 xl:columns-4 gap-4 animate-fade-in">
111
111
{items.map((item) => (
112
112
<div key={item.uri || item.cid} className="break-inside-avoid mb-4">
113
113
<Card item={item} onDelete={handleDelete} />
···
158
158
onChange={handleTabChange}
159
159
/>
160
160
</div>
161
161
-
<LayoutToggle />
161
161
+
<LayoutToggle className="hidden sm:inline-flex" />
162
162
</div>
163
163
</div>
164
164
)}
165
165
166
166
{!showTabs && (
167
167
<div className="flex justify-end mb-4">
168
168
-
<LayoutToggle />
168
168
+
<LayoutToggle className="hidden sm:inline-flex" />
169
169
</div>
170
170
)}
171
171
+14
web/src/components/navigation/MobileNav.tsx
···
17
17
Highlighter,
18
18
X,
19
19
} from "lucide-react";
20
20
+
import { AppleIcon } from "../common/Icons";
20
21
21
22
export default function MobileNav() {
22
23
const user = useStore($user);
···
118
119
<Settings size={20} />
119
120
<span>Settings</span>
120
121
</Link>
122
122
+
123
123
+
<div className="h-px bg-surface-200 dark:bg-surface-700 my-2" />
124
124
+
125
125
+
<a
126
126
+
href="https://www.icloud.com/shortcuts/21c87edf29b046db892c9e57dac6d1fd"
127
127
+
target="_blank"
128
128
+
rel="noopener noreferrer"
129
129
+
className="flex items-center gap-3 p-3 rounded-xl hover:bg-surface-100 dark:hover:bg-surface-800 transition-colors text-surface-700 dark:text-surface-200"
130
130
+
onClick={closeMenu}
131
131
+
>
132
132
+
<AppleIcon size={20} />
133
133
+
<span>iOS Shortcut</span>
134
134
+
</a>
121
135
122
136
<div className="h-px bg-surface-200 dark:bg-surface-700 my-2" />
123
137
+7
-2
web/src/components/ui/LayoutToggle.tsx
···
8
8
} from "../../store/feedLayout";
9
9
import { clsx } from "clsx";
10
10
11
11
-
export default function LayoutToggle() {
11
11
+
export default function LayoutToggle({ className }: { className?: string }) {
12
12
const layout = useStore($feedLayout);
13
13
14
14
const options: { id: FeedLayout; icon: typeof List; label: string }[] = [
···
17
17
];
18
18
19
19
return (
20
20
-
<div className="inline-flex items-center rounded-lg border border-surface-200 dark:border-surface-700 p-0.5 bg-surface-100 dark:bg-surface-800/60">
20
20
+
<div
21
21
+
className={clsx(
22
22
+
"inline-flex items-center rounded-lg border border-surface-200 dark:border-surface-700 p-0.5 bg-surface-100 dark:bg-surface-800/60",
23
23
+
className,
24
24
+
)}
25
25
+
>
21
26
{options.map((opt) => (
22
27
<button
23
28
key={opt.id}
+25
-3
web/src/views/content/Url.tsx
···
17
17
Globe,
18
18
} from "lucide-react";
19
19
20
20
-
import { EmptyState, Tabs } from "../../components/ui";
20
20
+
import { EmptyState, Tabs, Input, Button } from "../../components/ui";
21
21
22
22
export default function UrlPage() {
23
23
const user = useStore($user);
···
187
187
Explore
188
188
</h1>
189
189
<p className="text-surface-500 dark:text-surface-400 max-w-md mx-auto mb-8">
190
190
-
Search for any URL in the sidebar to specific see annotations and
190
190
+
Search for any URL in the sidebar to see specific annotations and
191
191
highlights.
192
192
</p>
193
193
+
194
194
+
<form
195
195
+
onSubmit={(e) => {
196
196
+
e.preventDefault();
197
197
+
const formData = new FormData(e.currentTarget);
198
198
+
const q = formData.get("q") as string;
199
199
+
if (q?.trim()) {
200
200
+
navigate(`/url?q=${encodeURIComponent(q.trim())}`);
201
201
+
}
202
202
+
}}
203
203
+
className="max-w-md mx-auto mb-8 flex gap-2"
204
204
+
>
205
205
+
<div className="flex-1">
206
206
+
<Input
207
207
+
name="q"
208
208
+
placeholder="https://example.com"
209
209
+
className="w-full bg-surface-50 dark:bg-surface-800"
210
210
+
autoFocus
211
211
+
/>
212
212
+
</div>
213
213
+
<Button type="submit">Search</Button>
214
214
+
</form>
193
215
194
216
{recentSearches.length > 0 && (
195
217
<div className="text-left max-w-lg mx-auto bg-surface-50 dark:bg-surface-800/50 rounded-2xl p-6 border border-surface-100 dark:border-surface-800">
···
259
281
className="flex items-center gap-1.5 px-3 py-1.5 bg-surface-100 dark:bg-surface-800 hover:bg-surface-200 dark:hover:bg-surface-700 text-surface-900 dark:text-white text-sm font-medium rounded-lg transition-colors"
260
282
>
261
283
{copied ? <Check size={14} /> : <Copy size={14} />}
262
262
-
{copied ? "Copied" : "Share"}
284
284
+
{copied ? "Copied" : "Share your thoughts on this URL"}
263
285
</button>
264
286
)}
265
287
</div>
+19
-19
web/src/views/content/UserUrl.tsx
···
135
135
136
136
return (
137
137
<div className="max-w-3xl mx-auto pb-20">
138
138
-
<header className="flex items-center gap-6 mb-8 p-6 bg-white rounded-2xl border border-surface-200 shadow-sm">
138
138
+
<header className="flex items-center gap-6 mb-8 p-6 bg-white dark:bg-surface-800 rounded-2xl border border-surface-200 dark:border-surface-700 shadow-sm">
139
139
<a
140
140
href={bskyProfileUrl}
141
141
target="_blank"
···
146
146
<img
147
147
src={avatarUrl}
148
148
alt={displayName}
149
149
-
className="w-20 h-20 rounded-full object-cover border-4 border-surface-50"
149
149
+
className="w-20 h-20 rounded-full object-cover border-4 border-surface-50 dark:border-surface-700"
150
150
/>
151
151
) : (
152
152
-
<div className="w-20 h-20 rounded-full bg-surface-100 flex items-center justify-center text-2xl font-bold text-surface-500 border-4 border-surface-50">
152
152
+
<div className="w-20 h-20 rounded-full bg-surface-100 dark:bg-surface-700 flex items-center justify-center text-2xl font-bold text-surface-500 dark:text-surface-400 border-4 border-surface-50 dark:border-surface-700">
153
153
{getInitial()}
154
154
</div>
155
155
)}
156
156
</a>
157
157
<div className="flex-1">
158
158
-
<h1 className="text-2xl font-bold text-surface-900 mb-1">
158
158
+
<h1 className="text-2xl font-bold text-surface-900 dark:text-white mb-1">
159
159
{displayName}
160
160
</h1>
161
161
{displayHandle && (
···
163
163
href={bskyProfileUrl}
164
164
target="_blank"
165
165
rel="noopener noreferrer"
166
166
-
className="text-surface-500 hover:text-primary-600 transition-colors bg-surface-50 hover:bg-primary-50 px-2 py-1 rounded-md text-sm inline-flex items-center gap-1"
166
166
+
className="text-surface-500 dark:text-surface-400 hover:text-primary-600 transition-colors bg-surface-50 dark:bg-surface-700 hover:bg-primary-50 dark:hover:bg-primary-900/30 px-2 py-1 rounded-md text-sm inline-flex items-center gap-1"
167
167
>
168
168
@{displayHandle} <ExternalLink size={12} />
169
169
</a>
···
171
171
</div>
172
172
</header>
173
173
174
174
-
<div className="mb-8 p-4 bg-surface-50 border border-surface-200 rounded-xl flex flex-col sm:flex-row sm:items-center gap-4">
175
175
-
<span className="text-sm font-semibold text-surface-500 uppercase tracking-wide">
174
174
+
<div className="mb-8 p-4 bg-surface-50 dark:bg-surface-800 border border-surface-200 dark:border-surface-700 rounded-xl flex flex-col sm:flex-row sm:items-center gap-4">
175
175
+
<span className="text-sm font-semibold text-surface-500 dark:text-surface-400 uppercase tracking-wide">
176
176
Annotations on:
177
177
</span>
178
178
<a
···
199
199
)}
200
200
201
201
{!loading && !error && totalItems === 0 && (
202
202
-
<div className="text-center py-16 bg-surface-50 rounded-2xl border border-dashed border-surface-200">
203
203
-
<div className="w-12 h-12 bg-surface-100 rounded-full flex items-center justify-center mx-auto mb-4 text-surface-400">
202
202
+
<div className="text-center py-16 bg-surface-50 dark:bg-surface-800 rounded-2xl border border-dashed border-surface-200 dark:border-surface-700">
203
203
+
<div className="w-12 h-12 bg-surface-100 dark:bg-surface-700 rounded-full flex items-center justify-center mx-auto mb-4 text-surface-400">
204
204
<PenTool size={24} />
205
205
</div>
206
206
-
<h3 className="text-lg font-bold text-surface-900 mb-1">
206
206
+
<h3 className="text-lg font-bold text-surface-900 dark:text-white mb-1">
207
207
No items found
208
208
</h3>
209
209
-
<p className="text-surface-500">
209
209
+
<p className="text-surface-500 dark:text-surface-400">
210
210
{displayName} hasn't annotated this page yet.
211
211
</p>
212
212
</div>
···
215
215
{!loading && !error && totalItems > 0 && (
216
216
<div className="animate-fade-in">
217
217
<div className="flex flex-col md:flex-row md:items-center justify-between gap-4 mb-6">
218
218
-
<h2 className="text-xl font-bold text-surface-900">
218
218
+
<h2 className="text-xl font-bold text-surface-900 dark:text-white">
219
219
{totalItems} item{totalItems !== 1 ? "s" : ""}
220
220
</h2>
221
221
-
<div className="flex bg-surface-100 p-1 rounded-xl self-start md:self-auto">
221
221
+
<div className="flex bg-surface-100 dark:bg-surface-800 p-1 rounded-xl self-start md:self-auto">
222
222
<button
223
223
className={clsx(
224
224
"px-4 py-1.5 rounded-lg text-sm font-medium transition-all",
225
225
activeTab === "all"
226
226
-
? "bg-white text-surface-900 shadow-sm"
227
227
-
: "text-surface-500 hover:text-surface-700",
226
226
+
? "bg-white dark:bg-surface-700 text-surface-900 dark:text-white shadow-sm"
227
227
+
: "text-surface-500 dark:text-surface-400 hover:text-surface-700 dark:hover:text-surface-200",
228
228
)}
229
229
onClick={() => setActiveTab("all")}
230
230
>
···
234
234
className={clsx(
235
235
"px-4 py-1.5 rounded-lg text-sm font-medium transition-all",
236
236
activeTab === "annotations"
237
237
-
? "bg-white text-surface-900 shadow-sm"
238
238
-
: "text-surface-500 hover:text-surface-700",
237
237
+
? "bg-white dark:bg-surface-700 text-surface-900 dark:text-white shadow-sm"
238
238
+
: "text-surface-500 dark:text-surface-400 hover:text-surface-700 dark:hover:text-surface-200",
239
239
)}
240
240
onClick={() => setActiveTab("annotations")}
241
241
>
···
245
245
className={clsx(
246
246
"px-4 py-1.5 rounded-lg text-sm font-medium transition-all",
247
247
activeTab === "highlights"
248
248
-
? "bg-white text-surface-900 shadow-sm"
249
249
-
: "text-surface-500 hover:text-surface-700",
248
248
+
? "bg-white dark:bg-surface-700 text-surface-900 dark:text-white shadow-sm"
249
249
+
: "text-surface-500 dark:text-surface-400 hover:text-surface-700 dark:hover:text-surface-200",
250
250
)}
251
251
onClick={() => setActiveTab("highlights")}
252
252
>