karaoke-gen 0.75.53__py3-none-any.whl → 0.76.20__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- karaoke_gen/audio_fetcher.py +218 -0
- karaoke_gen/karaoke_gen.py +190 -25
- karaoke_gen/lyrics_processor.py +14 -25
- karaoke_gen/utils/__init__.py +26 -0
- karaoke_gen/utils/cli_args.py +9 -1
- karaoke_gen/utils/gen_cli.py +1 -1
- karaoke_gen/utils/remote_cli.py +33 -6
- {karaoke_gen-0.75.53.dist-info → karaoke_gen-0.76.20.dist-info}/METADATA +2 -2
- {karaoke_gen-0.75.53.dist-info → karaoke_gen-0.76.20.dist-info}/RECORD +36 -32
- lyrics_transcriber/frontend/index.html +5 -1
- lyrics_transcriber/frontend/package-lock.json +4553 -0
- lyrics_transcriber/frontend/package.json +3 -0
- lyrics_transcriber/frontend/playwright.config.ts +69 -0
- lyrics_transcriber/frontend/public/nomad-karaoke-logo.svg +5 -0
- lyrics_transcriber/frontend/src/App.tsx +88 -59
- lyrics_transcriber/frontend/src/components/AIFeedbackModal.tsx +55 -21
- lyrics_transcriber/frontend/src/components/AppHeader.tsx +65 -0
- lyrics_transcriber/frontend/src/components/CorrectedWordWithActions.tsx +5 -5
- lyrics_transcriber/frontend/src/components/DurationTimelineView.tsx +9 -9
- lyrics_transcriber/frontend/src/components/EditModal.tsx +1 -1
- lyrics_transcriber/frontend/src/components/EditWordList.tsx +1 -1
- lyrics_transcriber/frontend/src/components/Header.tsx +34 -48
- lyrics_transcriber/frontend/src/components/LyricsSynchronizer/TimelineCanvas.tsx +22 -21
- lyrics_transcriber/frontend/src/components/ReferenceView.tsx +1 -1
- lyrics_transcriber/frontend/src/components/TranscriptionView.tsx +1 -1
- lyrics_transcriber/frontend/src/components/WordDivider.tsx +3 -3
- lyrics_transcriber/frontend/src/components/shared/components/Word.tsx +2 -2
- lyrics_transcriber/frontend/src/components/shared/constants.ts +15 -5
- lyrics_transcriber/frontend/src/main.tsx +1 -7
- lyrics_transcriber/frontend/src/theme.ts +337 -135
- lyrics_transcriber/frontend/vite.config.ts +5 -0
- lyrics_transcriber/frontend/yarn.lock +1005 -1046
- lyrics_transcriber/review/server.py +1 -1
- {karaoke_gen-0.75.53.dist-info → karaoke_gen-0.76.20.dist-info}/WHEEL +0 -0
- {karaoke_gen-0.75.53.dist-info → karaoke_gen-0.76.20.dist-info}/entry_points.txt +0 -0
- {karaoke_gen-0.75.53.dist-info → karaoke_gen-0.76.20.dist-info}/licenses/LICENSE +0 -0
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
"@mui/icons-material": "^6.3.0",
|
|
20
20
|
"@mui/material": "^6.3.0",
|
|
21
21
|
"@mui/system": "^6.4.3",
|
|
22
|
+
"lucide-react": "^0.562.0",
|
|
22
23
|
"nanoid": "^5.0.9",
|
|
23
24
|
"react": "^18.3.1",
|
|
24
25
|
"react-dom": "^18.3.1",
|
|
@@ -26,6 +27,7 @@
|
|
|
26
27
|
},
|
|
27
28
|
"devDependencies": {
|
|
28
29
|
"@eslint/js": "^9.17.0",
|
|
30
|
+
"@playwright/test": "^1.57.0",
|
|
29
31
|
"@types/react": "^18.3.18",
|
|
30
32
|
"@types/react-dom": "^18.3.5",
|
|
31
33
|
"@vitejs/plugin-react": "^4.3.4",
|
|
@@ -34,6 +36,7 @@
|
|
|
34
36
|
"eslint-plugin-react-refresh": "^0.4.16",
|
|
35
37
|
"gh-pages": "^6.3.0",
|
|
36
38
|
"globals": "^15.14.0",
|
|
39
|
+
"playwright": "^1.57.0",
|
|
37
40
|
"typescript": "~5.6.2",
|
|
38
41
|
"typescript-eslint": "^8.18.2",
|
|
39
42
|
"vite": "^6.0.5"
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { defineConfig, devices } from '@playwright/test';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Playwright configuration for lyrics-transcriber frontend E2E tests.
|
|
5
|
+
* Tests run against the local dev server (localhost:5173) served by vite.
|
|
6
|
+
*/
|
|
7
|
+
export default defineConfig({
|
|
8
|
+
testDir: './e2e',
|
|
9
|
+
|
|
10
|
+
// Run tests sequentially for now
|
|
11
|
+
fullyParallel: false,
|
|
12
|
+
|
|
13
|
+
// Fail the build on CI if you accidentally left test.only in the source code
|
|
14
|
+
forbidOnly: !!process.env.CI,
|
|
15
|
+
|
|
16
|
+
// Retry on CI only
|
|
17
|
+
retries: process.env.CI ? 2 : 0,
|
|
18
|
+
|
|
19
|
+
// Single worker
|
|
20
|
+
workers: 1,
|
|
21
|
+
|
|
22
|
+
// Reporter to use
|
|
23
|
+
reporter: [
|
|
24
|
+
['html', { open: 'never' }],
|
|
25
|
+
['list'],
|
|
26
|
+
],
|
|
27
|
+
|
|
28
|
+
// Shared settings for all tests
|
|
29
|
+
use: {
|
|
30
|
+
// Base URL for the local dev server
|
|
31
|
+
baseURL: 'http://localhost:5173',
|
|
32
|
+
|
|
33
|
+
// Collect trace on failure for debugging
|
|
34
|
+
trace: 'on-first-retry',
|
|
35
|
+
|
|
36
|
+
// Screenshot on failure
|
|
37
|
+
screenshot: 'only-on-failure',
|
|
38
|
+
|
|
39
|
+
// Video on failure
|
|
40
|
+
video: 'on-first-retry',
|
|
41
|
+
|
|
42
|
+
// Increase timeout for complex interactions
|
|
43
|
+
actionTimeout: 30000,
|
|
44
|
+
},
|
|
45
|
+
|
|
46
|
+
// Longer timeout for tests
|
|
47
|
+
timeout: 120000, // 2 minutes per test
|
|
48
|
+
|
|
49
|
+
// Expect timeout for assertions
|
|
50
|
+
expect: {
|
|
51
|
+
timeout: 30000, // 30 seconds for expects
|
|
52
|
+
},
|
|
53
|
+
|
|
54
|
+
// Configure projects (browsers)
|
|
55
|
+
projects: [
|
|
56
|
+
{
|
|
57
|
+
name: 'chromium',
|
|
58
|
+
use: { ...devices['Desktop Chrome'] },
|
|
59
|
+
},
|
|
60
|
+
],
|
|
61
|
+
|
|
62
|
+
// Run local dev server before starting tests
|
|
63
|
+
webServer: {
|
|
64
|
+
command: 'yarn dev',
|
|
65
|
+
url: 'http://localhost:5173',
|
|
66
|
+
reuseExistingServer: !process.env.CI,
|
|
67
|
+
timeout: 120000,
|
|
68
|
+
},
|
|
69
|
+
});
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
2
|
+
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
|
3
|
+
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="45 60 255 135">
|
|
4
|
+
<g data-v-70b83f88="" fill="#ff7acc" class="basesvg" transform="translate(48.368003845214844,62.62239074707031)"><g fill-rule="" class="tp-name" transform="translate(0,0)"><g transform="scale(1.4000000000000004)"><g stroke="#ff7acc" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" paint-order="stroke" data-gra-attr="stroke" fill-opacity="0"><path d="M1.4-33.44L1.4 0C31.48 0 3.96 0 34.04 0L34.04-32.64 17.81-32.64 17.81-20.61 1.4-33.44ZM52.21-33.76C42.6-33.76 34.81-25.97 34.81-16.37 34.81-6.76 42.6 1.03 52.21 1.03 61.81 1.03 69.6-6.76 69.6-16.37 69.6-25.97 61.81-33.76 52.21-33.76ZM70.7 0L103.34 0 103.25-33.9 86.93-21.54 70.61-33.9 70.7 0ZM102.34 0C138.2 0 107.52 0 143.56 0L122.95-34.93 102.34 0ZM158.84 0.05C167.84 0.05 175.16-7.27 175.16-16.27 175.16-25.27 167.84-32.64 158.84-32.64L142.52-32.64C142.52 0.75 142.52-33.34 142.52 0.05L158.84 0.05Z" transform="translate(-1.399999976158142, 34.93000030517578)"></path></g><g transform="translate(0,38.959999084472656)"><g stroke="#ff7acc" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" paint-order="stroke" data-gra-attr="stroke" fill="#ff7acc" fill-opacity="0" transform="scale(1.02719)"><path d="M24.43 0L17.67 0 7.98-11.87 7.98 0 2.63 0 2.63-26.64 7.98-26.64 7.98-14.69 17.67-26.64 24.12-26.64 13.13-13.44 24.43 0ZM45.03 0L43.27-5.08 32.66-5.08 30.91 0 25.3 0 34.88-26.68 41.1-26.68 50.68 0 45.03 0ZM34.11-9.35L41.82-9.35 37.97-20.5 34.11-9.35ZM73.19 0L67.01 0 61.13-10.38 58.61-10.38 58.61 0 53.27 0 53.27-26.64 63.27-26.64Q66.36-26.64 68.53-25.55 70.71-24.47 71.8-22.61 72.89-20.76 72.89-18.47L72.89-18.47Q72.89-15.84 71.36-13.72 69.83-11.6 66.82-10.8L66.82-10.8 73.19 0ZM58.61-22.21L58.61-14.39 63.08-14.39Q65.25-14.39 66.32-15.44 67.39-16.49 67.39-18.36L67.39-18.36Q67.39-20.19 66.32-21.2 65.25-22.21 63.08-22.21L63.08-22.21 58.61-22.21ZM94.82 0L93.07-5.08 82.46-5.08 80.7 0 75.09 0 84.67-26.68 90.89-26.68 100.47 0 94.82 0ZM83.91-9.35L91.62-9.35 87.76-20.5 83.91-9.35ZM115.43 0.27Q111.69 0.27 108.56-1.49 105.43-3.24 103.6-6.35 101.76-9.47 101.76-13.4L101.76-13.4Q101.76-17.29 103.6-20.4 105.43-23.51 108.56-25.27 111.69-27.02 115.43-27.02L115.43-27.02Q119.21-27.02 122.32-25.27 125.43-23.51 127.24-20.4 129.05-17.29 129.05-13.4L129.05-13.4Q129.05-9.47 127.24-6.35 125.43-3.24 122.3-1.49 119.17 0.27 115.43 0.27L115.43 0.27ZM115.43-4.5Q117.83-4.5 119.66-5.59 121.5-6.68 122.53-8.7 123.56-10.73 123.56-13.4L123.56-13.4Q123.56-16.07 122.53-18.07 121.5-20.08 119.66-21.15 117.83-22.21 115.43-22.21L115.43-22.21Q113.02-22.21 111.17-21.15 109.32-20.08 108.29-18.07 107.26-16.07 107.26-13.4L107.26-13.4Q107.26-10.73 108.29-8.7 109.32-6.68 111.17-5.59 113.02-4.5 115.43-4.5L115.43-4.5ZM153.82 0L147.06 0 137.37-11.87 137.37 0 132.02 0 132.02-26.64 137.37-26.64 137.37-14.69 147.06-26.64 153.51-26.64 142.52-13.44 153.82 0ZM171.79-22.33L161.67-22.33 161.67-15.65 170.64-15.65 170.64-11.41 161.67-11.41 161.67-4.35 171.79-4.35 171.79 0 156.33 0 156.33-26.68 171.79-26.68 171.79-22.33Z" transform="translate(-2.630000114440918, 27.020000457763672)"></path></g></g></g></g> <g data-gra="path-slogan" fill-rule="" class="tp-slogan" fill="#ffdf6b" transform="translate(5,111.788818359375)"><rect x="0" height="1" y="5.9832000732421875" width="14.278396606445312"></rect> <rect height="1" y="5.9832000732421875" width="14.278396606445312" x="218.985595703125"></rect> <g transform="translate(17.278396606445312,0)"><g transform="scale(1.2800000000000002)"><path d="M10.21-8.38L12.02-8.38L9.68 0L7.70 0L6.13-5.96L4.49 0L2.52 0.01L0.26-8.38L2.06-8.38L3.54-1.87L5.24-8.38L7.12-8.38L8.72-1.91L10.21-8.38ZM18.38-8.38L20.06-8.38L20.06 0L18.38 0L18.38-3.56L14.80-3.56L14.80 0L13.12 0L13.12-8.38L14.80-8.38L14.80-4.93L18.38-4.93L18.38-8.38ZM26.58-7.02L23.40-7.02L23.40-4.92L26.22-4.92L26.22-3.59L23.40-3.59L23.40-1.37L26.58-1.37L26.58 0L21.72 0L21.72-8.39L26.58-8.39L26.58-7.02ZM34.37 0L32.42 0L30.58-3.26L29.78-3.26L29.78 0L28.10 0L28.10-8.38L31.25-8.38Q32.22-8.38 32.90-8.03Q33.59-7.69 33.93-7.11Q34.27-6.53 34.27-5.81L34.27-5.81Q34.27-4.98 33.79-4.31Q33.31-3.65 32.36-3.40L32.36-3.40L34.37 0ZM29.78-6.98L29.78-4.52L31.19-4.52Q31.87-4.52 32.21-4.85Q32.54-5.18 32.54-5.77L32.54-5.77Q32.54-6.35 32.21-6.67Q31.87-6.98 31.19-6.98L31.19-6.98L29.78-6.98ZM40.66-7.02L37.48-7.02L37.48-4.92L40.30-4.92L40.30-3.59L37.48-3.59L37.48-1.37L40.66-1.37L40.66 0L35.80 0L35.80-8.39L40.66-8.39L40.66-7.02ZM47.92-8.38L49.70-8.38L46.63 0L44.59 0L41.52-8.38L43.32-8.38L45.62-1.72L47.92-8.38ZM55.57-7.02L52.39-7.02L52.39-4.92L55.21-4.92L55.21-3.59L52.39-3.59L52.39-1.37L55.57-1.37L55.57 0L50.71 0L50.71-8.39L55.57-8.39L55.57-7.02ZM63.36 0L61.42 0L59.57-3.26L58.78-3.26L58.78 0L57.10 0L57.10-8.38L60.24-8.38Q61.21-8.38 61.90-8.03Q62.58-7.69 62.92-7.11Q63.26-6.53 63.26-5.81L63.26-5.81Q63.26-4.98 62.78-4.31Q62.30-3.65 61.36-3.40L61.36-3.40L63.36 0ZM58.78-6.98L58.78-4.52L60.18-4.52Q60.86-4.52 61.20-4.85Q61.54-5.18 61.54-5.77L61.54-5.77Q61.54-6.35 61.20-6.67Q60.86-6.98 60.18-6.98L60.18-6.98L58.78-6.98ZM72.43-8.38L74.30-8.38L71.47-2.92L71.47 0L69.79 0L69.79-2.92L66.95-8.38L68.84-8.38L70.64-4.55L72.43-8.38ZM79.16 0.08Q77.99 0.08 77.00-0.47Q76.02-1.02 75.44-2.00Q74.87-2.98 74.87-4.21L74.87-4.21Q74.87-5.44 75.44-6.41Q76.02-7.39 77.00-7.94Q77.99-8.50 79.16-8.50L79.16-8.50Q80.35-8.50 81.33-7.94Q82.31-7.39 82.88-6.41Q83.45-5.44 83.45-4.21L83.45-4.21Q83.45-2.98 82.88-2.00Q82.31-1.02 81.32-0.47Q80.34 0.08 79.16 0.08L79.16 0.08ZM79.16-1.42Q79.92-1.42 80.50-1.76Q81.07-2.10 81.40-2.74Q81.72-3.37 81.72-4.21L81.72-4.21Q81.72-5.05 81.40-5.68Q81.07-6.31 80.50-6.65Q79.92-6.98 79.16-6.98L79.16-6.98Q78.41-6.98 77.83-6.65Q77.24-6.31 76.92-5.68Q76.60-5.05 76.60-4.21L76.60-4.21Q76.60-3.37 76.92-2.74Q77.24-2.10 77.83-1.76Q78.41-1.42 79.16-1.42L79.16-1.42ZM84.67-8.38L86.35-8.38L86.35-3.19Q86.35-2.34 86.80-1.89Q87.24-1.44 88.04-1.44L88.04-1.44Q88.86-1.44 89.30-1.89Q89.75-2.34 89.75-3.19L89.75-3.19L89.75-8.38L91.44-8.38L91.44-3.20Q91.44-2.14 90.98-1.40Q90.52-0.66 89.74-0.29Q88.97 0.08 88.02 0.08L88.02 0.08Q87.08 0.08 86.32-0.29Q85.56-0.66 85.12-1.40Q84.67-2.14 84.67-3.20L84.67-3.20L84.67-8.38ZM101.60 0L101.05-1.60L97.72-1.60L97.16 0L95.40 0L98.41-8.39L100.37-8.39L103.38 0L101.60 0ZM98.17-2.94L100.60-2.94L99.38-6.44L98.17-2.94ZM110.77 0L108.83 0L106.98-3.26L106.19-3.26L106.19 0L104.51 0L104.51-8.38L107.65-8.38Q108.62-8.38 109.31-8.03Q109.99-7.69 110.33-7.11Q110.68-6.53 110.68-5.81L110.68-5.81Q110.68-4.98 110.20-4.31Q109.72-3.65 108.77-3.40L108.77-3.40L110.77 0ZM106.19-6.98L106.19-4.52L107.59-4.52Q108.28-4.52 108.61-4.85Q108.95-5.18 108.95-5.77L108.95-5.77Q108.95-6.35 108.61-6.67Q108.28-6.98 107.59-6.98L107.59-6.98L106.19-6.98ZM117.06-7.02L113.88-7.02L113.88-4.92L116.70-4.92L116.70-3.59L113.88-3.59L113.88-1.37L117.06-1.37L117.06 0L112.20 0L112.20-8.39L117.06-8.39L117.06-7.02ZM118.72-1.76L120.42-1.76L119.02 1.61L117.94 1.61L118.72-1.76ZM127.38 0.08Q126.50 0.08 125.80-0.22Q125.10-0.52 124.69-1.08Q124.28-1.64 124.27-2.41L124.27-2.41L126.07-2.41Q126.11-1.90 126.44-1.60Q126.77-1.30 127.34-1.30L127.34-1.30Q127.93-1.30 128.27-1.58Q128.60-1.86 128.60-2.32L128.60-2.32Q128.60-2.69 128.38-2.93Q128.15-3.17 127.81-3.31Q127.46-3.44 126.86-3.61L126.86-3.61Q126.05-3.85 125.54-4.09Q125.03-4.32 124.66-4.79Q124.30-5.27 124.30-6.06L124.30-6.06Q124.30-6.80 124.67-7.36Q125.04-7.91 125.71-8.20Q126.38-8.50 127.25-8.50L127.25-8.50Q128.54-8.50 129.35-7.87Q130.16-7.24 130.25-6.11L130.25-6.11L128.40-6.11Q128.38-6.54 128.03-6.82Q127.69-7.10 127.13-7.10L127.13-7.10Q126.64-7.10 126.34-6.85Q126.05-6.60 126.05-6.12L126.05-6.12Q126.05-5.78 126.27-5.56Q126.49-5.34 126.82-5.20Q127.15-5.06 127.75-4.88L127.75-4.88Q128.57-4.64 129.08-4.40Q129.60-4.16 129.97-3.68Q130.34-3.20 130.34-2.42L130.34-2.42Q130.34-1.75 130.00-1.18Q129.65-0.60 128.98-0.26Q128.30 0.08 127.38 0.08L127.38 0.08ZM131.80-8.38L133.48-8.38L133.48 0L131.80 0L131.80-8.38ZM142.30-8.39L142.30 0L140.62 0L136.81-5.75L136.81 0L135.13 0L135.13-8.39L136.81-8.39L140.62-2.63L140.62-8.39L142.30-8.39ZM151.72-5.86L149.78-5.86Q149.50-6.38 148.99-6.66Q148.49-6.94 147.82-6.94L147.82-6.94Q147.07-6.94 146.50-6.60Q145.92-6.26 145.60-5.64Q145.27-5.02 145.27-4.20L145.27-4.20Q145.27-3.36 145.60-2.74Q145.93-2.11 146.52-1.78Q147.11-1.44 147.89-1.44L147.89-1.44Q148.85-1.44 149.46-1.95Q150.07-2.46 150.26-3.37L150.26-3.37L147.38-3.37L147.38-4.66L151.92-4.66L151.92-3.19Q151.75-2.32 151.20-1.57Q150.65-0.83 149.78-0.38Q148.91 0.07 147.83 0.07L147.83 0.07Q146.62 0.07 145.64-0.47Q144.66-1.02 144.10-1.99Q143.54-2.96 143.54-4.20L143.54-4.20Q143.54-5.44 144.10-6.41Q144.66-7.39 145.64-7.94Q146.62-8.48 147.82-8.48L147.82-8.48Q149.23-8.48 150.28-7.79Q151.32-7.10 151.72-5.86L151.72-5.86ZM153.53-8.52L155.34-8.52L155.15-2.72L153.73-2.72L153.53-8.52ZM154.48 0.08Q154.02 0.08 153.73-0.20Q153.43-0.48 153.43-0.90L153.43-0.90Q153.43-1.32 153.73-1.60Q154.02-1.88 154.48-1.88L154.48-1.88Q154.92-1.88 155.21-1.60Q155.50-1.32 155.50-0.90L155.50-0.90Q155.50-0.48 155.21-0.20Q154.92 0.08 154.48 0.08L154.48 0.08Z" transform="translate(-0.264, 8.52)"></path></g></g></g></g>
|
|
5
|
+
</svg>
|
|
@@ -1,12 +1,23 @@
|
|
|
1
1
|
import UploadFileIcon from '@mui/icons-material/UploadFile'
|
|
2
2
|
import { Alert, Box, Button, Modal, Typography } from '@mui/material'
|
|
3
|
-
import {
|
|
3
|
+
import { ThemeProvider } from '@mui/material/styles'
|
|
4
|
+
import CssBaseline from '@mui/material/CssBaseline'
|
|
5
|
+
import { useEffect, useState, useMemo } from 'react'
|
|
4
6
|
import { ApiClient, FileOnlyClient, LiveApiClient } from './api'
|
|
5
7
|
import CorrectionMetrics from './components/CorrectionMetrics'
|
|
6
8
|
import LyricsAnalyzer from './components/LyricsAnalyzer'
|
|
9
|
+
import AppHeader from './components/AppHeader'
|
|
7
10
|
import { CorrectionData } from './types'
|
|
11
|
+
import { createAppTheme } from './theme'
|
|
12
|
+
|
|
13
|
+
const THEME_STORAGE_KEY = 'nomad-karaoke-lyrics-theme'
|
|
8
14
|
|
|
9
15
|
export default function App() {
|
|
16
|
+
const [isDarkMode, setIsDarkMode] = useState(() => {
|
|
17
|
+
// Initialize from localStorage
|
|
18
|
+
const stored = localStorage.getItem(THEME_STORAGE_KEY)
|
|
19
|
+
return stored !== 'light' // Default to dark
|
|
20
|
+
})
|
|
10
21
|
const [data, setData] = useState<CorrectionData | null>(null)
|
|
11
22
|
const [showMetadata, setShowMetadata] = useState(false)
|
|
12
23
|
const [error, setError] = useState<string | null>(null)
|
|
@@ -14,6 +25,16 @@ export default function App() {
|
|
|
14
25
|
const [isReadOnly, setIsReadOnly] = useState(true)
|
|
15
26
|
const [audioHash, setAudioHash] = useState<string>('')
|
|
16
27
|
|
|
28
|
+
// Create theme based on mode
|
|
29
|
+
const theme = useMemo(() => createAppTheme(isDarkMode ? 'dark' : 'light'), [isDarkMode])
|
|
30
|
+
|
|
31
|
+
// Handle theme toggle
|
|
32
|
+
const handleToggleTheme = () => {
|
|
33
|
+
const newIsDark = !isDarkMode
|
|
34
|
+
setIsDarkMode(newIsDark)
|
|
35
|
+
localStorage.setItem(THEME_STORAGE_KEY, newIsDark ? 'dark' : 'light')
|
|
36
|
+
}
|
|
37
|
+
|
|
17
38
|
useEffect(() => {
|
|
18
39
|
// Parse query parameters
|
|
19
40
|
const params = new URLSearchParams(window.location.search)
|
|
@@ -145,70 +166,78 @@ export default function App() {
|
|
|
145
166
|
|
|
146
167
|
if (!data) {
|
|
147
168
|
return (
|
|
148
|
-
<
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
<>
|
|
156
|
-
<Alert severity="info" sx={{ mb: 2 }}>
|
|
157
|
-
Running in read-only mode. Connect to an API to enable editing.
|
|
169
|
+
<ThemeProvider theme={theme}>
|
|
170
|
+
<CssBaseline />
|
|
171
|
+
<AppHeader isDarkMode={isDarkMode} onToggleTheme={handleToggleTheme} />
|
|
172
|
+
<Box sx={{ p: 3 }}>
|
|
173
|
+
{error && (
|
|
174
|
+
<Alert severity="error" sx={{ mb: 2 }} onClose={() => setError(null)}>
|
|
175
|
+
{error}
|
|
158
176
|
</Alert>
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
177
|
+
)}
|
|
178
|
+
{isReadOnly ? (
|
|
179
|
+
<>
|
|
180
|
+
<Alert severity="info" sx={{ mb: 2 }}>
|
|
181
|
+
Running in read-only mode. Connect to an API to enable editing.
|
|
182
|
+
</Alert>
|
|
183
|
+
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', mb: 3 }}>
|
|
184
|
+
<Typography variant="h4">
|
|
185
|
+
Lyrics Correction Review
|
|
186
|
+
</Typography>
|
|
187
|
+
<Button
|
|
188
|
+
variant="outlined"
|
|
189
|
+
startIcon={<UploadFileIcon />}
|
|
190
|
+
onClick={handleFileLoad}
|
|
191
|
+
>
|
|
192
|
+
Load File
|
|
193
|
+
</Button>
|
|
194
|
+
</Box>
|
|
195
|
+
<Box sx={{ mb: 3 }}>
|
|
196
|
+
<CorrectionMetrics />
|
|
197
|
+
</Box>
|
|
198
|
+
</>
|
|
199
|
+
) : (
|
|
200
|
+
<Box sx={{ display: 'flex', justifyContent: 'center', alignItems: 'center', height: '50vh' }}>
|
|
201
|
+
<Typography variant="h6" color="text.secondary">
|
|
202
|
+
Loading Lyrics Transcription Review...
|
|
162
203
|
</Typography>
|
|
163
|
-
<Button
|
|
164
|
-
variant="outlined"
|
|
165
|
-
startIcon={<UploadFileIcon />}
|
|
166
|
-
onClick={handleFileLoad}
|
|
167
|
-
>
|
|
168
|
-
Load File
|
|
169
|
-
</Button>
|
|
170
|
-
</Box>
|
|
171
|
-
<Box sx={{ mb: 3 }}>
|
|
172
|
-
<CorrectionMetrics />
|
|
173
204
|
</Box>
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
<Typography variant="h6" color="text.secondary">
|
|
178
|
-
Loading Lyrics Correction Review...
|
|
179
|
-
</Typography>
|
|
180
|
-
</Box>
|
|
181
|
-
)}
|
|
182
|
-
</Box>
|
|
205
|
+
)}
|
|
206
|
+
</Box>
|
|
207
|
+
</ThemeProvider>
|
|
183
208
|
)
|
|
184
209
|
}
|
|
185
210
|
|
|
186
211
|
return (
|
|
187
|
-
<
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
212
|
+
<ThemeProvider theme={theme}>
|
|
213
|
+
<CssBaseline />
|
|
214
|
+
<AppHeader isDarkMode={isDarkMode} onToggleTheme={handleToggleTheme} />
|
|
215
|
+
<Box sx={{
|
|
216
|
+
p: 1.5,
|
|
217
|
+
pb: 3,
|
|
218
|
+
maxWidth: '100%',
|
|
219
|
+
overflowX: 'hidden'
|
|
220
|
+
}}>
|
|
221
|
+
{error && (
|
|
222
|
+
<Alert severity="error" sx={{ mb: 1 }} onClose={() => setError(null)}>
|
|
223
|
+
{error}
|
|
224
|
+
</Alert>
|
|
225
|
+
)}
|
|
226
|
+
{isReadOnly && (
|
|
227
|
+
<Alert severity="info" sx={{ mb: 1 }}>
|
|
228
|
+
Running in read-only mode. Connect to an API to enable editing.
|
|
229
|
+
</Alert>
|
|
230
|
+
)}
|
|
231
|
+
<LyricsAnalyzer
|
|
232
|
+
data={data}
|
|
233
|
+
onFileLoad={handleFileLoad}
|
|
234
|
+
onShowMetadata={() => setShowMetadata(true)}
|
|
235
|
+
apiClient={apiClient}
|
|
236
|
+
isReadOnly={isReadOnly}
|
|
237
|
+
audioHash={audioHash}
|
|
238
|
+
/>
|
|
239
|
+
{renderMetadataModal()}
|
|
240
|
+
</Box>
|
|
241
|
+
</ThemeProvider>
|
|
213
242
|
)
|
|
214
243
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React from "react";
|
|
1
|
+
import React, { useEffect, useRef } from "react";
|
|
2
2
|
|
|
3
3
|
type Props = {
|
|
4
4
|
isOpen: boolean;
|
|
@@ -12,22 +12,57 @@ export const AIFeedbackModal: React.FC<Props> = ({ isOpen, onClose, onSubmit, su
|
|
|
12
12
|
const [finalText, setFinalText] = React.useState("");
|
|
13
13
|
const [reasonCategory, setReason] = React.useState("AI_CORRECT");
|
|
14
14
|
const [reasonDetail, setDetail] = React.useState("");
|
|
15
|
+
const modalRef = useRef<HTMLDivElement>(null);
|
|
16
|
+
|
|
17
|
+
// Handle Escape key to close modal
|
|
18
|
+
useEffect(() => {
|
|
19
|
+
const handleKeyDown = (e: KeyboardEvent) => {
|
|
20
|
+
if (e.key === 'Escape' && isOpen) {
|
|
21
|
+
onClose();
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
document.addEventListener('keydown', handleKeyDown);
|
|
25
|
+
return () => document.removeEventListener('keydown', handleKeyDown);
|
|
26
|
+
}, [isOpen, onClose]);
|
|
27
|
+
|
|
28
|
+
// Focus modal on open
|
|
29
|
+
useEffect(() => {
|
|
30
|
+
if (isOpen && modalRef.current) {
|
|
31
|
+
modalRef.current.focus();
|
|
32
|
+
}
|
|
33
|
+
}, [isOpen]);
|
|
15
34
|
|
|
16
35
|
if (!isOpen) return null;
|
|
17
36
|
|
|
37
|
+
// Dark theme colors matching karaoke-gen
|
|
38
|
+
const colors = {
|
|
39
|
+
background: '#1a1a1a', // slate-800
|
|
40
|
+
text: '#f8fafc', // slate-50
|
|
41
|
+
textSecondary: '#888888', // slate-400
|
|
42
|
+
border: '#2a2a2a', // slate-700
|
|
43
|
+
inputBg: '#0f0f0f', // slate-900
|
|
44
|
+
};
|
|
45
|
+
|
|
18
46
|
return (
|
|
19
|
-
<div style={{ position: "fixed", inset: 0, background: "rgba(0,0,0,0.
|
|
20
|
-
<div
|
|
21
|
-
|
|
22
|
-
|
|
47
|
+
<div style={{ position: "fixed", inset: 0, background: "rgba(0,0,0,0.7)", display: "flex", alignItems: "center", justifyContent: "center", zIndex: 1300 }}>
|
|
48
|
+
<div
|
|
49
|
+
ref={modalRef}
|
|
50
|
+
role="dialog"
|
|
51
|
+
aria-modal="true"
|
|
52
|
+
aria-labelledby="ai-feedback-title"
|
|
53
|
+
tabIndex={-1}
|
|
54
|
+
style={{ background: colors.background, padding: 16, width: 480, borderRadius: 8, border: `1px solid ${colors.border}`, color: colors.text, outline: 'none' }}
|
|
55
|
+
>
|
|
56
|
+
<h3 id="ai-feedback-title" style={{ color: colors.text, margin: 0 }}>AI Suggestion</h3>
|
|
57
|
+
<p style={{ marginTop: 8, color: colors.text }}>
|
|
23
58
|
{suggestion?.text ?? "No suggestion"}
|
|
24
59
|
{suggestion?.confidence != null ? ` (confidence ${Math.round((suggestion.confidence || 0) * 100)}%)` : null}
|
|
25
60
|
</p>
|
|
26
|
-
{suggestion?.reasoning ? <small>{suggestion.reasoning}</small> : null}
|
|
61
|
+
{suggestion?.reasoning ? <small style={{ color: colors.textSecondary }}>{suggestion.reasoning}</small> : null}
|
|
27
62
|
|
|
28
|
-
<div style={{ marginTop: 12 }}>
|
|
29
|
-
<label>Action</label>
|
|
30
|
-
<select value={reviewerAction} onChange={(e) => setAction(e.target.value)} style={{
|
|
63
|
+
<div style={{ marginTop: 12, display: 'flex', alignItems: 'center', gap: 8 }}>
|
|
64
|
+
<label htmlFor="ai-action-select" style={{ color: colors.text }}>Action</label>
|
|
65
|
+
<select id="ai-action-select" value={reviewerAction} onChange={(e) => setAction(e.target.value)} style={{ background: colors.inputBg, color: colors.text, border: `1px solid ${colors.border}`, borderRadius: 4, padding: '4px 8px' }}>
|
|
31
66
|
<option value="ACCEPT">Accept</option>
|
|
32
67
|
<option value="REJECT">Reject</option>
|
|
33
68
|
<option value="MODIFY">Modify</option>
|
|
@@ -35,15 +70,15 @@ export const AIFeedbackModal: React.FC<Props> = ({ isOpen, onClose, onSubmit, su
|
|
|
35
70
|
</div>
|
|
36
71
|
|
|
37
72
|
{reviewerAction === "MODIFY" ? (
|
|
38
|
-
<div style={{ marginTop: 12 }}>
|
|
39
|
-
<label>Final Text</label>
|
|
40
|
-
<input value={finalText} onChange={(e) => setFinalText(e.target.value)} style={{
|
|
73
|
+
<div style={{ marginTop: 12, display: 'flex', flexDirection: 'column', gap: 4 }}>
|
|
74
|
+
<label htmlFor="ai-final-text" style={{ color: colors.text }}>Final Text</label>
|
|
75
|
+
<input id="ai-final-text" value={finalText} onChange={(e) => setFinalText(e.target.value)} style={{ width: "100%", background: colors.inputBg, color: colors.text, border: `1px solid ${colors.border}`, borderRadius: 4, padding: '4px 8px', boxSizing: 'border-box' }} />
|
|
41
76
|
</div>
|
|
42
77
|
) : null}
|
|
43
78
|
|
|
44
|
-
<div style={{ marginTop: 12 }}>
|
|
45
|
-
<label>Reason</label>
|
|
46
|
-
<select value={reasonCategory} onChange={(e) => setReason(e.target.value)} style={{
|
|
79
|
+
<div style={{ marginTop: 12, display: 'flex', alignItems: 'center', gap: 8 }}>
|
|
80
|
+
<label htmlFor="ai-reason-select" style={{ color: colors.text }}>Reason</label>
|
|
81
|
+
<select id="ai-reason-select" value={reasonCategory} onChange={(e) => setReason(e.target.value)} style={{ background: colors.inputBg, color: colors.text, border: `1px solid ${colors.border}`, borderRadius: 4, padding: '4px 8px' }}>
|
|
47
82
|
<option value="AI_CORRECT">AI_CORRECT</option>
|
|
48
83
|
<option value="AI_INCORRECT">AI_INCORRECT</option>
|
|
49
84
|
<option value="AI_SUBOPTIMAL">AI_SUBOPTIMAL</option>
|
|
@@ -52,17 +87,18 @@ export const AIFeedbackModal: React.FC<Props> = ({ isOpen, onClose, onSubmit, su
|
|
|
52
87
|
</select>
|
|
53
88
|
</div>
|
|
54
89
|
|
|
55
|
-
<div style={{ marginTop: 12 }}>
|
|
56
|
-
<label>Details</label>
|
|
57
|
-
<textarea value={reasonDetail} onChange={(e) => setDetail(e.target.value)} style={{
|
|
90
|
+
<div style={{ marginTop: 12, display: 'flex', flexDirection: 'column', gap: 4 }}>
|
|
91
|
+
<label htmlFor="ai-details-textarea" style={{ color: colors.text }}>Details</label>
|
|
92
|
+
<textarea id="ai-details-textarea" value={reasonDetail} onChange={(e) => setDetail(e.target.value)} style={{ width: "100%", background: colors.inputBg, color: colors.text, border: `1px solid ${colors.border}`, borderRadius: 4, padding: '4px 8px', boxSizing: 'border-box' }} />
|
|
58
93
|
</div>
|
|
59
94
|
|
|
60
95
|
<div style={{ display: "flex", gap: 8, justifyContent: "flex-end", marginTop: 16 }}>
|
|
61
|
-
<button onClick={onClose}>Cancel</button>
|
|
96
|
+
<button onClick={onClose} style={{ background: colors.border, color: colors.text, border: 'none', borderRadius: 4, padding: '6px 12px', cursor: 'pointer' }}>Cancel</button>
|
|
62
97
|
<button
|
|
63
98
|
onClick={() =>
|
|
64
99
|
onSubmit({ reviewerAction, finalText: finalText || undefined, reasonCategory, reasonDetail: reasonDetail || undefined })
|
|
65
100
|
}
|
|
101
|
+
style={{ background: '#f97316', color: '#fff', border: 'none', borderRadius: 4, padding: '6px 12px', cursor: 'pointer' }}
|
|
66
102
|
>
|
|
67
103
|
Submit
|
|
68
104
|
</button>
|
|
@@ -73,5 +109,3 @@ export const AIFeedbackModal: React.FC<Props> = ({ isOpen, onClose, onSubmit, su
|
|
|
73
109
|
};
|
|
74
110
|
|
|
75
111
|
export default AIFeedbackModal;
|
|
76
|
-
|
|
77
|
-
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { Box, IconButton, Tooltip, Typography, useTheme } from '@mui/material'
|
|
2
|
+
import { Sun, Moon } from 'lucide-react'
|
|
3
|
+
|
|
4
|
+
interface AppHeaderProps {
|
|
5
|
+
isDarkMode?: boolean
|
|
6
|
+
onToggleTheme?: () => void
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export default function AppHeader({ isDarkMode = true, onToggleTheme }: AppHeaderProps) {
|
|
10
|
+
const theme = useTheme()
|
|
11
|
+
|
|
12
|
+
return (
|
|
13
|
+
<Box
|
|
14
|
+
component="header"
|
|
15
|
+
sx={{
|
|
16
|
+
borderBottom: `1px solid ${theme.palette.divider}`,
|
|
17
|
+
backgroundColor: theme.palette.background.paper,
|
|
18
|
+
backdropFilter: 'blur(8px)',
|
|
19
|
+
position: 'sticky',
|
|
20
|
+
top: 0,
|
|
21
|
+
zIndex: 1100,
|
|
22
|
+
px: 2,
|
|
23
|
+
py: 1.5,
|
|
24
|
+
display: 'flex',
|
|
25
|
+
alignItems: 'center',
|
|
26
|
+
justifyContent: 'space-between',
|
|
27
|
+
}}
|
|
28
|
+
>
|
|
29
|
+
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1.5 }}>
|
|
30
|
+
<img src="/nomad-karaoke-logo.svg" alt="Nomad Karaoke" style={{ height: 40 }} />
|
|
31
|
+
<Typography
|
|
32
|
+
variant="h6"
|
|
33
|
+
sx={{
|
|
34
|
+
fontWeight: 'bold',
|
|
35
|
+
color: theme.palette.text.primary,
|
|
36
|
+
fontSize: '1.1rem',
|
|
37
|
+
lineHeight: 1,
|
|
38
|
+
m: 0,
|
|
39
|
+
}}
|
|
40
|
+
>
|
|
41
|
+
Lyrics Transcription Review
|
|
42
|
+
</Typography>
|
|
43
|
+
</Box>
|
|
44
|
+
|
|
45
|
+
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
|
46
|
+
{onToggleTheme && (
|
|
47
|
+
<Tooltip title={isDarkMode ? 'Switch to light mode' : 'Switch to dark mode'}>
|
|
48
|
+
<IconButton
|
|
49
|
+
onClick={onToggleTheme}
|
|
50
|
+
sx={{
|
|
51
|
+
color: theme.palette.text.secondary,
|
|
52
|
+
'&:hover': {
|
|
53
|
+
color: theme.palette.text.primary,
|
|
54
|
+
backgroundColor: theme.palette.action.hover,
|
|
55
|
+
},
|
|
56
|
+
}}
|
|
57
|
+
>
|
|
58
|
+
{isDarkMode ? <Sun className="w-5 h-5" /> : <Moon className="w-5 h-5" />}
|
|
59
|
+
</IconButton>
|
|
60
|
+
</Tooltip>
|
|
61
|
+
)}
|
|
62
|
+
</Box>
|
|
63
|
+
</Box>
|
|
64
|
+
)
|
|
65
|
+
}
|
|
@@ -42,7 +42,7 @@ const WordContainer = styled(Box, {
|
|
|
42
42
|
'50%': { opacity: 0.5 }
|
|
43
43
|
},
|
|
44
44
|
'&:hover': {
|
|
45
|
-
backgroundColor: '
|
|
45
|
+
backgroundColor: 'rgba(34, 197, 94, 0.35)' // green tint hover for dark mode
|
|
46
46
|
}
|
|
47
47
|
}))
|
|
48
48
|
|
|
@@ -51,7 +51,7 @@ const OriginalWordLabel = styled(Box)({
|
|
|
51
51
|
top: '-14px',
|
|
52
52
|
left: '0',
|
|
53
53
|
fontSize: '0.6rem',
|
|
54
|
-
color: '#
|
|
54
|
+
color: '#888888', // slate-400 for dark mode
|
|
55
55
|
textDecoration: 'line-through',
|
|
56
56
|
opacity: 0.7,
|
|
57
57
|
whiteSpace: 'nowrap',
|
|
@@ -71,10 +71,10 @@ const ActionButton = styled(IconButton)(({ theme }) => ({
|
|
|
71
71
|
minHeight: '20px',
|
|
72
72
|
width: '20px',
|
|
73
73
|
height: '20px',
|
|
74
|
-
backgroundColor: 'rgba(
|
|
75
|
-
border: '1px solid rgba(
|
|
74
|
+
backgroundColor: 'rgba(30, 41, 59, 0.9)', // slate-800 with opacity for dark mode
|
|
75
|
+
border: '1px solid rgba(248, 250, 252, 0.1)', // light border for dark mode
|
|
76
76
|
'&:hover': {
|
|
77
|
-
backgroundColor: 'rgba(
|
|
77
|
+
backgroundColor: 'rgba(51, 65, 85, 1)', // slate-700 for dark mode
|
|
78
78
|
transform: 'scale(1.1)'
|
|
79
79
|
},
|
|
80
80
|
'& .MuiSvgIcon-root': {
|
|
@@ -30,7 +30,7 @@ const SegmentTimeline = styled(Box)({
|
|
|
30
30
|
const TimelineRuler = styled(Box)({
|
|
31
31
|
position: 'relative',
|
|
32
32
|
height: '20px',
|
|
33
|
-
borderBottom: '1px solid #
|
|
33
|
+
borderBottom: '1px solid #2a2a2a', // slate-700 for dark mode
|
|
34
34
|
marginBottom: '4px'
|
|
35
35
|
})
|
|
36
36
|
|
|
@@ -38,14 +38,14 @@ const TimelineMark = styled(Box)({
|
|
|
38
38
|
position: 'absolute',
|
|
39
39
|
width: '1px',
|
|
40
40
|
height: '8px',
|
|
41
|
-
backgroundColor: '#
|
|
41
|
+
backgroundColor: '#666666', // slate-500 for dark mode
|
|
42
42
|
bottom: 0
|
|
43
43
|
})
|
|
44
44
|
|
|
45
45
|
const TimelineLabel = styled(Typography)({
|
|
46
46
|
position: 'absolute',
|
|
47
47
|
fontSize: '0.65rem',
|
|
48
|
-
color: '#
|
|
48
|
+
color: '#888888', // slate-400 for dark mode
|
|
49
49
|
bottom: '10px',
|
|
50
50
|
transform: 'translateX(-50%)',
|
|
51
51
|
whiteSpace: 'nowrap'
|
|
@@ -58,7 +58,7 @@ const WordsBar = styled(Box)({
|
|
|
58
58
|
alignItems: 'stretch',
|
|
59
59
|
minWidth: '100%',
|
|
60
60
|
touchAction: 'pan-y', // Better mobile scrolling
|
|
61
|
-
backgroundColor: '#
|
|
61
|
+
backgroundColor: '#0f0f0f', // slate-900 for dark mode
|
|
62
62
|
borderRadius: '4px',
|
|
63
63
|
marginBottom: '8px'
|
|
64
64
|
})
|
|
@@ -84,7 +84,7 @@ const WordBar = styled(Box, {
|
|
|
84
84
|
? COLORS.corrected
|
|
85
85
|
: isGap
|
|
86
86
|
? COLORS.uncorrectedGap
|
|
87
|
-
: '#
|
|
87
|
+
: '#2a2a2a', // slate-700 for dark mode default
|
|
88
88
|
border: isLong ? '2px solid #f44336' : 'none',
|
|
89
89
|
boxShadow: isLong ? '0 0 4px rgba(244, 67, 54, 0.5)' : 'none',
|
|
90
90
|
'&:hover': {
|
|
@@ -101,13 +101,13 @@ const WordBar = styled(Box, {
|
|
|
101
101
|
|
|
102
102
|
const OriginalWordLabel = styled(Typography)({
|
|
103
103
|
fontSize: '0.65rem',
|
|
104
|
-
color: '#
|
|
104
|
+
color: '#888888', // slate-400 for dark mode
|
|
105
105
|
lineHeight: 1.1,
|
|
106
106
|
marginBottom: '3px',
|
|
107
107
|
textDecoration: 'line-through',
|
|
108
108
|
opacity: 0.85,
|
|
109
109
|
fontWeight: 500,
|
|
110
|
-
backgroundColor: 'rgba(
|
|
110
|
+
backgroundColor: 'rgba(15, 23, 42, 0.8)', // slate-900 with opacity for dark mode
|
|
111
111
|
padding: '1px 3px',
|
|
112
112
|
borderRadius: '2px'
|
|
113
113
|
})
|
|
@@ -226,7 +226,7 @@ export default function DurationTimelineView({
|
|
|
226
226
|
whiteSpace: 'nowrap',
|
|
227
227
|
width: '100%',
|
|
228
228
|
textAlign: 'center',
|
|
229
|
-
color: correction ? '#
|
|
229
|
+
color: correction ? '#4ade80' : '#e5e5e5' // green-400 or slate-50 for dark mode
|
|
230
230
|
}}
|
|
231
231
|
>
|
|
232
232
|
{word.text}
|
|
@@ -235,7 +235,7 @@ export default function DurationTimelineView({
|
|
|
235
235
|
<Typography
|
|
236
236
|
sx={{
|
|
237
237
|
fontSize: '0.6rem',
|
|
238
|
-
color: 'rgba(
|
|
238
|
+
color: 'rgba(248, 250, 252, 0.6)', // slate-50 with opacity for dark mode
|
|
239
239
|
lineHeight: 1,
|
|
240
240
|
marginTop: '3px',
|
|
241
241
|
fontWeight: 600
|
|
@@ -624,7 +624,7 @@ export default function EditModal({
|
|
|
624
624
|
position: 'absolute',
|
|
625
625
|
top: 0,
|
|
626
626
|
left: 0,
|
|
627
|
-
backgroundColor: 'rgba(
|
|
627
|
+
backgroundColor: 'rgba(30, 41, 59, 0.95)', // slate-800 with opacity for dark mode
|
|
628
628
|
zIndex: 10
|
|
629
629
|
}}>
|
|
630
630
|
<CircularProgress size={60} thickness={4} />
|
|
@@ -312,7 +312,7 @@ export default function EditWordList({
|
|
|
312
312
|
width: '8px',
|
|
313
313
|
},
|
|
314
314
|
'&::-webkit-scrollbar-thumb': {
|
|
315
|
-
backgroundColor: 'rgba(
|
|
315
|
+
backgroundColor: 'rgba(248, 250, 252, 0.2)', // slate-50 for dark mode
|
|
316
316
|
borderRadius: '4px',
|
|
317
317
|
},
|
|
318
318
|
scrollbarWidth: 'thin',
|