Skip to content

Commit 867b62d

Browse files
committed
style: refine frontend typography and visual noise
1 parent bfc0df8 commit 867b62d

12 files changed

Lines changed: 126 additions & 103 deletions

File tree

Makefile

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# frozen_string_literal: true
22

3-
.PHONY: help test lint lint-js lint-ruby lintfix lintfix-js lintfix-ruby setup dev clean frontend-setup ready yard-verify-public-docs openapi openapi-verify openapi-client openapi-client-verify openapi-lint openapi-lint-redocly openapi-lint-spectral openai-lint-spectral
3+
.PHONY: help test lint lint-js lint-ruby lintfix lintfix-js lintfix-ruby setup dev clean frontend-setup quick-check ready yard-verify-public-docs openapi openapi-verify openapi-client openapi-client-verify openapi-lint openapi-lint-redocly openapi-lint-spectral openai-lint-spectral
44

55
# Default target
66
help: ## Show this help message
@@ -59,6 +59,8 @@ lint-ruby: ## Run Ruby linter (RuboCop) - errors when issues found
5959
@echo "Ruby linting complete!"
6060

6161
lint-js: ## Run JavaScript/Frontend linter (Prettier) - errors when issues found
62+
@echo "Running TypeScript typecheck..."
63+
@cd frontend && npm run typecheck
6264
@echo "Running Prettier format check..."
6365
@cd frontend && npm run format:check
6466
@echo "JavaScript linting complete!"
@@ -76,10 +78,15 @@ lintfix-js: ## Auto-fix JavaScript/Frontend linting issues
7678
@cd frontend && npm run format
7779
@echo "JavaScript lintfix complete!"
7880

79-
ready: ## Pre-commit gate (RuboCop + RSpec)
81+
quick-check: ## Fast local checks (Ruby lint/docs + frontend format/typecheck)
82+
@echo "Running quick checks..."
83+
$(MAKE) lint-ruby
84+
$(MAKE) lint-js
85+
@echo "Quick checks complete!"
86+
87+
ready: ## Pre-commit gate (quick checks + RSpec)
8088
@echo "Running pre-commit checks..."
81-
bundle exec rubocop -F
82-
bundle exec rake yard:verify_public_docs
89+
$(MAKE) quick-check
8390
bundle exec rspec
8491
@echo "Pre-commit checks complete!"
8592

frontend/.prettierignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
11
# Frontend package files
22
package-lock.json
33
yarn.lock
4+
5+
# Generated and transient frontend output
6+
.astro/
7+
test-results/
8+
src/api/generated/

frontend/index.html

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,7 @@
44
<meta charset="UTF-8" />
55
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
66
<meta name="robots" content="noindex, nofollow" />
7-
<meta
8-
name="description"
9-
content="Convert websites to RSS feeds instantly with html2rss-web."
10-
/>
7+
<meta name="description" content="Convert websites to RSS feeds instantly with html2rss-web." />
118
<link
129
rel="icon"
1310
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"
@@ -18,4 +15,4 @@
1815
<div id="app"></div>
1916
<script type="module" src="/src/main.tsx"></script>
2017
</body>
21-
</html>
18+
</html>

frontend/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
"preview": "vite preview --port 4001 --host",
1111
"format": "prettier --write .",
1212
"format:check": "prettier --check .",
13+
"typecheck": "tsc -p tsconfig.typecheck.json --noEmit",
1314
"openapi:generate": "openapi-ts -i ../docs/api/v1/openapi.yaml -o src/api/generated -c @hey-api/client-fetch",
1415
"openapi:verify": "npm run openapi:generate && git diff --exit-code -- src/api/generated",
1516
"test": "vitest",

frontend/src/components/App.tsx

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,6 @@
11
import { useEffect, useState } from 'preact/hooks';
22
import { ResultDisplay } from './ResultDisplay';
3-
import {
4-
GuestOnboardingPanel,
5-
MemberConvertPanel,
6-
type Strategy,
7-
} from './AppPanels';
3+
import { GuestOnboardingPanel, MemberConvertPanel, type Strategy } from './AppPanels';
84
import { useAuth } from '../hooks/useAuth';
95
import { useFeedConversion } from '../hooks/useFeedConversion';
106
import { useStrategies } from '../hooks/useStrategies';
@@ -78,7 +74,8 @@ export function App() {
7874

7975
const strategyHint = (strategy: Strategy) => {
8076
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.';
77+
if (strategy.id === 'browserless')
78+
return 'Browser render. Use for JavaScript-heavy pages and SPA output.';
8279
return strategy.name;
8380
};
8481

frontend/src/components/AppPanels.tsx

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,9 @@ export function GuestOnboardingPanel({
7272
onAuthFieldChange,
7373
onBackToDemo,
7474
}: GuestOnboardingPanelProps) {
75-
const [selectedDemoId, setSelectedDemoId] = useState<(typeof DEMO_SOURCES)[number]['id']>(DEMO_SOURCES[0].id);
75+
const [selectedDemoId, setSelectedDemoId] = useState<(typeof DEMO_SOURCES)[number]['id']>(
76+
DEMO_SOURCES[0].id
77+
);
7678
const selectedDemo = DEMO_SOURCES.find((source) => source.id === selectedDemoId) ?? DEMO_SOURCES[0];
7779

7880
return (
@@ -84,7 +86,8 @@ export function GuestOnboardingPanel({
8486
</div>
8587
<div class="stack stack--lg">
8688
<p class="muted-copy">
87-
The guest path is for quick verification. Operator mode unlocks arbitrary URLs and strategy selection.
89+
The guest path is for quick verification. Operator mode unlocks arbitrary URLs and strategy
90+
selection.
8891
</p>
8992
<div class="metric-strip" aria-label="guest capabilities">
9093
<div class="metric-tile">
@@ -257,11 +260,11 @@ export function MemberConvertPanel({
257260
return (
258261
<section class="state-layout state-layout--member">
259262
<form class="surface surface--main form-shell" onSubmit={onFeedSubmit}>
260-
<div class="surface__header surface__header--row">
261-
<div>
262-
<p class="eyebrow">operator workspace</p>
263-
<h2>Convert a website</h2>
264-
</div>
263+
<div class="surface__header surface__header--row">
264+
<div>
265+
<p class="eyebrow">operator workspace</p>
266+
<h2>Convert a website</h2>
267+
</div>
265268
<div class="surface__toolbar">
266269
<span class="surface__operator">
267270
operator:<span class="surface__operator-name">{username}</span>
@@ -300,14 +303,15 @@ export function MemberConvertPanel({
300303
disabled={strategiesLoading}
301304
onChange={(event) => onFeedFieldChange('strategy', (event.target as HTMLSelectElement).value)}
302305
>
303-
{strategiesLoading
304-
? <option value="">Loading…</option>
305-
: strategies.map((strategy) => (
306-
<option key={strategy.id} value={strategy.id}>
307-
{strategy.display_name}
308-
</option>
309-
))
310-
}
306+
{strategiesLoading ? (
307+
<option value="">Loading…</option>
308+
) : (
309+
strategies.map((strategy) => (
310+
<option key={strategy.id} value={strategy.id}>
311+
{strategy.display_name}
312+
</option>
313+
))
314+
)}
311315
</select>
312316
<span class="field-help">
313317
{strategiesError

frontend/src/components/ResultDisplay.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,9 @@ export function ResultDisplay({
167167
))}
168168
</ol>
169169
) : (
170-
<p class="muted-copy">The feed endpoint is live. Preview items are not available for this response.</p>
170+
<p class="muted-copy">
171+
The feed endpoint is live. Preview items are not available for this response.
172+
</p>
171173
)}
172174
</aside>
173175
)}
@@ -178,9 +180,7 @@ export function ResultDisplay({
178180
}
179181

180182
const extractFeedToken = (publicUrl: string): string | null => {
181-
const path = publicUrl.startsWith('http')
182-
? new URL(publicUrl).pathname
183-
: publicUrl;
183+
const path = publicUrl.startsWith('http') ? new URL(publicUrl).pathname : publicUrl;
184184
const segments = path.split('/').filter(Boolean);
185185
const feedIndex = segments.findIndex((segment) => segment === 'feeds');
186186
if (feedIndex < 0) return null;

frontend/src/env.d.ts

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1 @@
11
/// <reference types="vite/client" />
2-
3-
declare module '*.module.css' {
4-
const classes: Record<string, string>;
5-
export default classes;
6-
}

frontend/src/hooks/useFeedConversion.ts

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { useState } from 'preact/hooks';
22
import { createFeed } from '../api/generated';
3-
import { apiClient, bearerHeaders } from '../api/client';
3+
import { apiClient } from '../api/client';
44
import type { FeedRecord } from '../api/contracts';
55

66
interface ConversionState {
@@ -32,22 +32,20 @@ export function useFeedConversion() {
3232
const response = await createFeed({
3333
client: apiClient,
3434
headers: {
35-
'Content-Type': 'application/json',
36-
...bearerHeaders(token),
35+
Authorization: `Bearer ${token}`,
3736
},
3837
body: {
3938
url: url.trim(),
4039
strategy: strategy.trim(),
4140
},
42-
responseStyle: 'data',
4341
throwOnError: true,
4442
});
4543

46-
if (!response?.success || !response.data?.feed) {
44+
if (!response.data?.success || !response.data.data?.feed) {
4745
throw new Error('Invalid response format');
4846
}
4947

50-
const result = response.data.feed;
48+
const result = response.data.data.feed;
5149
setState((prev) => ({ ...prev, isConverting: false, result, error: null }));
5250
} catch (error) {
5351
setState((prev) => ({
@@ -89,7 +87,8 @@ const toErrorMessage = (error: unknown): string => {
8987
const extractMessage = (error: unknown): string | null => {
9088
if (!error || typeof error !== 'object') return null;
9189

92-
const candidate = (error as { error?: { message?: unknown }; message?: unknown }).error?.message ??
90+
const candidate =
91+
(error as { error?: { message?: unknown }; message?: unknown }).error?.message ??
9392
(error as { message?: unknown }).message;
9493

9594
return typeof candidate === 'string' && candidate.trim() ? candidate : null;

frontend/src/hooks/useStrategies.ts

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -29,20 +29,18 @@ export function useStrategies(token: string | null) {
2929
client: apiClient,
3030
headers: {
3131
...bearerHeaders(token),
32-
'Content-Type': 'application/json',
3332
},
34-
responseStyle: 'data',
3533
});
3634

37-
if (response?.success && response.data?.strategies) {
38-
setState({
39-
strategies: response.data.strategies,
40-
isLoading: false,
41-
error: null,
42-
});
43-
} else {
35+
if (response.error || !response.data?.success || !response.data.data?.strategies) {
4436
throw new Error('Invalid response format from strategies API');
4537
}
38+
39+
setState({
40+
strategies: response.data.data.strategies,
41+
isLoading: false,
42+
error: null,
43+
});
4644
} catch (error) {
4745
setState({
4846
strategies: [],

0 commit comments

Comments
 (0)