@trishchuk/coolors-mcp 1.0.0

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.
Files changed (197) hide show
  1. package/.claude/settings.local.json +39 -0
  2. package/.env +2 -0
  3. package/.github/ISSUE_TEMPLATE/bug_report.md +73 -0
  4. package/.github/ISSUE_TEMPLATE/feature_request.md +71 -0
  5. package/.github/pull_request_template.md +97 -0
  6. package/.github/workflows/ci.yml +127 -0
  7. package/.github/workflows/deploy-docs.yml +56 -0
  8. package/.github/workflows/release.yml +99 -0
  9. package/.mcp.json +12 -0
  10. package/.prettierignore +1 -0
  11. package/CLAUDE.md +201 -0
  12. package/DOCUMENTATION.md +274 -0
  13. package/GEMINI.md +54 -0
  14. package/LICENSE +21 -0
  15. package/README.md +401 -0
  16. package/demo/content_based_color.png +0 -0
  17. package/demo/music-player.html +621 -0
  18. package/demo/podcast-player.html +903 -0
  19. package/dist/bin/coolors-mcp.d.ts +1 -0
  20. package/dist/bin/coolors-mcp.js +154 -0
  21. package/dist/bin/coolors-mcp.js.map +1 -0
  22. package/dist/bin/server.d.ts +1 -0
  23. package/dist/bin/server.js +3292 -0
  24. package/dist/bin/server.js.map +1 -0
  25. package/dist/chunk-IQ7NN26V.js +114 -0
  26. package/dist/chunk-IQ7NN26V.js.map +1 -0
  27. package/dist/chunk-P3ARRKLS.js +1214 -0
  28. package/dist/chunk-P3ARRKLS.js.map +1 -0
  29. package/dist/color/index.d.ts +716 -0
  30. package/dist/color/index.js +153 -0
  31. package/dist/color/index.js.map +1 -0
  32. package/dist/coolors-mcp.d.ts +136 -0
  33. package/dist/coolors-mcp.js +7 -0
  34. package/dist/coolors-mcp.js.map +1 -0
  35. package/docs/.vitepress/cache/deps/@braintree_sanitize-url.js +93 -0
  36. package/docs/.vitepress/cache/deps/@braintree_sanitize-url.js.map +7 -0
  37. package/docs/.vitepress/cache/deps/_metadata.json +127 -0
  38. package/docs/.vitepress/cache/deps/chunk-BUSYA2B4.js +9 -0
  39. package/docs/.vitepress/cache/deps/chunk-BUSYA2B4.js.map +7 -0
  40. package/docs/.vitepress/cache/deps/chunk-JD3CXNQ6.js +12683 -0
  41. package/docs/.vitepress/cache/deps/chunk-JD3CXNQ6.js.map +7 -0
  42. package/docs/.vitepress/cache/deps/chunk-SYPOPCWC.js +9719 -0
  43. package/docs/.vitepress/cache/deps/chunk-SYPOPCWC.js.map +7 -0
  44. package/docs/.vitepress/cache/deps/cytoscape-cose-bilkent.js +4710 -0
  45. package/docs/.vitepress/cache/deps/cytoscape-cose-bilkent.js.map +7 -0
  46. package/docs/.vitepress/cache/deps/cytoscape.js +30278 -0
  47. package/docs/.vitepress/cache/deps/cytoscape.js.map +7 -0
  48. package/docs/.vitepress/cache/deps/dayjs.js +285 -0
  49. package/docs/.vitepress/cache/deps/dayjs.js.map +7 -0
  50. package/docs/.vitepress/cache/deps/debug.js +468 -0
  51. package/docs/.vitepress/cache/deps/debug.js.map +7 -0
  52. package/docs/.vitepress/cache/deps/package.json +3 -0
  53. package/docs/.vitepress/cache/deps/prismjs.js +1466 -0
  54. package/docs/.vitepress/cache/deps/prismjs.js.map +7 -0
  55. package/docs/.vitepress/cache/deps/prismjs_components_prism-bash.js +228 -0
  56. package/docs/.vitepress/cache/deps/prismjs_components_prism-bash.js.map +7 -0
  57. package/docs/.vitepress/cache/deps/prismjs_components_prism-javascript.js +142 -0
  58. package/docs/.vitepress/cache/deps/prismjs_components_prism-javascript.js.map +7 -0
  59. package/docs/.vitepress/cache/deps/prismjs_components_prism-json.js +27 -0
  60. package/docs/.vitepress/cache/deps/prismjs_components_prism-json.js.map +7 -0
  61. package/docs/.vitepress/cache/deps/prismjs_components_prism-python.js +65 -0
  62. package/docs/.vitepress/cache/deps/prismjs_components_prism-python.js.map +7 -0
  63. package/docs/.vitepress/cache/deps/prismjs_components_prism-typescript.js +53 -0
  64. package/docs/.vitepress/cache/deps/prismjs_components_prism-typescript.js.map +7 -0
  65. package/docs/.vitepress/cache/deps/prismjs_components_prism-yaml.js +73 -0
  66. package/docs/.vitepress/cache/deps/prismjs_components_prism-yaml.js.map +7 -0
  67. package/docs/.vitepress/cache/deps/vitepress___@vue_devtools-api.js +4507 -0
  68. package/docs/.vitepress/cache/deps/vitepress___@vue_devtools-api.js.map +7 -0
  69. package/docs/.vitepress/cache/deps/vitepress___@vueuse_core.js +584 -0
  70. package/docs/.vitepress/cache/deps/vitepress___@vueuse_core.js.map +7 -0
  71. package/docs/.vitepress/cache/deps/vitepress___@vueuse_integrations_useFocusTrap.js +1146 -0
  72. package/docs/.vitepress/cache/deps/vitepress___@vueuse_integrations_useFocusTrap.js.map +7 -0
  73. package/docs/.vitepress/cache/deps/vitepress___mark__js_src_vanilla__js.js +1667 -0
  74. package/docs/.vitepress/cache/deps/vitepress___mark__js_src_vanilla__js.js.map +7 -0
  75. package/docs/.vitepress/cache/deps/vitepress___minisearch.js +1814 -0
  76. package/docs/.vitepress/cache/deps/vitepress___minisearch.js.map +7 -0
  77. package/docs/.vitepress/cache/deps/vue.js +344 -0
  78. package/docs/.vitepress/cache/deps/vue.js.map +7 -0
  79. package/docs/.vitepress/components/ClientGrid.vue +125 -0
  80. package/docs/.vitepress/components/CodeBlock.vue +231 -0
  81. package/docs/.vitepress/components/ConfigModal.vue +477 -0
  82. package/docs/.vitepress/components/DiagramModal.vue +528 -0
  83. package/docs/.vitepress/components/TroubleshootingModal.vue +472 -0
  84. package/docs/.vitepress/config.js +162 -0
  85. package/docs/.vitepress/theme/FundingLayout.vue +251 -0
  86. package/docs/.vitepress/theme/Layout.vue +134 -0
  87. package/docs/.vitepress/theme/components/AdBanner.vue +317 -0
  88. package/docs/.vitepress/theme/components/AdPlaceholder.vue +78 -0
  89. package/docs/.vitepress/theme/components/FundingEffects.vue +322 -0
  90. package/docs/.vitepress/theme/components/FundingHero.vue +345 -0
  91. package/docs/.vitepress/theme/components/SupportSection.vue +511 -0
  92. package/docs/.vitepress/theme/custom-app.css +339 -0
  93. package/docs/.vitepress/theme/custom.css +699 -0
  94. package/docs/.vitepress/theme/index.js +25 -0
  95. package/docs/README.md +198 -0
  96. package/docs/concepts/accessibility.md +473 -0
  97. package/docs/concepts/color-spaces.md +222 -0
  98. package/docs/concepts/distance-metrics.md +384 -0
  99. package/docs/concepts/hct.md +261 -0
  100. package/docs/concepts/image-analysis.md +396 -0
  101. package/docs/concepts/material-design.md +306 -0
  102. package/docs/concepts/theme-matching.md +399 -0
  103. package/docs/examples/basic-colors.md +490 -0
  104. package/docs/examples/creating-themes.md +898 -0
  105. package/docs/examples/css-refactoring.md +824 -0
  106. package/docs/examples/image-extraction.md +882 -0
  107. package/docs/getting-started.md +366 -0
  108. package/docs/index.md +190 -0
  109. package/docs/installation.md +157 -0
  110. package/docs/tools/README.md +234 -0
  111. package/docs/tools/accessibility.md +614 -0
  112. package/docs/tools/color-operations.md +374 -0
  113. package/docs/tools/image-extraction.md +624 -0
  114. package/docs/tools/material-design.md +347 -0
  115. package/docs/tools/theme-matching.md +552 -0
  116. package/eslint.config.ts +14 -0
  117. package/examples/theme-matching.md +113 -0
  118. package/jsr.json +7 -0
  119. package/mcp-config.json +8 -0
  120. package/note.md +35 -0
  121. package/package.json +122 -0
  122. package/research_results.md +53 -0
  123. package/src/bin/coolors-mcp.ts +194 -0
  124. package/src/bin/server.ts +61 -0
  125. package/src/color/__tests__/conversions-argb.test.ts +198 -0
  126. package/src/color/__tests__/extract-colors.test.ts +360 -0
  127. package/src/color/__tests__/image-utils.test.ts +242 -0
  128. package/src/color/__tests__/reference-colors.test.ts +278 -0
  129. package/src/color/__tests__/round-trip.test.ts +197 -0
  130. package/src/color/conversions.test.ts +402 -0
  131. package/src/color/conversions.ts +393 -0
  132. package/src/color/dislike/__tests__/dislike-analyzer.test.ts +248 -0
  133. package/src/color/dislike/dislike-analyzer.ts +114 -0
  134. package/src/color/extract-colors.ts +228 -0
  135. package/src/color/hct/__tests__/hct-class.test.ts +232 -0
  136. package/src/color/hct/harmonization.ts +204 -0
  137. package/src/color/hct/hct-class.ts +109 -0
  138. package/src/color/hct/hct-solver.ts +168 -0
  139. package/src/color/hct/index.ts +39 -0
  140. package/src/color/hct/tonal-palette.ts +211 -0
  141. package/src/color/hct/types.ts +88 -0
  142. package/src/color/image-utils.ts +79 -0
  143. package/src/color/index.ts +87 -0
  144. package/src/color/material-theme.ts +157 -0
  145. package/src/color/metrics.test.ts +276 -0
  146. package/src/color/metrics.ts +281 -0
  147. package/src/color/quantize/__tests__/quantizer_celebi.test.ts +195 -0
  148. package/src/color/quantize/lab_point_provider.ts +55 -0
  149. package/src/color/quantize/point_provider.ts +27 -0
  150. package/src/color/quantize/quantizer_celebi.ts +51 -0
  151. package/src/color/quantize/quantizer_celebi_test.ts +71 -0
  152. package/src/color/quantize/quantizer_map.ts +47 -0
  153. package/src/color/quantize/quantizer_wsmeans.ts +232 -0
  154. package/src/color/quantize/quantizer_wu.ts +472 -0
  155. package/src/color/score/__tests__/score.test.ts +224 -0
  156. package/src/color/score/score.ts +175 -0
  157. package/src/color/types.ts +151 -0
  158. package/src/color/utils/color_utils.ts +292 -0
  159. package/src/color/utils/math_utils.ts +145 -0
  160. package/src/color/utils.test.ts +403 -0
  161. package/src/color/utils.ts +315 -0
  162. package/src/constants.ts +5 -0
  163. package/src/coolors-mcp.ts +37 -0
  164. package/src/examples/addition.ts +333 -0
  165. package/src/examples/color-demo.ts +125 -0
  166. package/src/examples/custom-logger.ts +201 -0
  167. package/src/examples/oauth-server.ts +113 -0
  168. package/src/examples/session-context.ts +269 -0
  169. package/src/session.ts +116 -0
  170. package/src/theme/__tests__/matcher.test.ts +180 -0
  171. package/src/theme/__tests__/parser.test.ts +148 -0
  172. package/src/theme/__tests__/refactor.test.ts +224 -0
  173. package/src/theme/index.ts +34 -0
  174. package/src/theme/matcher.ts +395 -0
  175. package/src/theme/parser.ts +392 -0
  176. package/src/theme/refactor.ts +360 -0
  177. package/src/theme/types.ts +152 -0
  178. package/src/tools/__tests__/gradient-generator.test.ts +206 -0
  179. package/src/tools/__tests__/palette-with-locks.test.ts +109 -0
  180. package/src/tools/color-conversion.tool.ts +54 -0
  181. package/src/tools/color-distance.tool.ts +41 -0
  182. package/src/tools/colors.ts +31 -0
  183. package/src/tools/contrast-checker.tool.ts +37 -0
  184. package/src/tools/dislike-analyzer.tool.ts +247 -0
  185. package/src/tools/gradient-generator.tool.ts +250 -0
  186. package/src/tools/image-extraction.tools.ts +289 -0
  187. package/src/tools/index.ts +39 -0
  188. package/src/tools/material-theme.tools.ts +250 -0
  189. package/src/tools/palette-generator.tool.ts +135 -0
  190. package/src/tools/palette-with-locks.tool.ts +221 -0
  191. package/src/tools/registry.ts +142 -0
  192. package/src/tools/simple-tools.ts +37 -0
  193. package/src/tools/theme-matching.tools.ts +334 -0
  194. package/src/types.ts +182 -0
  195. package/src/utils.ts +22 -0
  196. package/tsconfig.json +8 -0
  197. package/vitest.config.js +15 -0
@@ -0,0 +1,903 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>Podcast Player with Chapters - Content-Based Colors</title>
7
+ <style>
8
+ :root {
9
+ /* Material Design 3 - Generated from podcast cover */
10
+ --md-sys-color-primary: #7c4dff;
11
+ --md-sys-color-on-primary: #ffffff;
12
+ --md-sys-color-primary-container: #e1d5ff;
13
+ --md-sys-color-on-primary-container: #21005d;
14
+
15
+ --md-sys-color-secondary: #625b71;
16
+ --md-sys-color-on-secondary: #ffffff;
17
+ --md-sys-color-secondary-container: #e8def8;
18
+ --md-sys-color-on-secondary-container: #1d192b;
19
+
20
+ --md-sys-color-tertiary: #7d5260;
21
+ --md-sys-color-on-tertiary: #ffffff;
22
+ --md-sys-color-tertiary-container: #ffd8e4;
23
+ --md-sys-color-on-tertiary-container: #31111d;
24
+
25
+ --md-sys-color-background: #fdfbff;
26
+ --md-sys-color-on-background: #1c1b1f;
27
+ --md-sys-color-surface: #fdfbff;
28
+ --md-sys-color-on-surface: #1c1b1f;
29
+ --md-sys-color-surface-variant: #e7e0ec;
30
+ --md-sys-color-on-surface-variant: #49454f;
31
+
32
+ --md-sys-color-outline: #79747e;
33
+ --md-sys-color-outline-variant: #cac4d0;
34
+
35
+ --md-sys-color-surface-container: #f4eff7;
36
+ --md-sys-color-surface-container-high: #ece7f0;
37
+ --md-sys-color-surface-container-highest: #e7e0ec;
38
+ }
39
+
40
+ @media (prefers-color-scheme: dark) {
41
+ :root {
42
+ --md-sys-color-primary: #c9a8ff;
43
+ --md-sys-color-on-primary: #370093;
44
+ --md-sys-color-primary-container: #5029ca;
45
+ --md-sys-color-on-primary-container: #e1d5ff;
46
+
47
+ --md-sys-color-secondary: #ccc2dc;
48
+ --md-sys-color-on-secondary: #332d41;
49
+ --md-sys-color-secondary-container: #4a4459;
50
+ --md-sys-color-on-secondary-container: #e8def8;
51
+
52
+ --md-sys-color-tertiary: #efb8c8;
53
+ --md-sys-color-on-tertiary: #492532;
54
+ --md-sys-color-tertiary-container: #633b48;
55
+ --md-sys-color-on-tertiary-container: #ffd8e4;
56
+
57
+ --md-sys-color-background: #141318;
58
+ --md-sys-color-on-background: #e6e1e5;
59
+ --md-sys-color-surface: #1c1b1f;
60
+ --md-sys-color-on-surface: #e6e1e5;
61
+ --md-sys-color-surface-variant: #49454f;
62
+ --md-sys-color-on-surface-variant: #cac4d0;
63
+
64
+ --md-sys-color-outline: #938f99;
65
+ --md-sys-color-outline-variant: #49454f;
66
+
67
+ --md-sys-color-surface-container: #211f26;
68
+ --md-sys-color-surface-container-high: #2b2930;
69
+ --md-sys-color-surface-container-highest: #36343b;
70
+ }
71
+ }
72
+
73
+ * {
74
+ margin: 0;
75
+ padding: 0;
76
+ box-sizing: border-box;
77
+ }
78
+
79
+ body {
80
+ font-family:
81
+ -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu,
82
+ sans-serif;
83
+ background: var(--md-sys-color-background);
84
+ color: var(--md-sys-color-on-background);
85
+ min-height: 100vh;
86
+ display: flex;
87
+ justify-content: center;
88
+ align-items: center;
89
+ padding: 20px;
90
+ background: linear-gradient(
91
+ 135deg,
92
+ var(--md-sys-color-primary-container) 0%,
93
+ var(--md-sys-color-secondary-container) 50%,
94
+ var(--md-sys-color-tertiary-container) 100%
95
+ );
96
+ }
97
+
98
+ .container {
99
+ width: 100%;
100
+ max-width: 480px;
101
+ background: var(--md-sys-color-surface);
102
+ border-radius: 28px;
103
+ box-shadow:
104
+ 0 10px 40px rgba(0, 0, 0, 0.2),
105
+ 0 0 0 1px var(--md-sys-color-outline-variant);
106
+ overflow: hidden;
107
+ }
108
+
109
+ .player-section {
110
+ padding: 24px;
111
+ background: var(--md-sys-color-surface);
112
+ border-bottom: 1px solid var(--md-sys-color-outline-variant);
113
+ }
114
+
115
+ .podcast-header {
116
+ display: flex;
117
+ gap: 16px;
118
+ margin-bottom: 24px;
119
+ }
120
+
121
+ .podcast-cover {
122
+ width: 120px;
123
+ height: 120px;
124
+ border-radius: 16px;
125
+ background: linear-gradient(
126
+ 135deg,
127
+ var(--md-sys-color-primary),
128
+ var(--md-sys-color-secondary)
129
+ );
130
+ display: flex;
131
+ align-items: center;
132
+ justify-content: center;
133
+ font-size: 48px;
134
+ color: var(--md-sys-color-on-primary);
135
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
136
+ position: relative;
137
+ overflow: hidden;
138
+ }
139
+
140
+ .cover-pattern {
141
+ position: absolute;
142
+ inset: 0;
143
+ opacity: 0.3;
144
+ background-image:
145
+ repeating-linear-gradient(
146
+ 45deg,
147
+ transparent,
148
+ transparent 10px,
149
+ rgba(255, 255, 255, 0.1) 10px,
150
+ rgba(255, 255, 255, 0.1) 20px
151
+ ),
152
+ repeating-linear-gradient(
153
+ -45deg,
154
+ transparent,
155
+ transparent 10px,
156
+ rgba(0, 0, 0, 0.1) 10px,
157
+ rgba(0, 0, 0, 0.1) 20px
158
+ );
159
+ }
160
+
161
+ .podcast-info {
162
+ flex: 1;
163
+ display: flex;
164
+ flex-direction: column;
165
+ justify-content: center;
166
+ }
167
+
168
+ .podcast-title {
169
+ font-size: 20px;
170
+ font-weight: 600;
171
+ color: var(--md-sys-color-on-surface);
172
+ margin-bottom: 4px;
173
+ }
174
+
175
+ .podcast-author {
176
+ font-size: 14px;
177
+ color: var(--md-sys-color-on-surface-variant);
178
+ margin-bottom: 8px;
179
+ }
180
+
181
+ .episode-title {
182
+ font-size: 16px;
183
+ color: var(--md-sys-color-primary);
184
+ font-weight: 500;
185
+ }
186
+
187
+ .waveform-container {
188
+ height: 60px;
189
+ margin-bottom: 16px;
190
+ background: var(--md-sys-color-surface-container);
191
+ border-radius: 8px;
192
+ padding: 8px;
193
+ position: relative;
194
+ overflow: hidden;
195
+ }
196
+
197
+ .waveform {
198
+ display: flex;
199
+ align-items: center;
200
+ height: 100%;
201
+ gap: 2px;
202
+ }
203
+
204
+ .wave-bar {
205
+ flex: 1;
206
+ background: var(--md-sys-color-primary);
207
+ opacity: 0.3;
208
+ border-radius: 2px;
209
+ transition: opacity 0.3s;
210
+ }
211
+
212
+ .wave-bar.active {
213
+ opacity: 1;
214
+ background: var(--md-sys-color-primary);
215
+ }
216
+
217
+ .wave-bar.played {
218
+ opacity: 0.6;
219
+ background: var(--md-sys-color-secondary);
220
+ }
221
+
222
+ .progress-time {
223
+ display: flex;
224
+ justify-content: space-between;
225
+ font-size: 12px;
226
+ color: var(--md-sys-color-on-surface-variant);
227
+ margin-bottom: 20px;
228
+ }
229
+
230
+ .controls {
231
+ display: flex;
232
+ align-items: center;
233
+ justify-content: center;
234
+ gap: 16px;
235
+ margin-bottom: 20px;
236
+ }
237
+
238
+ .control-btn {
239
+ background: transparent;
240
+ border: none;
241
+ color: var(--md-sys-color-on-surface);
242
+ cursor: pointer;
243
+ display: flex;
244
+ align-items: center;
245
+ justify-content: center;
246
+ transition: all 0.2s;
247
+ border-radius: 50%;
248
+ }
249
+
250
+ .control-btn:hover {
251
+ background: var(--md-sys-color-surface-variant);
252
+ }
253
+
254
+ .control-btn.small {
255
+ width: 40px;
256
+ height: 40px;
257
+ }
258
+
259
+ .control-btn.primary {
260
+ width: 56px;
261
+ height: 56px;
262
+ background: var(--md-sys-color-primary);
263
+ color: var(--md-sys-color-on-primary);
264
+ box-shadow: 0 4px 12px rgba(124, 77, 255, 0.3);
265
+ }
266
+
267
+ .control-btn.primary:hover {
268
+ transform: scale(1.05);
269
+ box-shadow: 0 6px 16px rgba(124, 77, 255, 0.4);
270
+ }
271
+
272
+ .speed-controls {
273
+ display: flex;
274
+ align-items: center;
275
+ justify-content: center;
276
+ gap: 12px;
277
+ }
278
+
279
+ .speed-btn {
280
+ padding: 6px 12px;
281
+ background: var(--md-sys-color-surface-container);
282
+ color: var(--md-sys-color-on-surface);
283
+ border: none;
284
+ border-radius: 8px;
285
+ font-size: 13px;
286
+ cursor: pointer;
287
+ transition: all 0.2s;
288
+ }
289
+
290
+ .speed-btn.active {
291
+ background: var(--md-sys-color-primary);
292
+ color: var(--md-sys-color-on-primary);
293
+ }
294
+
295
+ .speed-btn:hover {
296
+ background: var(--md-sys-color-surface-container-high);
297
+ }
298
+
299
+ .speed-btn.active:hover {
300
+ background: var(--md-sys-color-primary);
301
+ }
302
+
303
+ .chapters-section {
304
+ padding: 20px;
305
+ background: var(--md-sys-color-surface);
306
+ max-height: 400px;
307
+ overflow-y: auto;
308
+ }
309
+
310
+ .section-title {
311
+ font-size: 18px;
312
+ font-weight: 600;
313
+ color: var(--md-sys-color-on-surface);
314
+ margin-bottom: 16px;
315
+ display: flex;
316
+ align-items: center;
317
+ gap: 8px;
318
+ }
319
+
320
+ .chapter-list {
321
+ display: flex;
322
+ flex-direction: column;
323
+ gap: 2px;
324
+ }
325
+
326
+ .chapter-item {
327
+ display: flex;
328
+ align-items: center;
329
+ padding: 12px;
330
+ background: var(--md-sys-color-surface);
331
+ border-radius: 12px;
332
+ cursor: pointer;
333
+ transition: all 0.2s;
334
+ border: 1px solid transparent;
335
+ }
336
+
337
+ .chapter-item:hover {
338
+ background: var(--md-sys-color-surface-container);
339
+ }
340
+
341
+ .chapter-item.active {
342
+ background: var(--md-sys-color-primary-container);
343
+ border-color: var(--md-sys-color-primary);
344
+ }
345
+
346
+ .chapter-item.played {
347
+ opacity: 0.7;
348
+ }
349
+
350
+ .chapter-number {
351
+ width: 32px;
352
+ height: 32px;
353
+ border-radius: 8px;
354
+ background: var(--md-sys-color-surface-variant);
355
+ color: var(--md-sys-color-on-surface-variant);
356
+ display: flex;
357
+ align-items: center;
358
+ justify-content: center;
359
+ font-size: 14px;
360
+ font-weight: 600;
361
+ margin-right: 12px;
362
+ }
363
+
364
+ .chapter-item.active .chapter-number {
365
+ background: var(--md-sys-color-primary);
366
+ color: var(--md-sys-color-on-primary);
367
+ }
368
+
369
+ .chapter-info {
370
+ flex: 1;
371
+ }
372
+
373
+ .chapter-title {
374
+ font-size: 15px;
375
+ font-weight: 500;
376
+ color: var(--md-sys-color-on-surface);
377
+ margin-bottom: 2px;
378
+ }
379
+
380
+ .chapter-description {
381
+ font-size: 13px;
382
+ color: var(--md-sys-color-on-surface-variant);
383
+ display: -webkit-box;
384
+ -webkit-line-clamp: 1;
385
+ -webkit-box-orient: vertical;
386
+ overflow: hidden;
387
+ }
388
+
389
+ .chapter-time {
390
+ display: flex;
391
+ flex-direction: column;
392
+ align-items: flex-end;
393
+ gap: 2px;
394
+ }
395
+
396
+ .chapter-timestamp {
397
+ font-size: 13px;
398
+ color: var(--md-sys-color-primary);
399
+ font-weight: 500;
400
+ }
401
+
402
+ .chapter-duration {
403
+ font-size: 11px;
404
+ color: var(--md-sys-color-on-surface-variant);
405
+ }
406
+
407
+ .icon {
408
+ width: 24px;
409
+ height: 24px;
410
+ fill: currentColor;
411
+ }
412
+
413
+ .icon.large {
414
+ width: 32px;
415
+ height: 32px;
416
+ }
417
+
418
+ /* Custom scrollbar */
419
+ .chapters-section::-webkit-scrollbar {
420
+ width: 6px;
421
+ }
422
+
423
+ .chapters-section::-webkit-scrollbar-track {
424
+ background: var(--md-sys-color-surface-container);
425
+ border-radius: 3px;
426
+ }
427
+
428
+ .chapters-section::-webkit-scrollbar-thumb {
429
+ background: var(--md-sys-color-outline);
430
+ border-radius: 3px;
431
+ }
432
+
433
+ .chapters-section::-webkit-scrollbar-thumb:hover {
434
+ background: var(--md-sys-color-primary);
435
+ }
436
+
437
+ /* Playing animation */
438
+ @keyframes pulse {
439
+ 0%,
440
+ 100% {
441
+ transform: scaleY(0.3);
442
+ }
443
+ 50% {
444
+ transform: scaleY(1);
445
+ }
446
+ }
447
+
448
+ .playing-indicator {
449
+ display: none;
450
+ gap: 2px;
451
+ height: 16px;
452
+ align-items: flex-end;
453
+ }
454
+
455
+ .chapter-item.active .playing-indicator {
456
+ display: flex;
457
+ }
458
+
459
+ .playing-bar {
460
+ width: 3px;
461
+ height: 100%;
462
+ background: var(--md-sys-color-primary);
463
+ animation: pulse 1s ease-in-out infinite;
464
+ }
465
+
466
+ .playing-bar:nth-child(2) {
467
+ animation-delay: 0.2s;
468
+ }
469
+
470
+ .playing-bar:nth-child(3) {
471
+ animation-delay: 0.4s;
472
+ }
473
+
474
+ /* Theme switcher */
475
+ .theme-fab {
476
+ position: fixed;
477
+ bottom: 24px;
478
+ right: 24px;
479
+ width: 56px;
480
+ height: 56px;
481
+ border-radius: 16px;
482
+ background: var(--md-sys-color-primary);
483
+ color: var(--md-sys-color-on-primary);
484
+ border: none;
485
+ cursor: pointer;
486
+ display: flex;
487
+ align-items: center;
488
+ justify-content: center;
489
+ box-shadow: 0 6px 20px rgba(0, 0, 0, 0.2);
490
+ transition: all 0.3s;
491
+ }
492
+
493
+ .theme-fab:hover {
494
+ transform: scale(1.05);
495
+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.3);
496
+ }
497
+ </style>
498
+ </head>
499
+ <body>
500
+ <div class="container">
501
+ <div class="player-section">
502
+ <div class="podcast-header">
503
+ <div class="podcast-cover">
504
+ <div class="cover-pattern"></div>
505
+ <span>🎙️</span>
506
+ </div>
507
+ <div class="podcast-info">
508
+ <div class="podcast-title">Tech Talks Daily</div>
509
+ <div class="podcast-author">by Sarah Chen</div>
510
+ <div class="episode-title">Episode 42: The Future of AI</div>
511
+ </div>
512
+ </div>
513
+
514
+ <div class="waveform-container">
515
+ <div class="waveform" id="waveform"></div>
516
+ </div>
517
+
518
+ <div class="progress-time">
519
+ <span id="currentTime">15:32</span>
520
+ <span id="totalTime">45:00</span>
521
+ </div>
522
+
523
+ <div class="controls">
524
+ <button class="control-btn small" onclick="skipBack()">
525
+ <svg class="icon" viewBox="0 0 24 24">
526
+ <path d="M11 18V6l-8.5 6 8.5 6zm.5-6l8.5 6V6l-8.5 6z" />
527
+ </svg>
528
+ </button>
529
+
530
+ <button class="control-btn small" onclick="rewind()">
531
+ <svg class="icon" viewBox="0 0 24 24">
532
+ <text
533
+ x="50%"
534
+ y="50%"
535
+ text-anchor="middle"
536
+ dy=".3em"
537
+ font-size="12"
538
+ font-weight="bold"
539
+ >
540
+ -15
541
+ </text>
542
+ </svg>
543
+ </button>
544
+
545
+ <button
546
+ class="control-btn primary"
547
+ onclick="togglePlay()"
548
+ id="playBtn"
549
+ >
550
+ <svg class="icon large" viewBox="0 0 24 24">
551
+ <path d="M8 5v14l11-7z" />
552
+ </svg>
553
+ </button>
554
+
555
+ <button class="control-btn small" onclick="forward()">
556
+ <svg class="icon" viewBox="0 0 24 24">
557
+ <text
558
+ x="50%"
559
+ y="50%"
560
+ text-anchor="middle"
561
+ dy=".3em"
562
+ font-size="12"
563
+ font-weight="bold"
564
+ >
565
+ +30
566
+ </text>
567
+ </svg>
568
+ </button>
569
+
570
+ <button class="control-btn small" onclick="skipForward()">
571
+ <svg class="icon" viewBox="0 0 24 24">
572
+ <path d="M4 18l8.5-6L4 6v12zm9-12v12l8.5-6L13 6z" />
573
+ </svg>
574
+ </button>
575
+ </div>
576
+
577
+ <div class="speed-controls">
578
+ <button class="speed-btn" onclick="setSpeed(0.75)">0.75x</button>
579
+ <button class="speed-btn active" onclick="setSpeed(1)">1x</button>
580
+ <button class="speed-btn" onclick="setSpeed(1.25)">1.25x</button>
581
+ <button class="speed-btn" onclick="setSpeed(1.5)">1.5x</button>
582
+ <button class="speed-btn" onclick="setSpeed(2)">2x</button>
583
+ </div>
584
+ </div>
585
+
586
+ <div class="chapters-section">
587
+ <div class="section-title">
588
+ <svg class="icon" viewBox="0 0 24 24">
589
+ <path
590
+ d="M3 9h14V7H3v2zm0 4h14v-2H3v2zm0 4h14v-2H3v2zm16 0h2v-2h-2v2zm0-10v2h2V7h-2zm0 6h2v-2h-2v2z"
591
+ />
592
+ </svg>
593
+ Chapters
594
+ </div>
595
+
596
+ <div class="chapter-list">
597
+ <div class="chapter-item played" onclick="playChapter(0)">
598
+ <div class="chapter-number">1</div>
599
+ <div class="chapter-info">
600
+ <div class="chapter-title">Introduction</div>
601
+ <div class="chapter-description">
602
+ Welcome to today's episode about AI
603
+ </div>
604
+ </div>
605
+ <div class="chapter-time">
606
+ <span class="chapter-timestamp">00:00</span>
607
+ <span class="chapter-duration">2:15</span>
608
+ </div>
609
+ </div>
610
+
611
+ <div class="chapter-item played" onclick="playChapter(1)">
612
+ <div class="chapter-number">2</div>
613
+ <div class="chapter-info">
614
+ <div class="chapter-title">Guest Introduction</div>
615
+ <div class="chapter-description">
616
+ Meet Dr. Alex Johnson from OpenAI
617
+ </div>
618
+ </div>
619
+ <div class="chapter-time">
620
+ <span class="chapter-timestamp">02:15</span>
621
+ <span class="chapter-duration">3:45</span>
622
+ </div>
623
+ </div>
624
+
625
+ <div class="chapter-item played" onclick="playChapter(2)">
626
+ <div class="chapter-number">3</div>
627
+ <div class="chapter-info">
628
+ <div class="chapter-title">Current State of AI</div>
629
+ <div class="chapter-description">
630
+ Overview of where we are today with artificial intelligence
631
+ </div>
632
+ </div>
633
+ <div class="chapter-time">
634
+ <span class="chapter-timestamp">06:00</span>
635
+ <span class="chapter-duration">8:30</span>
636
+ </div>
637
+ </div>
638
+
639
+ <div class="chapter-item active" onclick="playChapter(3)">
640
+ <div class="chapter-number">
641
+ <div class="playing-indicator">
642
+ <div class="playing-bar"></div>
643
+ <div class="playing-bar"></div>
644
+ <div class="playing-bar"></div>
645
+ </div>
646
+ </div>
647
+ <div class="chapter-info">
648
+ <div class="chapter-title">Large Language Models</div>
649
+ <div class="chapter-description">
650
+ Deep dive into GPT and similar models
651
+ </div>
652
+ </div>
653
+ <div class="chapter-time">
654
+ <span class="chapter-timestamp">14:30</span>
655
+ <span class="chapter-duration">10:15</span>
656
+ </div>
657
+ </div>
658
+
659
+ <div class="chapter-item" onclick="playChapter(4)">
660
+ <div class="chapter-number">5</div>
661
+ <div class="chapter-info">
662
+ <div class="chapter-title">Ethical Considerations</div>
663
+ <div class="chapter-description">
664
+ Discussing the importance of responsible AI development
665
+ </div>
666
+ </div>
667
+ <div class="chapter-time">
668
+ <span class="chapter-timestamp">24:45</span>
669
+ <span class="chapter-duration">7:20</span>
670
+ </div>
671
+ </div>
672
+
673
+ <div class="chapter-item" onclick="playChapter(5)">
674
+ <div class="chapter-number">6</div>
675
+ <div class="chapter-info">
676
+ <div class="chapter-title">Future Predictions</div>
677
+ <div class="chapter-description">
678
+ What might the next 5 years look like?
679
+ </div>
680
+ </div>
681
+ <div class="chapter-time">
682
+ <span class="chapter-timestamp">32:05</span>
683
+ <span class="chapter-duration">6:40</span>
684
+ </div>
685
+ </div>
686
+
687
+ <div class="chapter-item" onclick="playChapter(6)">
688
+ <div class="chapter-number">7</div>
689
+ <div class="chapter-info">
690
+ <div class="chapter-title">Q&A Session</div>
691
+ <div class="chapter-description">
692
+ Answering listener questions
693
+ </div>
694
+ </div>
695
+ <div class="chapter-time">
696
+ <span class="chapter-timestamp">38:45</span>
697
+ <span class="chapter-duration">4:30</span>
698
+ </div>
699
+ </div>
700
+
701
+ <div class="chapter-item" onclick="playChapter(7)">
702
+ <div class="chapter-number">8</div>
703
+ <div class="chapter-info">
704
+ <div class="chapter-title">Closing Thoughts</div>
705
+ <div class="chapter-description">
706
+ Wrapping up and next episode preview
707
+ </div>
708
+ </div>
709
+ <div class="chapter-time">
710
+ <span class="chapter-timestamp">43:15</span>
711
+ <span class="chapter-duration">1:45</span>
712
+ </div>
713
+ </div>
714
+ </div>
715
+ </div>
716
+ </div>
717
+
718
+ <button class="theme-fab" onclick="changeTheme()">
719
+ <svg class="icon" viewBox="0 0 24 24">
720
+ <path
721
+ d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"
722
+ />
723
+ </svg>
724
+ </button>
725
+
726
+ <script>
727
+ // Generate waveform bars
728
+ const waveform = document.getElementById("waveform");
729
+ for (let i = 0; i < 50; i++) {
730
+ const bar = document.createElement("div");
731
+ bar.className = "wave-bar";
732
+ bar.style.height = Math.random() * 80 + 20 + "%";
733
+
734
+ // Simulate played progress
735
+ if (i < 17) {
736
+ bar.classList.add("played");
737
+ } else if (i === 17) {
738
+ bar.classList.add("active");
739
+ }
740
+
741
+ waveform.appendChild(bar);
742
+ }
743
+
744
+ let isPlaying = false;
745
+ let currentSpeed = 1;
746
+ let currentChapter = 3;
747
+
748
+ function togglePlay() {
749
+ isPlaying = !isPlaying;
750
+ const playBtn = document.getElementById("playBtn");
751
+ if (isPlaying) {
752
+ playBtn.innerHTML =
753
+ '<svg class="icon large" viewBox="0 0 24 24"><path d="M6 19h4V5H6v14zm8-14v14h4V5h-4z"/></svg>';
754
+ } else {
755
+ playBtn.innerHTML =
756
+ '<svg class="icon large" viewBox="0 0 24 24"><path d="M8 5v14l11-7z"/></svg>';
757
+ }
758
+ }
759
+
760
+ function setSpeed(speed) {
761
+ currentSpeed = speed;
762
+ document.querySelectorAll(".speed-btn").forEach((btn) => {
763
+ btn.classList.remove("active");
764
+ });
765
+ event.target.classList.add("active");
766
+ }
767
+
768
+ function playChapter(index) {
769
+ currentChapter = index;
770
+ document.querySelectorAll(".chapter-item").forEach((item, i) => {
771
+ item.classList.remove("active");
772
+ if (i < index) {
773
+ item.classList.add("played");
774
+ } else {
775
+ item.classList.remove("played");
776
+ }
777
+ });
778
+ document
779
+ .querySelectorAll(".chapter-item")
780
+ [index].classList.add("active");
781
+
782
+ // Update waveform
783
+ updateWaveform(index);
784
+ }
785
+
786
+ function updateWaveform(chapterIndex) {
787
+ const bars = document.querySelectorAll(".wave-bar");
788
+ const progress = (chapterIndex + 0.5) / 8; // 8 chapters total
789
+ const activeBar = Math.floor(progress * bars.length);
790
+
791
+ bars.forEach((bar, i) => {
792
+ bar.classList.remove("played", "active");
793
+ if (i < activeBar) {
794
+ bar.classList.add("played");
795
+ } else if (i === activeBar) {
796
+ bar.classList.add("active");
797
+ }
798
+ });
799
+ }
800
+
801
+ function skipBack() {
802
+ if (currentChapter > 0) {
803
+ playChapter(currentChapter - 1);
804
+ }
805
+ }
806
+
807
+ function skipForward() {
808
+ if (currentChapter < 7) {
809
+ playChapter(currentChapter + 1);
810
+ }
811
+ }
812
+
813
+ function rewind() {
814
+ // Simulate 15 second rewind
815
+ console.log("Rewind 15 seconds");
816
+ }
817
+
818
+ function forward() {
819
+ // Simulate 30 second forward
820
+ console.log("Forward 30 seconds");
821
+ }
822
+
823
+ // Theme variations
824
+ const themes = [
825
+ {
826
+ name: "Purple Dream",
827
+ primary: "#7c4dff",
828
+ secondary: "#625b71",
829
+ tertiary: "#7d5260",
830
+ primaryContainer: "#e1d5ff",
831
+ secondaryContainer: "#e8def8",
832
+ tertiaryContainer: "#ffd8e4",
833
+ },
834
+ {
835
+ name: "Ocean Breeze",
836
+ primary: "#0061a4",
837
+ secondary: "#535f70",
838
+ tertiary: "#6f5575",
839
+ primaryContainer: "#d1e4ff",
840
+ secondaryContainer: "#d9e3f0",
841
+ tertiaryContainer: "#f8d8ff",
842
+ },
843
+ {
844
+ name: "Forest",
845
+ primary: "#006e1c",
846
+ secondary: "#52634f",
847
+ tertiary: "#386666",
848
+ primaryContainer: "#b6f397",
849
+ secondaryContainer: "#d6e8cd",
850
+ tertiaryContainer: "#bbebeb",
851
+ },
852
+ {
853
+ name: "Sunset",
854
+ primary: "#bc004b",
855
+ secondary: "#775653",
856
+ tertiary: "#775a00",
857
+ primaryContainer: "#ffd9dc",
858
+ secondaryContainer: "#ffdad7",
859
+ tertiaryContainer: "#ffdeaa",
860
+ },
861
+ ];
862
+
863
+ let currentTheme = 0;
864
+
865
+ function changeTheme() {
866
+ currentTheme = (currentTheme + 1) % themes.length;
867
+ const theme = themes[currentTheme];
868
+ const root = document.documentElement;
869
+
870
+ root.style.setProperty("--md-sys-color-primary", theme.primary);
871
+ root.style.setProperty("--md-sys-color-secondary", theme.secondary);
872
+ root.style.setProperty("--md-sys-color-tertiary", theme.tertiary);
873
+ root.style.setProperty(
874
+ "--md-sys-color-primary-container",
875
+ theme.primaryContainer,
876
+ );
877
+ root.style.setProperty(
878
+ "--md-sys-color-secondary-container",
879
+ theme.secondaryContainer,
880
+ );
881
+ root.style.setProperty(
882
+ "--md-sys-color-tertiary-container",
883
+ theme.tertiaryContainer,
884
+ );
885
+
886
+ // Animate the change
887
+ root.style.transition = "all 0.3s ease";
888
+ }
889
+
890
+ // Simulate time updates
891
+ let seconds = 932; // 15:32
892
+ setInterval(() => {
893
+ if (isPlaying) {
894
+ seconds += currentSpeed;
895
+ const mins = Math.floor(seconds / 60);
896
+ const secs = Math.floor(seconds % 60);
897
+ document.getElementById("currentTime").textContent =
898
+ `${mins}:${secs.toString().padStart(2, "0")}`;
899
+ }
900
+ }, 1000);
901
+ </script>
902
+ </body>
903
+ </html>