@@ -8,13 +8,28 @@ import {
88import { useAuth } from '../hooks/useAuth' ;
99import { useFeedConversion } from '../hooks/useFeedConversion' ;
1010import { useStrategies } from '../hooks/useStrategies' ;
11- import styles from './App.module.css' ;
1211
1312type ViewMode = 'result' | 'guest-demo' | 'guest-auth' | 'member' ;
1413
1514const EMPTY_AUTH_ERRORS = { username : '' , token : '' , form : '' } ;
1615const 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+
1833export 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