Skip to content

Commit 1bc00b2

Browse files
committed
Add manual-extraction terminal; copy button + clean link in decrypted block
1 parent 403f240 commit 1bc00b2

3 files changed

Lines changed: 110 additions & 11 deletions

File tree

web/app.js

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,26 @@ function refreshBuildEnabled() {
4343
$("build").disabled = !(decrypted && store.worker);
4444
}
4545

46+
function renderCommand() {
47+
const ua = store.ua || "Happ/1.0";
48+
const hwid = store.hwid || "<your-hwid>";
49+
const sub = decrypted && /^https?:\/\//i.test(decrypted.value.trim())
50+
? decrypted.value.trim()
51+
: "<decrypt a happ:// link first>";
52+
53+
const el = $("cmd");
54+
el.innerHTML = "";
55+
const span = (cls, txt) => { const s = document.createElement("span"); s.className = cls; s.textContent = txt; return s; };
56+
const line = (...nodes) => { const d = document.createElement("div"); d.className = "cline"; nodes.forEach((n) => d.append(n)); el.append(d); };
57+
58+
line(span("c-prompt", "$ "), span("c-cmd", "curl -fsSL"), span("c-cont", " \\"));
59+
line(span("c-flag", " -H "), span("c-str", `"User-Agent: ${ua}"`), span("c-cont", " \\"));
60+
line(span("c-flag", " -H "), span("c-str", `"X-HWID: ${hwid}"`), span("c-cont", " \\"));
61+
line(span("c-url", ` "${sub}"`), span("c-pipe", " | "), span("c-cmd", "base64 -d"));
62+
63+
el.dataset.copy = `curl -fsSL -H "User-Agent: ${ua}" -H "X-HWID: ${hwid}" "${sub}" | base64 -d`;
64+
}
65+
4666
function schemeOf(uri) {
4767
const i = uri.indexOf("://");
4868
return i > 0 ? uri.slice(0, i) : uri.split(":")[0];
@@ -61,7 +81,8 @@ async function onDecrypt() {
6181
decrypted = decryptLink(link, await keys());
6282
stage("st-decrypt", "done");
6383
$("decoded").hidden = false;
64-
$("decoded").textContent = `mode: ${decrypted.mode}\n${decrypted.value}`;
84+
$("decoded-value").textContent = decrypted.value;
85+
renderCommand();
6586
setStatus(
6687
store.worker ? "Decrypted. Ready to forge." : "Decrypted. Add a Worker URL in settings to forge a subscription.",
6788
true
@@ -136,14 +157,17 @@ function initSettings() {
136157
$("ua").value = store.ua;
137158
$("hwid").value = store.hwid;
138159
$("worker").value = store.worker;
139-
$("ua").oninput = (e) => { store.ua = e.target.value; };
140-
$("hwid").oninput = (e) => { store.hwid = e.target.value; };
160+
$("ua").oninput = (e) => { store.ua = e.target.value; renderCommand(); };
161+
$("hwid").oninput = (e) => { store.hwid = e.target.value; renderCommand(); };
141162
$("worker").oninput = (e) => { store.worker = e.target.value; refreshBuildEnabled(); };
142163
$("settings-toggle").onclick = () => { $("settings").hidden = !$("settings").hidden; };
143164
}
144165

145166
$("decrypt").onclick = onDecrypt;
146167
$("build").onclick = onBuild;
147168
$("copy-sub").onclick = () => navigator.clipboard.writeText($("sub-url").textContent);
169+
$("copy-decoded").onclick = () => navigator.clipboard.writeText($("decoded-value").textContent);
170+
$("copy-cmd").onclick = () => navigator.clipboard.writeText($("cmd").dataset.copy || "");
148171
initSettings();
149172
refreshBuildEnabled();
173+
renderCommand();

web/index.html

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,13 @@ <h1>happwn</h1>
7373
</ol>
7474

7575
<p id="status" class="status"></p>
76-
<pre id="decoded" class="decoded" hidden></pre>
76+
<div id="decoded" class="decoded" hidden>
77+
<div class="decoded-head">
78+
<span class="decoded-label"><span class="ok-dot"></span> DECRYPTED</span>
79+
<button id="copy-decoded" class="btn small">Copy</button>
80+
</div>
81+
<code id="decoded-value" class="decoded-value"></code>
82+
</div>
7783
</section>
7884

7985
<section id="result" class="panel output reveal" hidden>
@@ -96,7 +102,17 @@ <h1>happwn</h1>
96102
<ul id="configs" class="configs"></ul>
97103
</section>
98104

99-
<footer class="reveal" style="--d:.12s">
105+
<section class="panel terminal reveal" style="--d:.1s">
106+
<div class="term-bar">
107+
<span class="dots"><i class="r"></i><i class="a"></i><i class="g"></i></span>
108+
<span class="term-title">manual extraction — pull configs yourself</span>
109+
<button id="copy-cmd" class="btn small">Copy</button>
110+
</div>
111+
<pre id="cmd" class="term-body"></pre>
112+
<p class="term-note">No Worker needed — run this in your terminal. It sends the Happ <b>User-Agent</b> + <b>X-HWID</b> the server requires; <code>base64 -d</code> decodes the response into raw configs.</p>
113+
</section>
114+
115+
<footer class="reveal" style="--d:.16s">
100116
<span class="dim">on-device decrypt · open source</span>
101117
<a href="https://github.com/useruserdev/happwn">github.com/useruserdev/happwn</a>
102118
</footer>

web/style.css

Lines changed: 65 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -301,18 +301,27 @@ textarea:focus, input:focus {
301301
color: var(--red);
302302
}
303303
.decoded {
304-
margin: 12px 0 0;
305-
font-family: var(--mono);
306-
font-size: 12.5px;
307-
line-height: 1.7;
308-
color: var(--ink);
304+
margin: 14px 0 0;
309305
background: var(--bg-2);
310306
border: 1px solid var(--line-2);
311307
border-left: 2px solid var(--cyan);
312308
border-radius: 10px;
313309
padding: 12px 14px;
314-
white-space: pre-wrap;
310+
}
311+
.decoded-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-bottom: 9px; }
312+
.decoded-label {
313+
display: inline-flex; align-items: center; gap: 8px;
314+
font-family: var(--mono); font-size: 10.5px; letter-spacing: 2px; color: var(--cyan);
315+
}
316+
.ok-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--cyan); box-shadow: 0 0 8px var(--cyan); }
317+
.decoded-value {
318+
display: block;
319+
font-family: var(--mono);
320+
font-size: 14px;
321+
line-height: 1.55;
322+
color: var(--ink);
315323
word-break: break-all;
324+
user-select: all;
316325
}
317326

318327
/* ---------- output ---------- */
@@ -384,6 +393,56 @@ textarea:focus, input:focus {
384393
.settings-grid label.wide { grid-column: 1 / -1; }
385394
.settings .hint { margin: 12px 0 0; font-size: 12.5px; color: var(--dim); line-height: 1.6; }
386395

396+
/* ---------- terminal (manual extraction) ---------- */
397+
.terminal { padding: 0; overflow: hidden; }
398+
.term-bar {
399+
display: flex; align-items: center; gap: 12px;
400+
padding: 11px 14px;
401+
background: #04060d;
402+
border-bottom: 1px solid var(--line-2);
403+
}
404+
.dots { display: inline-flex; gap: 7px; }
405+
.dots i { width: 11px; height: 11px; border-radius: 50%; display: block; opacity: .9; }
406+
.dots .r { background: var(--red); }
407+
.dots .a { background: var(--amber); }
408+
.dots .g { background: var(--cyan); }
409+
.term-title {
410+
flex: 1;
411+
font-family: var(--mono);
412+
font-size: 11px;
413+
letter-spacing: 1px;
414+
color: var(--muted);
415+
}
416+
.term-body {
417+
margin: 0;
418+
padding: 18px 16px;
419+
background: #04060d;
420+
font-family: var(--mono);
421+
font-size: 12.5px;
422+
line-height: 1.85;
423+
white-space: pre-wrap;
424+
word-break: break-all;
425+
overflow-x: auto;
426+
}
427+
.term-body .cline { min-height: 1.2em; }
428+
.term-body .c-prompt { color: var(--cyan); }
429+
.term-body .c-cmd { color: var(--ink); font-weight: 700; }
430+
.term-body .c-flag { color: var(--dim); }
431+
.term-body .c-str { color: var(--amber); }
432+
.term-body .c-url { color: var(--cyan); }
433+
.term-body .c-pipe { color: var(--dim); }
434+
.term-body .c-cont { color: var(--dim); }
435+
.term-note {
436+
margin: 0;
437+
padding: 12px 16px 16px;
438+
font-size: 12px;
439+
color: var(--dim);
440+
line-height: 1.6;
441+
border-top: 1px solid var(--line);
442+
}
443+
.term-note b { color: var(--cyan); font-weight: 600; }
444+
.term-note code { font-family: var(--mono); color: var(--amber); }
445+
387446
/* ---------- footer ---------- */
388447
footer {
389448
margin-top: 8px;

0 commit comments

Comments
 (0)