Change go to page + add Vercel Analytics integration
This commit is contained in:
parent
08480f07ed
commit
b56584cbed
6 changed files with 87 additions and 26 deletions
|
|
@ -1,11 +1,9 @@
|
||||||
import type { NextConfig } from "next";
|
import type { NextConfig } from "next";
|
||||||
|
|
||||||
const nextConfig: NextConfig = {
|
const nextConfig: NextConfig = {
|
||||||
experimental: {
|
turbopack: {
|
||||||
turbo: {
|
resolveAlias: {
|
||||||
resolveAlias: {
|
canvas: './empty-module.ts',
|
||||||
canvas: './empty-module.ts',
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
||||||
39
package-lock.json
generated
39
package-lock.json
generated
|
|
@ -12,6 +12,7 @@
|
||||||
"@types/howler": "^2.2.12",
|
"@types/howler": "^2.2.12",
|
||||||
"@types/string-similarity": "^4.0.2",
|
"@types/string-similarity": "^4.0.2",
|
||||||
"@types/uuid": "^10.0.0",
|
"@types/uuid": "^10.0.0",
|
||||||
|
"@vercel/analytics": "^1.5.0",
|
||||||
"compromise": "^14.14.4",
|
"compromise": "^14.14.4",
|
||||||
"core-js": "^3.41.0",
|
"core-js": "^3.41.0",
|
||||||
"howler": "^2.2.4",
|
"howler": "^2.2.4",
|
||||||
|
|
@ -1987,6 +1988,44 @@
|
||||||
"win32"
|
"win32"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"node_modules/@vercel/analytics": {
|
||||||
|
"version": "1.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@vercel/analytics/-/analytics-1.5.0.tgz",
|
||||||
|
"integrity": "sha512-MYsBzfPki4gthY5HnYN7jgInhAZ7Ac1cYDoRWFomwGHWEX7odTEzbtg9kf/QSo7XEsEAqlQugA6gJ2WS2DEa3g==",
|
||||||
|
"license": "MPL-2.0",
|
||||||
|
"peerDependencies": {
|
||||||
|
"@remix-run/react": "^2",
|
||||||
|
"@sveltejs/kit": "^1 || ^2",
|
||||||
|
"next": ">= 13",
|
||||||
|
"react": "^18 || ^19 || ^19.0.0-rc",
|
||||||
|
"svelte": ">= 4",
|
||||||
|
"vue": "^3",
|
||||||
|
"vue-router": "^4"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@remix-run/react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@sveltejs/kit": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"next": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"svelte": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"vue": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"vue-router": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@xmldom/xmldom": {
|
"node_modules/@xmldom/xmldom": {
|
||||||
"version": "0.7.13",
|
"version": "0.7.13",
|
||||||
"resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.7.13.tgz",
|
"resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.7.13.tgz",
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@
|
||||||
"@types/howler": "^2.2.12",
|
"@types/howler": "^2.2.12",
|
||||||
"@types/string-similarity": "^4.0.2",
|
"@types/string-similarity": "^4.0.2",
|
||||||
"@types/uuid": "^10.0.0",
|
"@types/uuid": "^10.0.0",
|
||||||
|
"@vercel/analytics": "^1.5.0",
|
||||||
"compromise": "^14.14.4",
|
"compromise": "^14.14.4",
|
||||||
"core-js": "^3.41.0",
|
"core-js": "^3.41.0",
|
||||||
"howler": "^2.2.4",
|
"howler": "^2.2.4",
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import { Providers } from "@/app/providers";
|
||||||
import { Metadata } from "next";
|
import { Metadata } from "next";
|
||||||
import { Footer } from "@/components/Footer";
|
import { Footer } from "@/components/Footer";
|
||||||
import { Toaster } from 'react-hot-toast';
|
import { Toaster } from 'react-hot-toast';
|
||||||
|
import { Analytics } from "@vercel/analytics/next"
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: "OpenReader WebUI",
|
title: "OpenReader WebUI",
|
||||||
|
|
@ -59,6 +60,7 @@ export default function RootLayout({ children }: { children: ReactNode }) {
|
||||||
<div className="relative max-w-6xl mx-auto align-center">
|
<div className="relative max-w-6xl mx-auto align-center">
|
||||||
<div className="bg-base rounded-lg shadow-lg">
|
<div className="bg-base rounded-lg shadow-lg">
|
||||||
{children}
|
{children}
|
||||||
|
<Analytics />
|
||||||
</div>
|
</div>
|
||||||
{!isDev && <Footer />}
|
{!isDev && <Footer />}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,10 @@ export function Footer() {
|
||||||
</PopoverButton>
|
</PopoverButton>
|
||||||
<PopoverPanel anchor="top" className="bg-base p-4 rounded-lg shadow-lg w-64">
|
<PopoverPanel anchor="top" className="bg-base p-4 rounded-lg shadow-lg w-64">
|
||||||
<p>Documents are uploaded to your local browser cache.</p>
|
<p>Documents are uploaded to your local browser cache.</p>
|
||||||
<p className='mt-3'>Each sentence of the document you are viewing is sent to my Kokoro-FastAPI server for audio generation, no requests or data is collected.</p>
|
<p className='mt-3'>Each sentence of the document you are viewing is sent to my Kokoro-FastAPI server for audio generation.</p>
|
||||||
|
<p className='mt-3'>The audio is streamed back to your browser and played in real-time.</p>
|
||||||
|
{/* Vercel analytics disclaimer */}
|
||||||
|
<p className='mt-3'>This site uses Vercel Analytics to collect anonymous usage data to help improve the service.</p>
|
||||||
</PopoverPanel>
|
</PopoverPanel>
|
||||||
</Popover>
|
</Popover>
|
||||||
<span className='w-full sm:w-fit'>•</span>
|
<span className='w-full sm:w-fit'>•</span>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { Button } from '@headlessui/react';
|
import { Button, Popover, PopoverButton, PopoverPanel } from '@headlessui/react';
|
||||||
import { useState, useEffect, useRef } from 'react';
|
import { useState, useEffect, useRef } from 'react';
|
||||||
|
|
||||||
export const Navigator = ({ currentPage, numPages, skipToLocation }: {
|
export const Navigator = ({ currentPage, numPages, skipToLocation }: {
|
||||||
|
|
@ -8,7 +8,7 @@ export const Navigator = ({ currentPage, numPages, skipToLocation }: {
|
||||||
numPages: number | undefined;
|
numPages: number | undefined;
|
||||||
skipToLocation: (location: string | number, shouldPause?: boolean) => void;
|
skipToLocation: (location: string | number, shouldPause?: boolean) => void;
|
||||||
}) => {
|
}) => {
|
||||||
const [inputValue, setInputValue] = useState(currentPage.toString());
|
const [inputValue, setInputValue] = useState('');
|
||||||
const inputRef = useRef<HTMLInputElement>(null);
|
const inputRef = useRef<HTMLInputElement>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
@ -22,6 +22,7 @@ export const Navigator = ({ currentPage, numPages, skipToLocation }: {
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleInputConfirm = () => {
|
const handleInputConfirm = () => {
|
||||||
|
if (inputValue === '') return; // Don't do anything if input is empty
|
||||||
let page = parseInt(inputValue, 10);
|
let page = parseInt(inputValue, 10);
|
||||||
if (isNaN(page)) return;
|
if (isNaN(page)) return;
|
||||||
const maxPage = numPages || 1;
|
const maxPage = numPages || 1;
|
||||||
|
|
@ -29,9 +30,8 @@ export const Navigator = ({ currentPage, numPages, skipToLocation }: {
|
||||||
if (page > maxPage) page = maxPage;
|
if (page > maxPage) page = maxPage;
|
||||||
if (page !== currentPage) {
|
if (page !== currentPage) {
|
||||||
skipToLocation(page, true);
|
skipToLocation(page, true);
|
||||||
} else {
|
|
||||||
setInputValue(page.toString()); // reset input if unchanged
|
|
||||||
}
|
}
|
||||||
|
setInputValue(''); // Clear input after confirming
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleInputKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
|
const handleInputKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
|
||||||
|
|
@ -41,6 +41,10 @@ export const Navigator = ({ currentPage, numPages, skipToLocation }: {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handlePopoverOpen = () => {
|
||||||
|
setInputValue(''); // Clear input when popup opens
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center space-x-1">
|
<div className="flex items-center space-x-1">
|
||||||
{/* Page back */}
|
{/* Page back */}
|
||||||
|
|
@ -55,22 +59,36 @@ export const Navigator = ({ currentPage, numPages, skipToLocation }: {
|
||||||
</svg>
|
</svg>
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
{/* Page number input */}
|
{/* Page number popup */}
|
||||||
<div className="bg-offbase px-2 py-0.5 rounded-full flex items-center">
|
<Popover className="relative">
|
||||||
<input
|
<PopoverButton
|
||||||
ref={inputRef}
|
className="bg-offbase px-2 py-0.5 rounded-full focus:outline-none cursor-pointer hover:bg-offbase/80"
|
||||||
type="text"
|
onClick={handlePopoverOpen}
|
||||||
inputMode="numeric"
|
>
|
||||||
pattern="[0-9]*"
|
<p className="text-xs whitespace-nowrap">
|
||||||
className="w-6 text-xs text-accent bg-transparent outline-none appearance-none text-right"
|
{currentPage} / {numPages || 1}
|
||||||
value={inputValue}
|
</p>
|
||||||
onChange={handleInputChange}
|
</PopoverButton>
|
||||||
onBlur={handleInputConfirm}
|
<PopoverPanel anchor="top" className="absolute z-50 bg-base p-3 rounded-md shadow-lg border border-offbase">
|
||||||
onKeyDown={handleInputKeyDown}
|
<div className="flex flex-col space-y-2">
|
||||||
aria-label="Page number"
|
<div className="text-xs font-medium text-foreground">Go to page</div>
|
||||||
/>
|
<input
|
||||||
<span className="w-6 text-xs ml-1">/ {numPages || 1}</span>
|
ref={inputRef}
|
||||||
</div>
|
type="text"
|
||||||
|
inputMode="numeric"
|
||||||
|
pattern="[0-9]*"
|
||||||
|
className="w-20 px-2 py-1 text-xs text-accent bg-offbase rounded border-none outline-none appearance-none text-center"
|
||||||
|
value={inputValue}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
onBlur={handleInputConfirm}
|
||||||
|
onKeyDown={handleInputKeyDown}
|
||||||
|
placeholder={currentPage.toString()}
|
||||||
|
aria-label="Page number"
|
||||||
|
/>
|
||||||
|
<div className="text-xs text-foreground/70 text-center">of {numPages || 1}</div>
|
||||||
|
</div>
|
||||||
|
</PopoverPanel>
|
||||||
|
</Popover>
|
||||||
|
|
||||||
{/* Page forward */}
|
{/* Page forward */}
|
||||||
<Button
|
<Button
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue