-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy paththread-unroll.html
More file actions
274 lines (263 loc) · 28.1 KB
/
Copy paththread-unroll.html
File metadata and controls
274 lines (263 loc) · 28.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
<!doctype html>
<!-- Generated by scripts/site.mjs from scripts/site-content.mjs. Edit those, not this file. -->
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="color-scheme" content="light dark">
<title>Thread unroller for X (Twitter), free · x-utils</title>
<meta name="description" content="Free thread unroller for X (Twitter): open any post of a thread, run the tool, and get the whole thread as clean text, a readable page and a Markdown file.">
<link rel="canonical" href="https://x-utils.com/thread-unroll">
<meta property="og:title" content="Thread unroller for X (Twitter), free">
<meta property="og:description" content="Free thread unroller for X (Twitter): open any post of a thread, run the tool, and get the whole thread as clean text, a readable page and a Markdown file.">
<meta property="og:image" content="https://x-utils.com/docs/assets/social-preview.png">
<meta property="og:url" content="https://x-utils.com/thread-unroll">
<meta property="og:type" content="website">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:site" content="@devploit">
<meta name="twitter:creator" content="@devploit">
<meta name="twitter:title" content="Thread unroller for X (Twitter), free">
<meta name="twitter:description" content="Free thread unroller for X (Twitter): open any post of a thread, run the tool, and get the whole thread as clean text, a readable page and a Markdown file.">
<meta name="twitter:image" content="https://x-utils.com/docs/assets/social-preview.png">
<link rel="icon" href="data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2032%2032%22%3E%3Crect%20width%3D%2232%22%20height%3D%2232%22%20rx%3D%227%22%20fill%3D%22%230d1220%22%2F%3E%3Cpath%20d%3D%22M8%208l16%2016M24%208L8%2024%22%20stroke%3D%22%23b3a6ff%22%20stroke-width%3D%224%22%20stroke-linecap%3D%22round%22%2F%3E%3C%2Fsvg%3E">
<script type="application/ld+json">{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"name":"x-utils","item":"https://x-utils.com/"},{"@type":"ListItem","position":2,"name":"Read a whole X (Twitter) thread as clean text","item":"https://x-utils.com/thread-unroll"}]}</script>
<script type="application/ld+json">{"@context":"https://schema.org","@type":"HowTo","name":"Thread unroller for X (Twitter), free","description":"Free thread unroller for X (Twitter): open any post of a thread, run the tool, and get the whole thread as clean text, a readable page and a Markdown file.","totalTime":"PT5M","tool":{"@type":"HowToTool","name":"A desktop browser"},"step":[{"@type":"HowToStep","position":1,"name":"Open any post of the thread on X","text":"On a computer, log in to X and open x.com/author/status/…. If the page was already open, reload it right before pasting: the tool reads the list as X loads it."},{"@type":"HowToStep","position":2,"name":"Open the browser console","text":"Press Cmd+Option+I on a Mac or F12 on Windows and Linux, then click the Console tab."},{"@type":"HowToStep","position":3,"name":"Copy the script","text":"Use the copy button on this page. The script is plain text you can read; it lives at x-utils.com/dist/thread-unroll.js."},{"@type":"HowToStep","position":4,"name":"Paste it in the console and press Enter","text":"The first time, the browser asks you to type \"allow pasting\" first. Type it, press Enter, paste again."},{"@type":"HowToStep","position":5,"name":"Wait, then open your report","text":"Under a minute. The report and a CSV land in your Downloads folder."}]}</script>
<script type="application/ld+json">{"@context":"https://schema.org","@type":"FAQPage","mainEntity":[{"@type":"Question","name":"Which posts count as the thread?","acceptedAnswer":{"@type":"Answer","text":"The author's own replies chained to the first post, plus contiguous posts by the same author shown on screen. Other people's replies are ignored."}}]}</script>
<style>
:root{--band:#0d1220;--band-2:#161d33;--band-ink:#f4f6fb;--band-muted:#8f9ab3;--ground:#eef0f4;--surface:#fff;--surface-2:#f6f7fa;--ink:#0d1220;--muted:#66708a;--line:#e3e6ee;--accent:#5b3df5;--accent-soft:rgba(91,61,245,.12);--good:#12805c;--good-soft:rgba(18,128,92,.12);--bad:#d2481d;--warn:#a86b0f;--warn-soft:rgba(168,107,15,.14);--mono:ui-monospace,"SF Mono",Menlo,Consolas,monospace;--sans:-apple-system,BlinkMacSystemFont,"Segoe UI",Inter,Roboto,Helvetica,Arial,sans-serif;--shadow:0 1px 2px rgba(13,18,32,.06),0 12px 32px -16px rgba(13,18,32,.18)}
@media (prefers-color-scheme:dark){:root{--band:#080b14;--band-2:#0f1424;--ground:#0b0f19;--surface:#121826;--surface-2:#171e2e;--ink:#e8ecf5;--muted:#8d97ad;--line:#222b3d;--accent:#a394ff;--accent-soft:rgba(163,148,255,.16);--good:#4fd39b;--good-soft:rgba(79,211,155,.14);--bad:#ff9a6c;--warn:#e2b35c;--warn-soft:rgba(226,179,92,.16);--shadow:0 1px 2px rgba(0,0,0,.4),0 16px 40px -20px rgba(0,0,0,.8)}}
*{box-sizing:border-box}html{scroll-behavior:smooth}@media (prefers-reduced-motion:reduce){html{scroll-behavior:auto}}
body{margin:0;background:var(--ground);color:var(--ink);font:16px/1.55 var(--sans);-webkit-font-smoothing:antialiased}
a{color:var(--accent);text-decoration:none}a:hover{text-decoration:underline}
:focus-visible{outline:2px solid var(--accent);outline-offset:2px;border-radius:4px}
svg{width:1em;height:1em;fill:none;stroke:currentColor;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;vertical-align:-.15em}
.wrap{max-width:1120px;margin:0 auto;padding:0 28px}
.mono{font-family:var(--mono)}
kbd{font:500 12.5px var(--mono);padding:2px 7px;border:1px solid var(--line);border-bottom-width:2px;border-radius:6px;background:var(--surface-2);color:var(--ink);white-space:nowrap}
.sr-only{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0)}
/* masthead */
.band{background:radial-gradient(900px 360px at 12% -20%,rgba(120,96,255,.35),transparent 65%),radial-gradient(700px 300px at 95% 10%,rgba(46,196,182,.18),transparent 60%),linear-gradient(180deg,var(--band-2),var(--band));color:var(--band-ink);padding:28px 0 72px}
.nav{display:flex;justify-content:space-between;align-items:center;gap:16px;color:var(--band-muted)}
.nav .brand{display:inline-flex;align-items:center;gap:10px;color:var(--band-ink);font:700 18px/1 var(--sans);letter-spacing:-.02em;text-decoration:none!important}
.nav .brand i{width:14px;height:14px;border-radius:4px;background:linear-gradient(135deg,#b3a6ff,#5ee0a8)}
.nav .brand:hover{color:#b3a6ff}
.nav .links{display:flex;align-items:center;gap:4px;font:500 13.5px var(--sans)}
.nav .links a{color:var(--band-muted);padding:7px 11px;border-radius:8px;text-decoration:none}
.nav .links a:hover{color:var(--band-ink);background:rgba(255,255,255,.06)}
.nav .links a.active{color:var(--band-ink);background:rgba(255,255,255,.08)}
.nav .links a.go{background:#b3a6ff;color:#0d1220;font-weight:650;margin-left:6px}.nav .links a.go:hover{background:#c4baff}
.hero{display:grid;grid-template-columns:minmax(0,1.05fr) minmax(0,1fr);gap:40px;align-items:end;padding:56px 0 0}
.hero h1{margin:0 0 16px;font-size:clamp(34px,4.6vw,56px);font-weight:750;letter-spacing:-.035em;line-height:1.02;text-wrap:balance}
.hero p{margin:0 0 22px;font-size:18px;color:var(--band-muted);max-width:52ch}
.pills{display:flex;flex-wrap:wrap;gap:8px;margin-bottom:26px}
.pill{display:inline-flex;align-items:center;gap:6px;font:500 12.5px var(--mono);padding:5px 10px;border-radius:999px;border:1px solid rgba(255,255,255,.14);color:#c7cede}
.pill svg{color:#5ee0a8}
.cta{display:inline-flex;align-items:center;gap:8px;height:46px;padding:0 18px;border-radius:10px;background:#b3a6ff;color:#0d1220;font-weight:650;text-decoration:none!important}
.cta:hover{background:#c4baff}
.cta.quiet{background:rgba(255,255,255,.08);color:var(--band-ink);margin-left:8px}
.hero-shot{position:relative;border-radius:14px;overflow:hidden;box-shadow:0 30px 80px -30px rgba(0,0,0,.8);border:1px solid rgba(255,255,255,.1);transform:translateY(104px)}
.hero-shot img{display:block;width:100%;height:auto}
/* content */
.content{padding:96px 0 64px}
h2{font-size:28px;letter-spacing:-.02em;margin:0 0 6px}
h3{font-size:18px;letter-spacing:-.01em;margin:0 0 8px}
.lead{color:var(--muted);margin:0 0 24px;max-width:70ch;font-size:16.5px}
section{margin-bottom:72px}
.card{background:var(--surface);border:1px solid var(--line);border-radius:14px;box-shadow:var(--shadow)}
/* three-beat explanation */
.beats{display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:14px}
.beat{padding:20px 22px}
.beat .ic{display:inline-grid;place-items:center;width:38px;height:38px;border-radius:10px;background:var(--accent-soft);color:var(--accent);font-size:18px;margin-bottom:12px}
.beat p{margin:0;color:var(--muted);font-size:15px}
/* guided setup */
.guide{padding:0;overflow:hidden}
.guide-head{padding:22px 24px 18px;border-bottom:1px solid var(--line);display:flex;flex-wrap:wrap;gap:16px 28px;align-items:flex-start}
.field{display:flex;flex-direction:column;gap:6px;min-width:220px;flex:1}
.field label{font-size:13px;font-weight:600;color:var(--muted)}
.field select,.field .inp{font:inherit;height:44px;padding:0 12px;border:1px solid var(--line);border-radius:10px;background:var(--surface-2);color:var(--ink);width:100%}
.field .inp{display:flex;align-items:center;gap:4px;padding:0 12px}
.field .inp span{color:var(--muted);font-family:var(--mono);font-size:14px}
.field .inp input{border:0;background:transparent;font:inherit;color:var(--ink);width:100%;outline:none;font-family:var(--mono)}
.field:focus-within select,.field:focus-within .inp{border-color:var(--accent);box-shadow:0 0 0 3px var(--accent-soft)}
.field .hint{font-size:12.5px;color:var(--muted)}
.gsteps{counter-reset:g}
.gstep{display:grid;grid-template-columns:56px minmax(0,1fr);gap:0 18px;padding:22px 24px;border-bottom:1px solid var(--line)}
.gstep.has-aside{grid-template-columns:56px minmax(0,1fr) minmax(0,460px)}
.gstep .aside{grid-column:3;align-self:start}
.gstep .aside .mock,.gstep .aside .panel-mock{margin-top:0}
.gstep:last-child{border-bottom:0}
.gstep::before{counter-increment:g;content:counter(g);display:grid;place-items:center;width:40px;height:40px;border-radius:12px;background:var(--ink);color:var(--surface);font:700 16px var(--mono)}
.gstep h3{margin:6px 0 6px}
.gstep p{margin:0 0 12px;color:var(--muted);font-size:15.5px;max-width:64ch}
.gstep p:last-child{margin-bottom:0}
.gstep .row{display:flex;flex-wrap:wrap;gap:10px;align-items:center}
.btn{display:inline-flex;align-items:center;gap:8px;height:42px;padding:0 16px;border-radius:10px;border:1px solid var(--line);background:var(--surface);color:var(--ink);font:600 15px var(--sans);cursor:pointer;text-decoration:none!important;white-space:nowrap}
.btn:hover{background:var(--surface-2)}.btn.primary{background:var(--accent);border-color:var(--accent);color:#fff}.btn.primary:hover{filter:brightness(.94)}.btn.done{border-color:var(--good);color:var(--good);background:var(--good-soft)}
.btn.sm{height:34px;padding:0 12px;font-size:13.5px;font-weight:500}
.btn.use{color:var(--accent);border-color:color-mix(in srgb,var(--accent) 40%,var(--line));background:var(--surface)}.btn.use:hover{background:var(--accent-soft)}
.tool.featured .btn.use{background:var(--accent);border-color:var(--accent);color:#fff}
.urlbox{display:inline-flex;align-items:center;gap:0;font:14px var(--mono);padding:10px 14px;border-radius:10px;background:var(--surface-2);border:1px solid var(--line);color:var(--ink);word-break:break-all}
.urlbox .ph{color:var(--accent);background:var(--accent-soft);border-radius:5px;padding:0 5px}
.btn.disabled{opacity:.55;pointer-events:none}
.need-handle{font-size:13.5px;color:var(--warn);margin:8px 0 0}
.tabs{display:inline-flex;gap:4px;padding:4px;border:1px solid var(--line);border-radius:10px;background:var(--surface-2);margin-bottom:12px}
.tabs button{font:500 13.5px var(--sans);padding:7px 12px;border:0;border-radius:7px;background:transparent;color:var(--muted);cursor:pointer}
.tabs button[aria-selected="true"]{background:var(--surface);color:var(--ink);box-shadow:0 1px 2px rgba(0,0,0,.08)}
.keys{font-size:15.5px;line-height:2;margin:2px 0 8px}.keys kbd{margin:0 2px}.keys[hidden]{display:none}
.note{display:flex;gap:10px;align-items:flex-start;padding:12px 14px;border-radius:10px;background:var(--warn-soft);color:var(--ink);font-size:14.5px;max-width:64ch}
.note svg{flex:0 0 auto;color:var(--warn);margin-top:3px}
.note.good{background:var(--good-soft)}.note.good svg{color:var(--good)}
/* mocks */
.mock{margin-top:14px;border:1px solid var(--line);border-radius:12px;overflow:hidden;background:#1e1e1e;color:#d4d4d4;font:13px/1.5 var(--mono);max-width:640px;box-shadow:var(--shadow)}
.mock .bar{display:flex;gap:2px;padding:6px 8px 0;background:#2b2b2b;border-bottom:1px solid #3a3a3a;font:12.5px var(--sans);color:#a9a9a9}
.mock .bar span{padding:6px 10px;border-radius:6px 6px 0 0}
.mock .bar .on{background:#1e1e1e;color:#fff;box-shadow:inset 0 -2px 0 #b3a6ff}
.mock .body{padding:12px 14px;min-height:96px;white-space:pre-wrap;word-break:break-word}
.mock .warn{color:#e9c46a}.mock .prompt{color:#7c7cff}.mock .dim{color:#7a7a7a}.mock .ok{color:#5ee0a8}
.mock .caret{display:inline-block;width:8px;height:15px;background:#d4d4d4;vertical-align:-3px;margin-left:2px}
.panel-mock{margin-top:14px;width:320px;max-width:100%;background:#0d1220;color:#f4f6fb;border:1px solid rgba(255,255,255,.12);border-radius:14px;padding:14px 16px 12px;font:13.5px/1.45 var(--sans);box-shadow:0 20px 50px -20px rgba(0,0,0,.6)}
.panel-mock .h{display:flex;align-items:center;gap:10px;margin-bottom:8px;font:600 11.5px var(--mono);letter-spacing:.08em;text-transform:uppercase;color:#c7cede}
.panel-mock .h i{width:10px;height:10px;border-radius:3px;background:linear-gradient(135deg,#b3a6ff,#5ee0a8)}
.panel-mock .s{color:#5ee0a8}
.panel-mock .p{height:4px;border-radius:2px;background:linear-gradient(90deg,#b3a6ff,#5ee0a8);margin:10px 0 8px}
.panel-mock .f{display:flex;justify-content:space-between;align-items:center;font-size:12px;color:#8f9ab3}
.panel-mock .f{gap:12px}.panel-mock .f b{background:#b3a6ff;color:#0d1220;border-radius:8px;padding:6px 11px;font-size:12.5px;white-space:nowrap;flex:0 0 auto}
/* tools */
.quiet-link{border-color:transparent;color:var(--muted)}.quiet-link:hover{color:var(--accent)}
.tools{display:grid;grid-template-columns:repeat(auto-fill,minmax(320px,1fr));gap:14px}
.tool{padding:18px 18px 14px;display:flex;flex-direction:column;gap:8px}
.tool.featured{border-color:color-mix(in srgb,var(--accent) 45%,var(--line))}
.tool h3{margin:0;display:flex;align-items:center;gap:8px;flex-wrap:wrap}
.tool .tag{font:500 10.5px var(--mono);padding:2px 7px;border-radius:5px;background:var(--accent-soft);color:var(--accent);text-transform:uppercase;letter-spacing:.06em}
.tool .tag-slow{background:var(--warn-soft);color:var(--warn)}
.tool p{margin:0;color:var(--muted);font-size:14.5px;flex:1}
.tool .page{font:12.5px var(--mono);color:var(--muted);word-break:break-all}
.tool .row{display:flex;gap:8px;margin-top:6px;flex-wrap:wrap}
.group{font:600 12px var(--mono);letter-spacing:.1em;text-transform:uppercase;color:var(--muted);margin:26px 0 12px}
.group:first-of-type{margin-top:0}
/* gallery */
.gallery{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:14px}
.gallery a{display:block;overflow:hidden;color:inherit;text-decoration:none!important}
.gallery img{display:block;width:100%;height:auto;aspect-ratio:3/2;object-fit:cover;object-position:top}
.gallery b{display:block;padding:12px 14px 4px}.gallery span{display:block;padding:0 14px 12px;color:var(--muted);font-size:13.5px}
/* safety */
.safety{display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:14px}
.safety .card{padding:18px 20px}
.safety .card b{display:flex;align-items:center;gap:8px;margin-bottom:6px}.safety .card b svg{color:var(--good)}
.safety .card p{margin:0;color:var(--muted);font-size:14.5px}
/* troubleshooting + faq */
details{border-top:1px solid var(--line)}
details:last-of-type{border-bottom:1px solid var(--line)}
summary{cursor:pointer;padding:14px 0;font-weight:600;list-style:none;display:flex;justify-content:space-between;gap:12px}
summary::-webkit-details-marker{display:none}
summary::after{content:"+";color:var(--muted);font-weight:400;font-size:20px;line-height:1}
details[open] summary::after{content:"–"}
details p{margin:0 0 14px;color:var(--muted);font-size:15px;max-width:72ch}
.two{display:grid;grid-template-columns:repeat(auto-fit,minmax(320px,1fr));gap:0 40px}
footer{color:var(--muted);font-size:13px;display:flex;flex-wrap:wrap;gap:8px 20px;justify-content:space-between;padding-top:22px;border-top:1px solid var(--line)}
.toast{position:fixed;left:50%;bottom:24px;transform:translateX(-50%) translateY(20px);background:var(--ink);color:var(--surface);padding:10px 16px;border-radius:10px;font-size:14px;opacity:0;pointer-events:none;transition:.2s;max-width:calc(100vw - 32px);text-align:center}
.toast.show{opacity:1;transform:translateX(-50%)}
.phone-note{display:none;margin-top:14px}
@media (max-width:860px){.nav .links a[data-nav],.nav .links a[href*="github"]{display:none}.hero{grid-template-columns:1fr;gap:12px;padding-top:36px}.band{padding-bottom:44px}.hero-shot{transform:translateY(64px)}.wrap{padding:0 18px}.content{padding:104px 18px 56px}.nav{flex-wrap:wrap;gap:8px 12px}.nav .brand{white-space:nowrap}.gstep,.gstep.has-aside{grid-template-columns:1fr}.gstep .aside{grid-column:1;margin-top:14px}.gstep::before{margin-bottom:8px}.cta.quiet{margin-left:0;margin-top:8px}}
@media (max-width:640px){.phone-note{display:flex}}
.tool-hero{align-items:start;padding-bottom:64px}
.tool-hero .kicker{display:inline-block;font:600 12px var(--mono);letter-spacing:.08em;text-transform:uppercase;color:#5ee0a8;margin-bottom:14px}
.tool-hero h1{font-size:clamp(32px,4.2vw,50px)}
.tool-hero .hero-shot{transform:none;margin-top:8px}
.facts{background:rgba(255,255,255,.05);border:1px solid rgba(255,255,255,.1);border-radius:14px;padding:22px 24px;color:var(--band-ink);display:grid;gap:14px;margin-top:8px}
.facts div{display:grid;grid-template-columns:110px minmax(0,1fr);gap:12px;font-size:15px}
.facts b{color:var(--band-muted);font:600 12px var(--mono);letter-spacing:.06em;text-transform:uppercase;padding-top:3px}
.facts .mono{word-break:break-all}
.bullets{margin:0;padding:0;list-style:none;display:grid;gap:12px;max-width:72ch}
.bullets li{display:grid;grid-template-columns:22px minmax(0,1fr);gap:10px;color:var(--ink);font-size:16px}
.bullets li svg{color:var(--good);margin-top:4px}
.section{margin-bottom:64px}
.related{display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:14px}
.related a.card{display:block;padding:18px 20px;color:var(--ink);text-decoration:none}
.related a.card:hover{border-color:var(--accent)}
.related a.card b{display:block;margin-bottom:6px}
.related a.card span{color:var(--muted);font-size:14.5px}
.crumbs{font-size:13.5px;color:var(--band-muted);margin:26px 0 0}.crumbs a{color:var(--band-muted)}.crumbs a:hover{color:var(--band-ink)}
.faq details{background:var(--surface);border:1px solid var(--line);border-radius:12px;padding:14px 18px;margin-bottom:10px}
.faq summary{cursor:pointer;font-weight:600}.faq details p{margin:10px 0 0;color:var(--muted);max-width:72ch}
@media (max-width:860px){.tool-hero{grid-template-columns:1fr}.facts div{grid-template-columns:1fr;gap:4px}}
</style>
</head>
<body>
<header class="band">
<div class="wrap">
<nav class="nav" aria-label="Main"><a class="brand" href="/" title="x-utils home"><i></i>x-utils</a><span class="links"><a href="/#how" data-nav="how">How it works</a><a href="/#tools" data-nav="tools">Tools</a><a href="/#examples" data-nav="examples">Examples</a><a href="/#safety" data-nav="safety">Safety</a><a href="/#help" data-nav="help">Help</a><a href="https://github.com/devploit/x-utils" target="_blank" rel="noopener">GitHub</a><a class="go" href="/#start">Start</a></span></nav>
<p class="crumbs"><a href="/">x-utils</a> › <a href="/#tools">Tools</a> › Read a whole X (Twitter) thread as clean text</p>
<div class="hero tool-hero">
<div>
<span class="kicker">Free tool for X (Twitter) · your content</span>
<h1>Read a whole X (Twitter) thread as clean text</h1>
<p>Open any post of a thread and get the whole thread as one clean text, a readable page and a Markdown file. No bot to mention, nothing posted on your behalf.</p>
<div class="pills"><span class="pill"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M5 12.5 9.5 17 19 7"/></svg>no X API to pay for</span><span class="pill"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M5 12.5 9.5 17 19 7"/></svg>no install</span><span class="pill"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M5 12.5 9.5 17 19 7"/></svg>password never asked</span><span class="pill"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M5 12.5 9.5 17 19 7"/></svg>only reads, changes nothing</span></div>
<button class="cta" type="button" data-copy="thread-unroll"><svg viewBox="0 0 24 24"><rect x="9" y="9" width="12" height="12" rx="2"/><path d="M5 15V5a2 2 0 0 1 2-2h10"/></svg><span>Copy the script</span></button><a class="cta quiet" href="#run">How to run it</a>
</div>
<div class="facts"><div><b>Needs</b><span class="mono">x.com/author/status/…</span></div><div><b>Takes</b><span>Under a minute.</span></div><div><b>You get</b><span>An HTML report you can sort and filter, a CSV and a JSON file, saved in your Downloads folder.</span></div></div>
</div>
</div>
</header>
<main class="content wrap">
<section class="section" id="what">
<h2>What you get</h2>
<p class="lead">A self-contained HTML report you can sort, filter and share, plus a CSV for spreadsheets and a JSON file. Everything is built in your browser from what X already shows you when you scroll.</p>
<ul class="bullets">
<li><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M5 12.5 9.5 17 19 7"/></svg><span>The author's posts in order, with replies from other people left out.</span></li>
<li><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M5 12.5 9.5 17 19 7"/></svg><span>Media and links kept, numbers for each post.</span></li>
<li><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M5 12.5 9.5 17 19 7"/></svg><span>Markdown, plain text and a readable HTML page.</span></li>
</ul>
</section>
<section class="section" id="run">
<h2>How to run it</h2>
<p class="lead">Five steps, on a computer. The <a href="/#start">guided setup on the home page</a> adapts them to your system and username.</p>
<div class="card gsteps">
<div class="gstep"><div><h3>Open any post of the thread on X</h3><p>On a computer, log in to X and open x.com/author/status/…. If the page was already open, reload it right before pasting: the tool reads the list as X loads it.</p><div class="row"><span class="urlbox">x.com/author/status/…</span></div></div></div>
<div class="gstep"><div><h3>Open the browser console</h3><p>Press Cmd+Option+I on a Mac or F12 on Windows and Linux, then click the Console tab.</p></div></div>
<div class="gstep"><div><h3>Copy the script</h3><p>Use the copy button on this page. The script is plain text you can read; it lives at x-utils.com/dist/thread-unroll.js.</p><div class="row"><button class="btn primary" type="button" data-copy="thread-unroll"><svg viewBox="0 0 24 24"><rect x="9" y="9" width="12" height="12" rx="2"/><path d="M5 15V5a2 2 0 0 1 2-2h10"/></svg><span>Copy the script</span></button><a class="btn" href="/dist/thread-unroll.js" target="_blank" rel="noopener">Open it as a file instead</a></div></div></div>
<div class="gstep"><div><h3>Paste it in the console and press Enter</h3><p>The first time, the browser asks you to type "allow pasting" first. Type it, press Enter, paste again.</p></div></div>
<div class="gstep"><div><h3>Wait, then open your report</h3><p>Under a minute. The report and a CSV land in your Downloads folder.</p></div></div>
</div>
</section>
<section class="section" id="safety">
<h2>Why this is safe</h2>
<div class="safety">
<div class="card"><b><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M5 12.5 9.5 17 19 7"/></svg>No password, ever</b><p>The script runs inside the X tab where you are already logged in. It never asks for, reads or stores your password, cookies or keys.</p></div>
<div class="card"><b><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M5 12.5 9.5 17 19 7"/></svg>Nothing is sent anywhere</b><p>There is no server behind this. The report and the spreadsheet are written straight into your Downloads folder.</p></div>
<div class="card"><b><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M5 12.5 9.5 17 19 7"/></svg>It only reads</b><p>No tool follows, unfollows, blocks, likes, posts or deletes anything. It scrolls and takes notes, like a very fast assistant looking at your screen.</p></div>
<div class="card"><b><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M5 12.5 9.5 17 19 7"/></svg>Anyone can read it</b><p>The code is open, short, in plain JavaScript and <a href="https://github.com/devploit/x-utils">published on GitHub</a>. If you know a developer, ask them to look.</p></div>
</div>
</section>
<section class="section faq" id="faq">
<h2>Questions</h2>
<details><summary>Which posts count as the thread?</summary><p>The author's own replies chained to the first post, plus contiguous posts by the same author shown on screen. Other people's replies are ignored.</p></details>
<p class="lead" style="margin-top:14px">More answers in the <a href="/#help">help section</a>.</p>
</section>
<section class="section" id="related">
<h2>Related tools</h2>
<div class="related">
<a class="card" href="/bookmarks-export"><b>Export your X (Twitter) bookmarks</b><span>Every bookmark as a readable page with the images, a Markdown file for Notion or Obsidian, and a spreadsheet.</span></a>
<a class="card" href="/engagement-report"><b>Which of your X (Twitter) posts actually work?</b><span>Your posts ranked by likes, views and engagement rate, a heatmap of when your posts perform best, and per-post charts you can hover.</span></a>
<a class="card" href="/search-export"><b>Export X (Twitter) search results</b><span>Any search, including advanced operators such as from:, since: and min_faves:, as a spreadsheet and a readable page.</span></a>
</div>
</section>
<footer><span><a href="/"><b>x-utils</b></a> · made by <a href="https://x.com/devploit" rel="me">@devploit</a> · free and open source (MIT) · not affiliated with X Corp</span><span><a href="https://github.com/devploit/x-utils">Source code</a> · <a href="https://github.com/devploit/x-utils/issues">Report a problem</a></span></footer>
</main>
<div class="toast" id="toast" role="status" aria-live="polite"></div>
<script>
(function(){
var toast=document.getElementById("toast"),timer;
function say(msg){toast.textContent=msg;toast.classList.add("show");clearTimeout(timer);timer=setTimeout(function(){toast.classList.remove("show")},4200)}
function copyText(text){if(navigator.clipboard&&navigator.clipboard.writeText)return navigator.clipboard.writeText(text);
return new Promise(function(resolve,reject){var ta=document.createElement("textarea");ta.value=text;ta.setAttribute("readonly","");ta.style.position="fixed";ta.style.opacity="0";document.body.appendChild(ta);ta.select();var ok=false;try{ok=document.execCommand("copy")}catch(e){}ta.remove();ok?resolve():reject(new Error("execCommand failed"))})}
document.querySelectorAll("[data-copy]").forEach(function(btn){btn.addEventListener("click",function(){var id=btn.dataset.copy,label=btn.querySelector("span")||btn,orig=label.textContent;
if(location.protocol==="file:"){window.open("dist/"+id+".js","_blank");say("Opened the script in a new tab. Select all, copy, then paste it into the X console.");return}
label.textContent="Copying…";
fetch("/dist/"+id+".js").then(function(r){if(!r.ok)throw new Error(r.status);return r.text()}).then(function(text){if(text.indexOf("x-utils")<0)throw new Error("unexpected content");return copyText(text)}).then(function(){label.textContent="Copied";say("Copied. Now go to the X page, click in the console and paste.");setTimeout(function(){label.textContent=orig},2600)})
.catch(function(){label.textContent=orig;say("Could not copy automatically. Use 'Open it as a file instead', select everything and copy.")})})});
})();
</script>
</body>
</html>