Skip to content

Commit e2b7ff8

Browse files
committed
[scramjet/demo] overall ui refresh
1 parent 90a90bf commit e2b7ff8

File tree

4 files changed

+265
-45
lines changed

4 files changed

+265
-45
lines changed

packages/scramjet/packages/demo/src/App.tsx

Lines changed: 253 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,131 @@ export const App: Component<
4141
this.frame = controller.createFrame(this.frameel);
4242
this.playgroundFrame = controller.createFrame();
4343

44-
let body = btoa(
45-
`<body style="background: #000; color: #fff">Welcome to <i>Scramjet</i>! Type in a URL in the omnibox above and press enter to get started.</body>`
46-
);
44+
const versionInfo = (globalThis as any).$scramjet?.versionInfo ?? {};
45+
const scramjetVersion = String(versionInfo.version ?? "unknown");
46+
const scramjetBuild = String(versionInfo.build ?? "unknown");
47+
const scramjetDate = String(versionInfo.date ?? "unknown");
48+
const parsedScramjetDate = new Date(scramjetDate);
49+
const scramjetDatePretty = Number.isNaN(parsedScramjetDate.getTime())
50+
? scramjetDate
51+
: parsedScramjetDate.toLocaleString(undefined, {
52+
dateStyle: "short",
53+
timeStyle: "short",
54+
});
55+
56+
let body = btoa(`<!doctype html>
57+
<html lang="en">
58+
<head>
59+
<meta charset="utf-8" />
60+
<meta name="viewport" content="width=device-width, initial-scale=1" />
61+
<title>Scramjet Demo</title>
62+
<style>
63+
:root { color-scheme: dark; }
64+
* { box-sizing: border-box; }
65+
body {
66+
margin: 0;
67+
min-height: 100vh;
68+
font-family: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
69+
background: #0f0f0f;
70+
color: #e5e7eb;
71+
display: grid;
72+
place-items: start center;
73+
padding: 30px 18px;
74+
}
75+
.sheet {
76+
width: min(760px, 100%);
77+
/*border: 1px solid #222;
78+
background: #111;
79+
*/
80+
padding: 26px 28px;
81+
}
82+
h1 {
83+
margin: 0 0 8px;
84+
font-size: 1.55rem;
85+
font-weight: 650;
86+
letter-spacing: 0.005em;
87+
}
88+
.meta {
89+
color: #9ca3af;
90+
font-size: 0.78rem;
91+
margin: 0 0 20px;
92+
}
93+
.section {
94+
margin: 0 0 18px;
95+
}
96+
.label {
97+
display: block;
98+
margin-bottom: 6px;
99+
color: #e5e7eb;
100+
font-size: 1.1rem;
101+
font-weight: 630;
102+
letter-spacing: 0.01em;
103+
text-transform: none;
104+
}
105+
p {
106+
margin: 0;
107+
color: #c7c7c7;
108+
line-height: 1.62;
109+
font-size: 0.94rem;
110+
max-width: 70ch;
111+
}
112+
code {
113+
font-family: "SF Mono", Menlo, Consolas, monospace;
114+
color: #e5e7eb;
115+
background: #181818;
116+
border: 1px solid #2a2a2a;
117+
padding: 1px 5px;
118+
}
119+
a {
120+
color: inherit;
121+
text-decoration: none;
122+
}
123+
a:hover {
124+
text-decoration: underline;
125+
}
126+
</style>
127+
</head>
128+
<body>
129+
<main class="sheet">
130+
<h1>Welcome to Scramjet!</h1>
131+
<p class="meta">Version ${scramjetVersion} | <a href="https://github.com/MercuryWorkshop/scramjet/commit/${scramjetBuild}">Build ${scramjetBuild}</a> | Updated ${scramjetDatePretty}</p>
132+
133+
<section class="section">
134+
<p>
135+
<a href="https://github.com/MercuryWorkshop/scramjet"><strong>Scramjet</strong></a> is an experimental <strong>interception-based web proxy</strong>, designed to evade internet censorship and bypass arbitrary browser restrictions.<br><br>
136+
Scramjet allows you to sandbox arbitrary web content, bypass CORS restrictions on loading websites, and instrument and debug websites inside the browser itself. This is accomplished through a combination of interception, rewriting, and sandboxing techniques. You can learn more about the technical details <a href="https://developer.puter.com/blog/how-I-ported-the-web-to-the-web/"><strong>here</strong></a>.<br><br>
137+
If you're interested in contributing to scramjet, or want to build your own web proxy or application using it, you should check out the <a href="https://github.com/MercuryWorkshop/scramjet"><strong>GitHub repository</strong></a>.<br><br>
138+
This website is a demo of scramjet's capabilities, letting you browse the web proxied through scramjet, inspect how scramjet rewrites requests and responses, and see how scramjet handles your custom content in the playground.
139+
</p>
140+
</section>
141+
142+
<section class="section">
143+
<span class="label">Browser</span>
144+
<p>
145+
Try entering a URL in the box above to browse the web proxied through scramjet.<br>
146+
All traffic sent through scramjet is <strong>fully end-to-end encrypted</strong> with TLS, powered by <a href="https://github.com/ading2210/libcurl.js"><strong>libcurl.js</strong></a> and the <a href="https://github.com/MercuryWorkshop/wisp-protocol"><strong>wisp protocol</strong></a>.
147+
</p>
148+
</section>
149+
150+
<section class="section">
151+
<span class="label">Requests</span>
152+
<p>
153+
You can inspect all the requests sent through scramjet, and what transformations are applied in the requests tab.<br>
154+
Note that requests will not be captured until you first click on the requests tab.
155+
</p>
156+
</section>
157+
158+
<section class="section">
159+
<span class="label">Playground</span>
160+
<p>
161+
The playground lets you write your own html/css/js snippets which will be run inside the scramjet sandbox.
162+
Your files are "served" by scramjet from a fake origin, which can be configured to be any URL. Notice that if you add a <code>console.log(location.href)</code> in your code, the page will really be running on the fake origin.
163+
164+
</p>
165+
</section>
166+
</main>
167+
</body>
168+
</html>`);
47169
this.frame.go(`data:text/html;base64,${body}`);
48170
};
49171

@@ -91,13 +213,33 @@ export const App: Component<
91213
this.frame?.go(urlStore.url);
92214
}}
93215
>
94-
<input
95-
id="search"
96-
class="url-input"
97-
type="text"
98-
value={use(urlStore.url)}
99-
placeholder="Enter URL"
100-
/>
216+
<div class="browser-omnibox-shell">
217+
<div class="omnibox-nav" aria-hidden="true">
218+
<button type="button" class="nav-btn">
219+
<span class="material-symbols-outlined">arrow_back</span>
220+
</button>
221+
<button type="button" class="nav-btn">
222+
<span class="material-symbols-outlined">arrow_forward</span>
223+
</button>
224+
<button
225+
type="button"
226+
class="nav-btn"
227+
on:click={() => {
228+
this.frame?.go(urlStore.url);
229+
}}
230+
>
231+
<span class="material-symbols-outlined">refresh</span>
232+
</button>
233+
</div>
234+
<input
235+
id="search"
236+
class="url-input"
237+
type="text"
238+
value={use(urlStore.url)}
239+
spellcheck="false"
240+
placeholder="Enter URL or search..."
241+
/>
242+
</div>
101243
</form>
102244
<div class="top-actions">
103245
<FlagEditor
@@ -159,6 +301,8 @@ export const App: Component<
159301
};
160302

161303
App.style = css`
304+
@import url("https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20,400,0,0");
305+
162306
:scope {
163307
width: 100vw;
164308
height: 100vh;
@@ -170,38 +314,69 @@ App.style = css`
170314
top: 0;
171315
left: 0;
172316
173-
padding: 0.5em;
317+
padding: 0;
174318
background: black;
175319
box-sizing: border-box;
176320
}
321+
.material-symbols-outlined {
322+
font-family: "Material Symbols Outlined";
323+
font-weight: normal;
324+
font-style: normal;
325+
font-size: 11px;
326+
line-height: 1;
327+
letter-spacing: normal;
328+
text-transform: none;
329+
display: inline-block;
330+
white-space: nowrap;
331+
word-wrap: normal;
332+
direction: ltr;
333+
-webkit-font-smoothing: antialiased;
334+
}
177335
.top-bar {
178336
display: flex;
179-
align-items: center;
180-
gap: 0.6em;
181-
margin-bottom: 0.5em;
337+
align-items: stretch;
338+
gap: 0;
339+
margin-bottom: 0;
340+
border-bottom: 1px solid #4a4a4a;
341+
background: #0f0f0f;
182342
}
183343
.tab-bar {
184344
display: flex;
185-
align-items: center;
186-
gap: 0.5em;
345+
align-items: stretch;
346+
gap: 0;
187347
}
188348
.tab-button {
189-
border: 1px solid #333;
190-
background: #151515;
191-
color: #ddd;
192-
padding: 0.4em 0.9em;
193-
border-radius: 8px;
349+
border: 1px solid transparent;
350+
border-bottom: 0;
351+
background: transparent;
352+
color: #a8a8a8;
353+
padding: 0.24em 0.62em;
354+
border-radius: 0;
194355
cursor: pointer;
195-
font-size: 0.9em;
356+
font-size: 0.84em;
357+
line-height: 1.2;
358+
min-height: 28px;
359+
margin: 0;
360+
white-space: nowrap;
361+
display: inline-flex;
362+
align-items: center;
363+
}
364+
.tab-button:hover {
365+
background: #181818;
366+
color: #d0d0d0;
196367
}
197368
.tab-button.active {
198-
background: #2b2b2b;
369+
background: #1f1f1f;
199370
color: #fff;
200-
border-color: #555;
371+
border-color: #4a4a4a;
372+
margin-bottom: -1px;
201373
}
202374
.top-actions {
203375
display: flex;
204376
align-items: center;
377+
margin-left: auto;
378+
padding: 0 0.35em;
379+
min-height: 28px;
205380
}
206381
.browser-view {
207382
display: flex;
@@ -235,33 +410,71 @@ App.style = css`
235410
.url-form {
236411
flex: 1;
237412
display: none;
413+
min-width: 0;
238414
}
239415
.url-form.active {
240416
display: flex;
417+
align-items: center;
418+
padding: 0 0.45em 0 0.2em;
419+
}
420+
.browser-omnibox-shell {
421+
display: flex;
422+
align-items: center;
423+
gap: 0.35em;
424+
min-width: 0;
425+
border: 0;
426+
background: transparent;
427+
padding: 0;
428+
flex: 1;
429+
}
430+
.omnibox-nav {
431+
display: flex;
432+
align-items: center;
433+
gap: 0.15em;
434+
padding-right: 0.25em;
435+
border-right: 1px solid #2a2a2a;
436+
}
437+
.nav-btn {
438+
border: 0;
439+
background: transparent;
440+
color: #8f8f8f;
441+
width: 1.5em;
442+
height: 1.5em;
443+
padding: 0;
444+
border-radius: 3px;
445+
cursor: pointer;
446+
display: inline-flex;
447+
align-items: center;
448+
justify-content: center;
449+
}
450+
.nav-btn:hover {
451+
background: #1f1f1f;
452+
color: #d0d0d0;
453+
}
454+
.browser-omnibox-shell .material-symbols-outlined {
455+
font-size: 15px !important;
456+
line-height: 1 !important;
457+
font-variation-settings:
458+
"OPSZ" 20,
459+
"wght" 300,
460+
"FILL" 0,
461+
"GRAD" 0;
241462
}
242463
.url-input {
243464
box-sizing: border-box;
244465
width: 100%;
245-
max-width: 680px;
246-
margin-left: auto;
247-
margin-right: auto;
248-
padding: 0.4em 0.7em;
249-
font-size: 0.88em;
250-
border-radius: 9px;
251-
border: 1px solid #2a2a2a;
252-
background: #111;
253-
color: #f3f4f6;
466+
padding: 0.22em 0.18em;
467+
font-size: 0.9em;
468+
border: 1px solid transparent;
469+
border-radius: 3px;
470+
background: transparent;
471+
color: #e5e7eb;
254472
outline: none;
255-
transition:
256-
border-color 0.2s ease,
257-
box-shadow 0.2s ease;
258-
box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.6);
259473
}
260474
.url-input::placeholder {
261-
color: #6b7280;
475+
color: #6f7680;
262476
}
263477
.url-input:focus {
264-
border-color: #60a5fa;
265-
box-shadow: 0 0 0 2px rgba(96, 165, 250, 0.2);
478+
/* border-color: #7a7a7a; */
266479
}
267480
`;

packages/scramjet/packages/demo/src/FlagEditor.tsx

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,9 @@ export const FlagEditor: Component<
8888
<input
8989
type="checkbox"
9090
checked={use(flagStore[flag])}
91-
on:change={() => toggleFlag(flag, event.target.checked)}
91+
on:change={(e: Event) =>
92+
toggleFlag(flag, (e.target as HTMLInputElement).checked)
93+
}
9294
/>
9395
<div class="flag-info">
9496
<span class="flag-name">{flag}</span>
@@ -125,6 +127,8 @@ FlagEditor.style = css`
125127
126128
:scope.inline {
127129
position: relative;
130+
display: flex;
131+
align-items: center;
128132
background: transparent;
129133
border: none;
130134
box-shadow: none;
@@ -149,9 +153,12 @@ FlagEditor.style = css`
149153
padding: 0.35em 0.7em;
150154
background: #1a1a1a;
151155
border: 1px solid #2a2a2a;
152-
border-radius: 8px;
156+
border-radius: 0;
153157
font-size: 0.8em;
154-
line-height: 1;
158+
line-height: 1.2;
159+
min-height: 28px;
160+
display: inline-flex;
161+
align-items: center;
155162
}
156163
157164
:scope.inline .toggle-button:hover {

0 commit comments

Comments
 (0)