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}>
{command}
</SyntaxHighlighter>
<br />
<ExtLink className="text-nowrap" href="https://g.126.fm/04jewvw">
MuMu
</ExtLink>
使 <code>adb connect ...</code>
</li>
<li>
<code>{file}</code>

View File

@ -8,7 +8,7 @@ export interface HeaderProps {
export function Header3({ children, className, id }: HeaderProps) {
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}
</h3>
);
@ -16,7 +16,7 @@ export function Header3({ children, className, id }: HeaderProps) {
export function Header4({ children, className, id }: HeaderProps) {
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}
</h4>
);
@ -24,7 +24,7 @@ export function Header4({ children, className, id }: HeaderProps) {
export function Header5({ children, className, id }: HeaderProps) {
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}
</h5>
);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,6 +1,7 @@
import { DecryptedAudioFile, ProcessState } from '../../fileListingSlice';
export const untouchedFile: DecryptedAudioFile = {
cleanName: 'ready',
fileName: 'ready.bin',
raw: 'blob://localhost/file-a',
decrypted: '',
@ -13,6 +14,7 @@ export const untouchedFile: DecryptedAudioFile = {
export const completedFile: DecryptedAudioFile = {
fileName: 'hello-b.bin',
cleanName: 'hello-b',
raw: 'blob://localhost/file-b',
decrypted: 'blob://localhost/file-b-decrypted',
ext: 'flac',
@ -30,6 +32,7 @@ export const completedFile: DecryptedAudioFile = {
export const fileWithError: DecryptedAudioFile = {
fileName: 'hello-c.bin',
cleanName: 'hello-c',
raw: 'blob://localhost/file-c',
decrypted: 'blob://localhost/file-c-decrypted',
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 type {
DecryptCommandOptions,
FetchMusicExNamePayload, ParseKugouHeaderPayload, ParseKugouHeaderResponse,
FetchMusicExNamePayload,
ParseKugouHeaderPayload,
ParseKugouHeaderResponse,
ParseKuwoHeaderPayload,
ParseKuwoHeaderResponse
ParseKuwoHeaderResponse,
} from '~/decrypt-worker/types';
import { decryptionQueue, workerClientBus } from '~/decrypt-worker/client';
import { DecryptErrorType } from '~/decrypt-worker/util/DecryptError';
@ -15,8 +17,9 @@ import {
selectKugouKey,
selectKWMv2Key,
selectQMCv2KeyByFileName,
selectQtfmAndroidKey
selectQtfmAndroidKey,
} from '../settings/settingsSelector';
import { cleanFilename } from '~/util/cleanFilename';
export enum ProcessState {
QUEUED = 'QUEUED',
@ -40,6 +43,7 @@ export interface AudioMetadata {
export interface DecryptedAudioFile {
fileName: string;
cleanName: string;
raw: string; // blob uri
ext: string;
decrypted: string; // blob uri
@ -106,6 +110,7 @@ export const fileListingSlice = createSlice({
addNewFile: (state, { payload }: PayloadAction<{ id: string; fileName: string; blobURI: string }>) => {
state.files[payload.id] = {
fileName: payload.fileName,
cleanName: cleanFilename(payload.fileName),
raw: payload.blobURI,
decrypted: '',
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}`}
>
{/* 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 */}
<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 { KuwoFAQ } from '~/faq/KuwoFAQ';
import { OtherFAQ } from '~/faq/OtherFAQ';
@ -8,7 +8,7 @@ import { KugouFAQ } from '~/faq/KugouFAQ.tsx';
type FAQEntry = {
id: string;
title: string;
Help: FC;
Help: ComponentType;
};
const faqEntries: FAQEntry[] = [
@ -20,10 +20,10 @@ const faqEntries: FAQEntry[] = [
export function FaqTab() {
return (
<section className="container pb-10">
<section className="container pb-10 px-4">
<h2 className="text-3xl font-bold text-center"></h2>
<Header3></Header3>
<ul className="list-disc list-inside">
<ul className="list-disc pl-6">
{faqEntries.map(({ id, title }) => (
<li key={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
}