Skip to content

Commit bfc0df8

Browse files
committed
refactor: redesign frontend ui system
1 parent d11b5d0 commit bfc0df8

8 files changed

Lines changed: 943 additions & 609 deletions

File tree

frontend/index.html

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,11 @@
88
name="description"
99
content="Convert websites to RSS feeds instantly with html2rss-web."
1010
/>
11-
<title>Turn Any Website Into an RSS Feed Instantly</title>
11+
<link
12+
rel="icon"
13+
href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' rx='10' fill='%230b0c0d'/%3E%3Cpath d='M14 20h36v6H14zm0 12h25v6H14zm0 12h16v6H14z' fill='%23f3f4f6'/%3E%3C/svg%3E"
14+
/>
15+
<title>html2rss</title>
1216
</head>
1317
<body>
1418
<div id="app"></div>

frontend/src/components/App.module.css

Lines changed: 0 additions & 28 deletions
This file was deleted.

frontend/src/components/App.tsx

Lines changed: 63 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,28 @@ import {
88
import { useAuth } from '../hooks/useAuth';
99
import { useFeedConversion } from '../hooks/useFeedConversion';
1010
import { useStrategies } from '../hooks/useStrategies';
11-
import styles from './App.module.css';
1211

1312
type ViewMode = 'result' | 'guest-demo' | 'guest-auth' | 'member';
1413

1514
const EMPTY_AUTH_ERRORS = { username: '', token: '', form: '' };
1615
const EMPTY_FEED_ERRORS = { url: '', form: '' };
1716

17+
function BrandLockup() {
18+
return (
19+
<div class="brand-lockup" aria-label="html2rss">
20+
<span class="brand-lockup__mark" aria-hidden="true">
21+
<span />
22+
<span />
23+
<span />
24+
</span>
25+
<div class="brand-lockup__text">
26+
<strong>html2rss</strong>
27+
<span>HTML ingestion to RSS feed output</span>
28+
</div>
29+
</div>
30+
);
31+
}
32+
1833
export function App() {
1934
const {
2035
isAuthenticated,
@@ -62,8 +77,8 @@ export function App() {
6277
};
6378

6479
const strategyHint = (strategy: Strategy) => {
65-
if (strategy.id === 'ssrf_filter') return 'Direct fetch — works for most sites. Fast and safe.';
66-
if (strategy.id === 'browserless') return 'Headless browser — use for JavaScript-rendered pages (React, Angular, SPAs).';
80+
if (strategy.id === 'ssrf_filter') return 'Direct fetch. Fast path for standard documents.';
81+
if (strategy.id === 'browserless') return 'Browser render. Use for JavaScript-heavy pages and SPA output.';
6782
return strategy.name;
6883
};
6984

@@ -82,10 +97,10 @@ export function App() {
8297

8398
try {
8499
await login(authFormData.username, authFormData.token);
85-
} catch (error) {
100+
} catch (submitError) {
86101
setAuthFieldErrors({
87102
...EMPTY_AUTH_ERRORS,
88-
form: error instanceof Error ? error.message : 'Unable to authenticate. Please try again.',
103+
form: submitError instanceof Error ? submitError.message : 'Unable to authenticate.',
89104
});
90105
}
91106
};
@@ -101,8 +116,8 @@ export function App() {
101116

102117
try {
103118
await convertFeed(feedFormData.url, feedFormData.strategy, token ?? '');
104-
} catch (error) {
105-
const message = error instanceof Error ? error.message : 'Unable to start conversion.';
119+
} catch (submitError) {
120+
const message = submitError instanceof Error ? submitError.message : 'Unable to start conversion.';
106121
if (message.toLowerCase().includes('url')) {
107122
setFeedFieldErrors({ ...EMPTY_FEED_ERRORS, url: message });
108123
} else {
@@ -122,8 +137,8 @@ export function App() {
122137
try {
123138
const demoStrategy = strategies[0]?.id ?? 'ssrf_filter';
124139
await convertFeed(url, demoStrategy, 'CHANGE_ME_DEMO_TOKEN');
125-
} catch (error) {
126-
setDemoError(error instanceof Error ? error.message : 'Demo conversion failed. Please try again.');
140+
} catch (submitError) {
141+
setDemoError(submitError instanceof Error ? submitError.message : 'Demo conversion failed.');
127142
}
128143
};
129144

@@ -134,23 +149,50 @@ export function App() {
134149

135150
if (authLoading) {
136151
return (
137-
<div class="app-shell">
138-
<div class={styles.loading}>
139-
<div class={styles.loadingSpinner} aria-label="Loading application" />
140-
<p>Loading...</p>
152+
<section class="workspace-shell workspace-shell--loading">
153+
<BrandLockup />
154+
<div class="status-card" aria-live="polite">
155+
<div class="status-card__spinner" aria-hidden="true" />
156+
<div>
157+
<strong>Booting session</strong>
158+
<p>Checking stored credentials and available strategies.</p>
159+
</div>
141160
</div>
142-
</div>
161+
</section>
143162
);
144163
}
145164

146165
return (
147-
<div class="app-shell app-shell--workspace">
166+
<section class={`workspace-shell workspace-shell--${mode}`}>
167+
<header class="workspace-frame">
168+
<div class="workspace-frame__masthead">
169+
<BrandLockup />
170+
<div class="workspace-frame__context">
171+
<span>{isAuthenticated ? `operator:${username}` : 'guest:public'}</span>
172+
<span>{mode === 'result' ? 'feed-ready' : 'interactive'}</span>
173+
</div>
174+
</div>
175+
<div class="workspace-frame__titleblock">
176+
<p class="eyebrow">html to rss conversion tool</p>
177+
<h1>
178+
{mode === 'result'
179+
? 'Feed generated'
180+
: isAuthenticated
181+
? 'Convert and inspect source pages'
182+
: 'Convert public HTML into a feed endpoint'}
183+
</h1>
184+
<p class="lede">
185+
Compact operator UI. Minimal inputs, explicit states, one canonical action per outcome.
186+
</p>
187+
</div>
188+
</header>
189+
148190
{authError && mode !== 'result' && (
149191
<section class="notice notice--error" role="alert">
150-
<h3>Authentication error</h3>
192+
<div class="notice__title">Authentication error</div>
151193
<p>{authError}</p>
152-
<button type="button" onClick={() => window.location.reload()} class="btn btn--outline">
153-
Retry
194+
<button type="button" onClick={() => window.location.reload()} class="btn btn--secondary">
195+
Reload session
154196
</button>
155197
</section>
156198
)}
@@ -161,7 +203,7 @@ export function App() {
161203
onClose={clearResult}
162204
isAuthenticated={isAuthenticated}
163205
onLogout={isAuthenticated ? handleLogout : undefined}
164-
username={username}
206+
username={username ?? undefined}
165207
onRequestSignIn={!isAuthenticated ? handleSignInFromResult : undefined}
166208
/>
167209
)}
@@ -182,7 +224,7 @@ export function App() {
182224

183225
{mode === 'member' && (
184226
<MemberConvertPanel
185-
username={username}
227+
username={username ?? ''}
186228
onLogout={handleLogout}
187229
feedFormData={feedFormData}
188230
feedFieldErrors={feedFieldErrors}
@@ -196,6 +238,6 @@ export function App() {
196238
strategyHint={strategyHint}
197239
/>
198240
)}
199-
</div>
241+
</section>
200242
);
201243
}

0 commit comments

Comments
 (0)