Skip to content

Commit 0c2d8ea

Browse files
committed
Switch to React Router 7 (Beta)
1 parent 30d5f26 commit 0c2d8ea

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

57 files changed

+3122
-1375
lines changed

.editorconfig

+9-7
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
1-
# EditorConfig is awesome: https://EditorConfig.org
2-
3-
# top-most EditorConfig file
41
root = true
52

63
[*]
7-
indent_style = tab
8-
indent_size = 2
94
end_of_line = lf
10-
charset = utf-8
115
trim_trailing_whitespace = true
12-
insert_final_newline = true
6+
insert_final_newline = true
7+
charset = utf-8
8+
indent_style = tab
9+
indent_size = 2
10+
11+
# YAML doesn't support hard tabs 🙃
12+
# Templates that will be weird with hard tabs in the website editor
13+
[{**.yml,**.yaml,**.md,**.rs,**.mdx,justfile,**.json}]
14+
indent_style = space

.gitignore

+9-9
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
node_modules
2-
dist
1+
.DS_Store
2+
/node_modules/
33

4-
.pnp.*
5-
.yarn/*
6-
!.yarn/patches
7-
!.yarn/plugins
8-
!.yarn/releases
9-
!.yarn/sdks
10-
!.yarn/versions
4+
# React Router
5+
/.react-router/
6+
/build/
7+
8+
# Cloudflare
9+
.mf
10+
.wrangler

.gitpod.yml

-7
This file was deleted.

.node-version

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
22

.npmrc

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
save-prefix=

.prettierignore

+3-4
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
.yarn
2-
dist
3-
node_modules
4-
pnpm-lock.yaml
1+
/pnpm-lock.yaml
2+
/build/**/*
3+
/auto-imports.d.ts

.prettierrc

+1-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
{
22
"singleQuote": true,
3-
"semi": false,
4-
"tabWidth": 2,
5-
"useTabs": true
3+
"semi": false
64
}

.vscode/settings.json

-11
This file was deleted.

README.md

+73-8
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,80 @@
1-
# subtitle-app
1+
# patdx-remix-template
22

3-
## Access here: https://subtitle-app.vercel.app/
3+
## Create a new project from this template
44

5-
This is an open-source application to play a subtitle file as if it were a video.
5+
This will copy the code in `main` branch to a new directory.
66

7-
It supports SRT files or ZIP archives with SRT files inside.
7+
```
8+
npx degit patdx/patdx-remix-template
9+
```
810

9-
It can be useful to enjoy content in a second subtitle language.
11+
## Summary
1012

11-
There are similar tools for computers such as [Language Reactor](https://www.languagereactor.com/) extension but I could not find any good tool that can be used with your TV.
13+
This is a template for React Router 7 (Remix) apps with the following features already set up:
1214

13-
With this app, you can play the main content via the regular streaming app on your TV, then open up Subtitle App on any old phone/table, queue up the subtitles file and place it next to the TV.
15+
- Cloudflare Pages Functions deployment using wrangler.toml
16+
- React 19
17+
- Auto import plugin with React imports set up
18+
- Tailwind CSS
1419

15-
I have used it to watch some K-dramas with my partner as both of us have different preferred languages for watching TV, and neither of us know Korean.
20+
It is largely based on the [React Router 7 Cloudflare D1 Template](https://github.com/remix-run/react-router-templates/tree/main/cloudflare-d1) but using Cloudflare Pages instead of the new Cloudflare Worker Assets feature, and a different plugin to use Worker Proxy mode instead of the Vite Environments API.
21+
22+
## Adding Node-targeted modules
23+
24+
If you need to use a module that is targeted for Node, try adding `nodejs_compat` to the wrangler.json:
25+
26+
```json
27+
{
28+
"compatibility_flags": ["nodejs_compat"]
29+
}
30+
```
31+
32+
And add the module to the SSR externals list in vite.config.ts:
33+
34+
```diff
35+
ssr: {
36+
external: [
37+
'node:async_hooks',
38+
+ 'nodemailer',
39+
],
40+
},
41+
```
42+
43+
## Development
44+
45+
Run the dev server:
46+
47+
```sh
48+
pnpm dev
49+
```
50+
51+
To run Wrangler:
52+
53+
```sh
54+
pnpm build
55+
pnpm start
56+
```
57+
58+
## Deployment
59+
60+
> [!WARNING]
61+
> Cloudflare does _not_ use `wrangler.toml` to configure deployment bindings.
62+
> You **MUST** [configure deployment bindings manually in the Cloudflare dashboard][bindings].
63+
64+
First, build your app for production:
65+
66+
```sh
67+
pnpm build
68+
```
69+
70+
Then, deploy your app to Cloudflare Pages:
71+
72+
```sh
73+
pnpm run deploy
74+
```
75+
76+
[bindings]: https://developers.cloudflare.com/pages/functions/bindings/
77+
78+
## Styling
79+
80+
This template comes with [Tailwind CSS](https://tailwindcss.com/) already configured for a simple default starting experience. You can use whatever css framework you prefer. See the [Vite docs on css](https://vitejs.dev/guide/features.html#css) for more information.

TODO.md

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
- restore favicon, etc
2+
- syncing between devices
3+
- use evolu or one(?) that crsqlite related thing
4+
- switch to remix?
5+
- disable unnecessary text highlighting
6+
- eg when clicking the next button
7+
- and double click triggering highlight instead of another click?
8+
- refer to ionic css: https://github.com/ionic-team/ionic-framework/blob/1b11b82eaaa0982f98547f2854563517174c865c/core/src/css/structure.scss#L75
9+
- better ui for navigating time
10+
- like +10s -10s
11+
- or a dial
12+
- or tapping text in the transcript view
13+
- fix background for black player page
14+
- track time for each episode separately
15+
- remember last time for each episode
16+
- next episode button
17+
- transcript view
18+
- markup support
19+
- have it fade out after a few seconds
20+
- only show full screen button on platforms that support it (not iOS)
21+
- add padding
22+
- add more options to change the font size
23+
- auto shrink large text if it does not fit
24+
- Fix untappable buttons in PwA mode
25+
- fix theme color/background color

app/.server/context.ts

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { AsyncLocalStorage } from 'node:async_hooks'
2+
3+
export interface MyCloudflareEnvironment {
4+
VALUE_FROM_CLOUDFLARE: string
5+
}
6+
7+
export interface MyAppLoadContext {
8+
request: Request
9+
env: MyCloudflareEnvironment
10+
executionCtx: ExecutionContext
11+
[key: string]: unknown
12+
}
13+
14+
declare module 'react-router' {
15+
interface AppLoadContext extends MyAppLoadContext {}
16+
}
17+
18+
export const MyAppLoadContext = new AsyncLocalStorage<MyAppLoadContext>()
19+
20+
export function getMyAppLoadContext() {
21+
const ctx = MyAppLoadContext.getStore()
22+
if (!ctx) throw new Error('MyAppLoadContext not found')
23+
return ctx
24+
}

app/app.css

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
@tailwind base;
2+
@tailwind components;
3+
@tailwind utilities;
4+
5+
body {
6+
user-select: none;
7+
8+
/* Should we use any of the properties set here? https://github.com/ionic-team/ionic-framework/blob/1b11b82eaaa0982f98547f2854563517174c865c/core/src/css/structure.scss#L75 */
9+
/* transform: translateZ(0);
10+
11+
text-rendering: optimizeLegibility;
12+
13+
overflow: hidden;
14+
15+
touch-action: manipulation;
16+
17+
-webkit-user-drag: none;
18+
19+
-ms-content-zooming: none;
20+
21+
word-wrap: break-word;
22+
23+
overscroll-behavior-y: none;
24+
-webkit-text-size-adjust: none;
25+
26+
text-size-adjust: none; */
27+
}
File renamed without changes.
File renamed without changes.

app/root.tsx

+87
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
2+
import * as konsta from 'konsta/react'
3+
import {
4+
isRouteErrorResponse,
5+
Links,
6+
Meta,
7+
Outlet,
8+
Scripts,
9+
ScrollRestoration,
10+
} from 'react-router'
11+
import type { Route } from './+types/root'
12+
import stylesheet from './app.css?url'
13+
14+
export const links: Route.LinksFunction = () => [
15+
{ rel: 'preconnect', href: 'https://fonts.googleapis.com' },
16+
{
17+
rel: 'preconnect',
18+
href: 'https://fonts.gstatic.com',
19+
crossOrigin: 'anonymous',
20+
},
21+
{
22+
rel: 'stylesheet',
23+
href: 'https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap',
24+
},
25+
{ rel: 'stylesheet', href: stylesheet },
26+
]
27+
28+
// Create a client
29+
const queryClient = new QueryClient()
30+
31+
export function Layout({ children }: { children: React.ReactNode }) {
32+
return (
33+
<html lang="en">
34+
<head>
35+
<meta charSet="utf-8" />
36+
<meta
37+
name="viewport"
38+
content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no, viewport-fit=cover"
39+
/>
40+
<Meta />
41+
<Links />
42+
</head>
43+
<body>
44+
<QueryClientProvider client={queryClient}>
45+
<konsta.App id="app" safeAreas theme="ios">
46+
{children}
47+
</konsta.App>
48+
</QueryClientProvider>
49+
<ScrollRestoration />
50+
<Scripts />
51+
</body>
52+
</html>
53+
)
54+
}
55+
56+
export default function App() {
57+
return <Outlet />
58+
}
59+
60+
export function ErrorBoundary({ error }: Route.ErrorBoundaryProps) {
61+
let message = 'Oops!'
62+
let details = 'An unexpected error occurred.'
63+
let stack: string | undefined
64+
65+
if (isRouteErrorResponse(error)) {
66+
message = error.status === 404 ? '404' : 'Error'
67+
details =
68+
error.status === 404
69+
? 'The requested page could not be found.'
70+
: error.statusText || details
71+
} else if (import.meta.env.DEV && error && error instanceof Error) {
72+
details = error.message
73+
stack = error.stack
74+
}
75+
76+
return (
77+
<main className="pt-16 p-4 container mx-auto">
78+
<h1>{message}</h1>
79+
<p>{details}</p>
80+
{stack && (
81+
<pre className="w-full p-4 overflow-x-auto">
82+
<code>{stack}</code>
83+
</pre>
84+
)}
85+
</main>
86+
)
87+
}

app/routes.ts

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import type { RouteConfig } from '@react-router/dev/routes'
2+
import { flatRoutes } from '@react-router/fs-routes'
3+
4+
export default flatRoutes() satisfies RouteConfig

0 commit comments

Comments
 (0)