adjust dark mode and layout
All checks were successful
Build and Deploy / build (push) Successful in 2m2s

This commit is contained in:
鲁树人 2025-05-18 23:18:01 +09:00
parent 6cb1f9f87f
commit 3ab73d8369
16 changed files with 53 additions and 45 deletions

View File

@ -48,6 +48,11 @@ export function AdbInstructionTemplate({ dir, file, platform }: AdbInstructionTe
<SyntaxHighlighter language={language} style={hljsStyleGitHub}> <SyntaxHighlighter language={language} style={hljsStyleGitHub}>
{command} {command}
</SyntaxHighlighter> </SyntaxHighlighter>
<br />
<ExtLink className="text-nowrap" href="https://g.126.fm/04jewvw">
MuMu
</ExtLink>
使 <code>adb connect ...</code>
</li> </li>
<li> <li>
<code>{file}</code> <code>{file}</code>

View File

@ -8,7 +8,7 @@ export interface HeaderProps {
export function Header3({ children, className, id }: HeaderProps) { export function Header3({ children, className, id }: HeaderProps) {
return ( return (
<h3 id={id} className={`text-2xl pt-3 pb-1 font-bold border-b border-base-300 text-neutral-800 ${className}`}> <h3 id={id} className={`text-2xl pt-3 pb-1 font-bold border-b border-base-300 ${className}`}>
{children} {children}
</h3> </h3>
); );
@ -16,7 +16,7 @@ export function Header3({ children, className, id }: HeaderProps) {
export function Header4({ children, className, id }: HeaderProps) { export function Header4({ children, className, id }: HeaderProps) {
return ( return (
<h4 id={id} className={`text-xl pt-3 pb-1 font-semibold text-neutral-800 ${className}`}> <h4 id={id} className={`text-xl pt-3 pb-1 font-semibold ${className}`}>
{children} {children}
</h4> </h4>
); );
@ -24,7 +24,7 @@ export function Header4({ children, className, id }: HeaderProps) {
export function Header5({ children, className, id }: HeaderProps) { export function Header5({ children, className, id }: HeaderProps) {
return ( return (
<h5 id={id} className={`text-lg pt-3 pb-1 font-semibold text-neutral-800 ${className}`}> <h5 id={id} className={`text-lg pt-3 pb-1 font-semibold ${className}`}>
{children} {children}
</h5> </h5>
); );

View File

@ -10,7 +10,7 @@ export function KugouFAQ() {
<p> <p>
<code>kgg</code> Windows <code>kgg</code> Windows
</p> </p>
<p></p> <p className="my-4"></p>
<div className="p-2 @container"> <div className="p-2 @container">
<div className="alert alert-warning"> <div className="alert alert-warning">

View File

@ -11,7 +11,7 @@ export function KuwoFAQ() {
<> <>
<Header4></Header4> <Header4></Header4>
<SegmentTryOfficialPlayer /> <SegmentTryOfficialPlayer />
<p> <p className="my-4">
<HiWord></HiWord> <HiWord></HiWord>
<VQuote> <VQuote>
<strong></strong> <strong></strong>
@ -22,10 +22,10 @@ export function KuwoFAQ() {
</VQuote> </VQuote>
</p> </p>
<p></p> <p className="my-4"></p>
<p>PC平台暂未推出使用新版加密的音质</p> <p className="my-4">PC平台暂未推出使用新版加密的音质</p>
<div className="alert alert-warning"> <div className="alert alert-warning mb-4">
<RiErrorWarningLine className="text-2xl" /> <RiErrorWarningLine className="text-2xl" />
<div> <div>
<p> root </p> <p> root </p>

View File

@ -50,8 +50,8 @@ export function OtherFAQ() {
root 使 root 使
使 NFC 使 NFC
</p> </p>
<p>使</p> <p className="my-2">使</p>
<p> <p className="my-2">
root 便 root 便
<VQuote> <VQuote>
<ExtLink href="https://mumu.163.com/"> MuMu 12</ExtLink> <ExtLink href="https://mumu.163.com/"> MuMu 12</ExtLink>

View File

@ -7,8 +7,8 @@ export function QQMusicFAQ() {
<> <>
<Header4></Header4> <Header4></Header4>
<SegmentTryOfficialPlayer /> <SegmentTryOfficialPlayer />
<p></p> <p className="my-4"></p>
<p className="mb-2"> <p className="mb-4">
<strong></strong>使 <strong></strong>使
</p> </p>

View File

@ -16,7 +16,7 @@ export function SegmentKeyImportInstructions({
}: SegmentKeyImportInstructionsProps) { }: SegmentKeyImportInstructionsProps) {
return ( return (
<> <>
<p></p> <p className="mt-2"></p>
<ol className="list-decimal pl-5"> <ol className="list-decimal pl-5">
<li> <li>
<SegmentTopNavSettings /> <SegmentTopNavSettings />
@ -28,7 +28,7 @@ export function SegmentKeyImportInstructions({
<SegmentAddKeyDropdown /> <SegmentAddKeyDropdown />
</li> </li>
<li> <li>
<p>{keyInstructionText}</p> <p className="mb-2">{keyInstructionText}</p>
{clientInstructions} {clientInstructions}
</li> </li>
</ol> </ol>

View File

@ -1,8 +1,8 @@
import { RiErrorWarningLine } from 'react-icons/ri'; import { RiErrorWarningLine } from 'react-icons/ri';
export function SegmentTryOfficialPlayer() { export function SegmentTryOfficialPlayer({ className = '' }: { className?: string }) {
return ( return (
<div className="alert alert-warning"> <div className={`alert alert-warning ${className}`}>
<RiErrorWarningLine className="text-2xl" /> <RiErrorWarningLine className="text-2xl" />
<p></p> <p></p>
</div> </div>

View File

@ -11,31 +11,23 @@ interface FileRowProps {
export function FileRow({ id, file }: FileRowProps) { export function FileRow({ id, file }: FileRowProps) {
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const isDecrypted = file.state === ProcessState.COMPLETE; const isDecrypted = file.state === ProcessState.COMPLETE;
const metadata = file.metadata;
const nameWithoutExt = file.fileName.replace(/\.[a-z\d]{3,6}$/, ''); const decryptedName = file.cleanName + '.' + file.ext;
const decryptedName = nameWithoutExt + '.' + file.ext;
return ( return (
<div className="card bg-base-100 shadow-sm w-full md:w-[30%] " data-testid="file-row"> <div className="card bg-base-100 shadow-sm w-full md:w-[30%] " data-testid="file-row">
<div className="card-body items-center text-center px-2"> <div className="card-body items-center text-center px-2">
<h2 <h2 className="card-title max-w-full whitespace-nowrap flex gap-0" data-testid="audio-meta-song-name">
className="card-title overflow-hidden text-ellipsis block max-w-full whitespace-nowrap" <span className="grow overflow-hidden text-ellipsis" title={decryptedName}>
data-testid="audio-meta-song-name" {file.cleanName}
> </span>
{metadata?.name ?? nameWithoutExt} {isDecrypted && file.ext && <div className="ml-2 badge badge-accent">{file.ext}</div>}
</h2> </h2>
<div className="w-full grow"> <div className="w-full grow">
{file.state === ProcessState.ERROR && <FileError error={file.errorMessage} code={file.errorCode} />} {file.state === ProcessState.ERROR && <FileError error={file.errorMessage} code={file.errorCode} />}
{isDecrypted && ( {isDecrypted && (
<audio <audio className="w-full" aria-disabled={!file.decrypted} controls autoPlay={false} src={file.decrypted} />
className="max-w-[100%]"
aria-disabled={!file.decrypted}
controls
autoPlay={false}
src={file.decrypted}
/>
)} )}
</div> </div>

View File

@ -14,5 +14,5 @@ test('should be able to render a list of 3 items', () => {
}); });
expect(screen.getAllByTestId('file-row')).toHaveLength(3); expect(screen.getAllByTestId('file-row')).toHaveLength(3);
expect(screen.getByText('r Alice')).toBeInTheDocument(); expect(screen.getByText('ready')).toBeInTheDocument();
}); });

View File

@ -3,17 +3,14 @@ import { untouchedFile } from './__fixture__/file-list';
import { FileRow } from '../FileRow'; import { FileRow } from '../FileRow';
import { completedFile } from './__fixture__/file-list'; import { completedFile } from './__fixture__/file-list';
test('should render no metadata when unavailable', () => { test('should render basic title (ready)', () => {
renderWithProviders(<FileRow id="file://ready" file={untouchedFile} />); renderWithProviders(<FileRow id="file://ready" file={untouchedFile} />);
expect(screen.getAllByTestId('file-row')).toHaveLength(1); expect(screen.getAllByTestId('file-row')).toHaveLength(1);
expect(screen.getByTestId('audio-meta-song-name')).toHaveTextContent('ready'); expect(screen.getByTestId('audio-meta-song-name')).toHaveTextContent('ready');
expect(screen.queryByTestId('audio-meta-album-name')).toBeFalsy();
expect(screen.queryByTestId('audio-meta-song-artist')).toBeFalsy();
expect(screen.queryByTestId('audio-meta-album-artist')).toBeFalsy();
}); });
test('should render metadata when file has been processed', () => { test('should render basic title (done)', () => {
renderWithProviders(<FileRow id="file://done" file={completedFile} />); renderWithProviders(<FileRow id="file://done" file={completedFile} />);
expect(screen.getAllByTestId('file-row')).toHaveLength(1); expect(screen.getAllByTestId('file-row')).toHaveLength(1);

View File

@ -1,6 +1,7 @@
import { DecryptedAudioFile, ProcessState } from '../../fileListingSlice'; import { DecryptedAudioFile, ProcessState } from '../../fileListingSlice';
export const untouchedFile: DecryptedAudioFile = { export const untouchedFile: DecryptedAudioFile = {
cleanName: 'ready',
fileName: 'ready.bin', fileName: 'ready.bin',
raw: 'blob://localhost/file-a', raw: 'blob://localhost/file-a',
decrypted: '', decrypted: '',
@ -13,6 +14,7 @@ export const untouchedFile: DecryptedAudioFile = {
export const completedFile: DecryptedAudioFile = { export const completedFile: DecryptedAudioFile = {
fileName: 'hello-b.bin', fileName: 'hello-b.bin',
cleanName: 'hello-b',
raw: 'blob://localhost/file-b', raw: 'blob://localhost/file-b',
decrypted: 'blob://localhost/file-b-decrypted', decrypted: 'blob://localhost/file-b-decrypted',
ext: 'flac', ext: 'flac',
@ -30,6 +32,7 @@ export const completedFile: DecryptedAudioFile = {
export const fileWithError: DecryptedAudioFile = { export const fileWithError: DecryptedAudioFile = {
fileName: 'hello-c.bin', fileName: 'hello-c.bin',
cleanName: 'hello-c',
raw: 'blob://localhost/file-c', raw: 'blob://localhost/file-c',
decrypted: 'blob://localhost/file-c-decrypted', decrypted: 'blob://localhost/file-c-decrypted',
ext: 'flac', ext: 'flac',

View File

@ -5,9 +5,11 @@ import type { RootState } from '~/store';
import { DECRYPTION_WORKER_ACTION_NAME, type DecryptionResult } from '~/decrypt-worker/constants'; import { DECRYPTION_WORKER_ACTION_NAME, type DecryptionResult } from '~/decrypt-worker/constants';
import type { import type {
DecryptCommandOptions, DecryptCommandOptions,
FetchMusicExNamePayload, ParseKugouHeaderPayload, ParseKugouHeaderResponse, FetchMusicExNamePayload,
ParseKugouHeaderPayload,
ParseKugouHeaderResponse,
ParseKuwoHeaderPayload, ParseKuwoHeaderPayload,
ParseKuwoHeaderResponse ParseKuwoHeaderResponse,
} from '~/decrypt-worker/types'; } from '~/decrypt-worker/types';
import { decryptionQueue, workerClientBus } from '~/decrypt-worker/client'; import { decryptionQueue, workerClientBus } from '~/decrypt-worker/client';
import { DecryptErrorType } from '~/decrypt-worker/util/DecryptError'; import { DecryptErrorType } from '~/decrypt-worker/util/DecryptError';
@ -15,8 +17,9 @@ import {
selectKugouKey, selectKugouKey,
selectKWMv2Key, selectKWMv2Key,
selectQMCv2KeyByFileName, selectQMCv2KeyByFileName,
selectQtfmAndroidKey selectQtfmAndroidKey,
} from '../settings/settingsSelector'; } from '../settings/settingsSelector';
import { cleanFilename } from '~/util/cleanFilename';
export enum ProcessState { export enum ProcessState {
QUEUED = 'QUEUED', QUEUED = 'QUEUED',
@ -40,6 +43,7 @@ export interface AudioMetadata {
export interface DecryptedAudioFile { export interface DecryptedAudioFile {
fileName: string; fileName: string;
cleanName: string;
raw: string; // blob uri raw: string; // blob uri
ext: string; ext: string;
decrypted: string; // blob uri decrypted: string; // blob uri
@ -106,6 +110,7 @@ export const fileListingSlice = createSlice({
addNewFile: (state, { payload }: PayloadAction<{ id: string; fileName: string; blobURI: string }>) => { addNewFile: (state, { payload }: PayloadAction<{ id: string; fileName: string; blobURI: string }>) => {
state.files[payload.id] = { state.files[payload.id] = {
fileName: payload.fileName, fileName: payload.fileName,
cleanName: cleanFilename(payload.fileName),
raw: payload.blobURI, raw: payload.blobURI,
decrypted: '', decrypted: '',
ext: '', ext: '',

View File

@ -20,7 +20,7 @@ export function ResponsiveNav({
className={`@container/nav grow grid grid-cols-1 grid-rows-[auto_1fr] md:grid-rows-1 md:grid-cols-[10rem_1fr] ${className}`} className={`@container/nav grow grid grid-cols-1 grid-rows-[auto_1fr] md:grid-rows-1 md:grid-cols-[10rem_1fr] ${className}`}
> >
{/* Sidebar */} {/* Sidebar */}
<aside className={`bg-gray-100 md:p-4 md:block ${navigationClassName}`}>{navigation}</aside> <aside className={`bg-base-300 md:p-4 md:block ${navigationClassName}`}>{navigation}</aside>
{/* Main content */} {/* Main content */}
<div className={`p-4 grow ${contentClassName}`}>{children}</div> <div className={`p-4 grow ${contentClassName}`}>{children}</div>

View File

@ -1,4 +1,4 @@
import { FC, Fragment } from 'react'; import { ComponentType, Fragment } from 'react';
import { Header3 } from '~/components/HelpText/Headers'; import { Header3 } from '~/components/HelpText/Headers';
import { KuwoFAQ } from '~/faq/KuwoFAQ'; import { KuwoFAQ } from '~/faq/KuwoFAQ';
import { OtherFAQ } from '~/faq/OtherFAQ'; import { OtherFAQ } from '~/faq/OtherFAQ';
@ -8,7 +8,7 @@ import { KugouFAQ } from '~/faq/KugouFAQ.tsx';
type FAQEntry = { type FAQEntry = {
id: string; id: string;
title: string; title: string;
Help: FC; Help: ComponentType;
}; };
const faqEntries: FAQEntry[] = [ const faqEntries: FAQEntry[] = [
@ -20,10 +20,10 @@ const faqEntries: FAQEntry[] = [
export function FaqTab() { export function FaqTab() {
return ( return (
<section className="container pb-10"> <section className="container pb-10 px-4">
<h2 className="text-3xl font-bold text-center"></h2> <h2 className="text-3xl font-bold text-center"></h2>
<Header3></Header3> <Header3></Header3>
<ul className="list-disc list-inside"> <ul className="list-disc pl-6">
{faqEntries.map(({ id, title }) => ( {faqEntries.map(({ id, title }) => (
<li key={id}> <li key={id}>
<a className="link link-info no-underline" href={`#faq-${id}`}> <a className="link link-info no-underline" href={`#faq-${id}`}>

View File

@ -0,0 +1,6 @@
export function cleanFilename(filename: string): string {
return filename
.replace(/\.[a-z\d]{3,6}$/, '') // Remove file extension
.replace(/\.(mgg|kgg|mflac)\d*$/, '') // Remove extra mgg/kgg/mflac extension
.replace(/\s?\[mq[a-z\d]*\]\s*$/, ''); // Remove " [mqms*]" suffix
}