Podcast player components (#37)
This commit is contained in:
@@ -0,0 +1,12 @@
|
||||
---
|
||||
'svelte-podcast': minor
|
||||
---
|
||||
|
||||
Improves theming with css variables
|
||||
Improves developer logs
|
||||
Fixes various package export issues
|
||||
Adds player components:
|
||||
|
||||
- Player: `<PlayerStack />`
|
||||
- Player: `<PlayerWidget />`
|
||||
- Utility: `<HeadlessTimeline />`
|
||||
@@ -12,3 +12,5 @@ node_modules
|
||||
pnpm-lock.yaml
|
||||
package-lock.json
|
||||
yarn.lock
|
||||
|
||||
**/components/_*
|
||||
@@ -17,4 +17,7 @@ module.exports = {
|
||||
es2017: true,
|
||||
node: true,
|
||||
},
|
||||
rules: {
|
||||
'no-restricted-imports': ['error', { patterns: ['$lib', '$lib/*'] }],
|
||||
},
|
||||
};
|
||||
|
||||
@@ -9,3 +9,4 @@ node_modules
|
||||
!.env.example
|
||||
vite.config.js.timestamp-*
|
||||
vite.config.ts.timestamp-*
|
||||
.eslintcache
|
||||
@@ -4,6 +4,9 @@
|
||||
"trailingComma": "all",
|
||||
"printWidth": 100,
|
||||
"plugins": ["prettier-plugin-svelte"],
|
||||
"svelteSortOrder": "options-scripts-styles-markup",
|
||||
"svelteBracketNewLine": true,
|
||||
"svelteIndentScriptAndStyle": true,
|
||||
"pluginSearchDirs": ["."],
|
||||
"overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }]
|
||||
}
|
||||
|
||||
Vendored
+1
@@ -2,6 +2,7 @@
|
||||
// See http://go.microsoft.com/fwlink/?LinkId=827846
|
||||
// for the documentation about the extensions.json format
|
||||
"recommendations": [
|
||||
"dbaeumer.vscode-eslint",
|
||||
"aaron-bond.better-comments",
|
||||
"esbenp.prettier-vscode",
|
||||
"gruntfuggly.todo-tree"
|
||||
|
||||
Vendored
+4
-1
@@ -7,6 +7,7 @@
|
||||
"source.organizeImports": true
|
||||
},
|
||||
"eslint.packageManager": "pnpm",
|
||||
"eslint.validate": ["javascript", "javascriptreact", "typescript", "typescriptreact", "svelte"],
|
||||
"files.exclude": {
|
||||
"**/.DS_Store": true,
|
||||
"**/*-lock.*": true,
|
||||
@@ -21,5 +22,7 @@
|
||||
"**/.svelte-kit": true
|
||||
},
|
||||
"problems.sortOrder": "severity",
|
||||
"todo-tree.tree.scanMode": "workspace only"
|
||||
"todo-tree.tree.scanMode": "workspace only",
|
||||
"javascript.preferences.importModuleSpecifier": "relative",
|
||||
"typescript.preferences.importModuleSpecifier": "relative"
|
||||
}
|
||||
|
||||
+21
-17
@@ -19,7 +19,9 @@
|
||||
"test": "playwright test",
|
||||
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
||||
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
||||
"lint": "prettier --check --plugin-search-dir=. .",
|
||||
"lint:prettier": "prettier --check --plugin-search-dir=. .",
|
||||
"lint:eslint": "TIMING=1 eslint . --ext .ts,.tsx,.svelte --cache",
|
||||
"lint": "pnpm lint:prettier && pnpm lint:eslint",
|
||||
"format": "prettier --write --plugin-search-dir=. .",
|
||||
"release": "pnpm package && changeset publish",
|
||||
"ts": "tsc --pretty --noImplicitAny --noEmit"
|
||||
@@ -34,31 +36,33 @@
|
||||
"dist"
|
||||
],
|
||||
"peerDependencies": {
|
||||
"svelte": "^3.54.0"
|
||||
"svelte": "^3.55.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@inqling/svelte-icons": "^3.0.2",
|
||||
"clsx": "^1.2.1",
|
||||
"just-clamp": "^4.2.0",
|
||||
"svelte-local-storage-store": "^0.4.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@changesets/cli": "^2.26.0",
|
||||
"@playwright/test": "^1.28.1",
|
||||
"@playwright/test": "^1.31.2",
|
||||
"@sveltejs/adapter-auto": "^2.0.0",
|
||||
"@sveltejs/kit": "^1.5.0",
|
||||
"@sveltejs/package": "^2.0.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.45.0",
|
||||
"@typescript-eslint/parser": "^5.45.0",
|
||||
"eslint": "^8.28.0",
|
||||
"eslint-config-prettier": "^8.5.0",
|
||||
"@sveltejs/kit": "^1.10.0",
|
||||
"@sveltejs/package": "^2.0.2",
|
||||
"@typescript-eslint/eslint-plugin": "^5.54.0",
|
||||
"@typescript-eslint/parser": "^5.54.0",
|
||||
"eslint": "^8.35.0",
|
||||
"eslint-config-prettier": "^8.6.0",
|
||||
"eslint-plugin-svelte3": "^4.0.0",
|
||||
"prettier": "^2.8.0",
|
||||
"prettier-plugin-svelte": "^2.8.1",
|
||||
"publint": "^0.1.9",
|
||||
"svelte": "^3.54.0",
|
||||
"svelte-check": "^3.0.1",
|
||||
"tslib": "^2.4.1",
|
||||
"typescript": "^4.9.3",
|
||||
"vite": "^4.0.0"
|
||||
"prettier": "^2.8.4",
|
||||
"prettier-plugin-svelte": "^2.9.0",
|
||||
"publint": "^0.1.10",
|
||||
"svelte": "^3.55.1",
|
||||
"svelte-check": "^3.0.4",
|
||||
"tslib": "^2.5.0",
|
||||
"typescript": "^4.9.5",
|
||||
"vite": "^4.1.4"
|
||||
},
|
||||
"svelte": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
|
||||
Generated
+83
-65
@@ -2,44 +2,48 @@ lockfileVersion: 5.4
|
||||
|
||||
specifiers:
|
||||
'@changesets/cli': ^2.26.0
|
||||
'@playwright/test': ^1.28.1
|
||||
'@inqling/svelte-icons': ^3.0.2
|
||||
'@playwright/test': ^1.31.2
|
||||
'@sveltejs/adapter-auto': ^2.0.0
|
||||
'@sveltejs/kit': ^1.5.0
|
||||
'@sveltejs/package': ^2.0.0
|
||||
'@typescript-eslint/eslint-plugin': ^5.45.0
|
||||
'@typescript-eslint/parser': ^5.45.0
|
||||
eslint: ^8.28.0
|
||||
eslint-config-prettier: ^8.5.0
|
||||
'@sveltejs/kit': ^1.10.0
|
||||
'@sveltejs/package': ^2.0.2
|
||||
'@typescript-eslint/eslint-plugin': ^5.54.0
|
||||
'@typescript-eslint/parser': ^5.54.0
|
||||
clsx: ^1.2.1
|
||||
eslint: ^8.35.0
|
||||
eslint-config-prettier: ^8.6.0
|
||||
eslint-plugin-svelte3: ^4.0.0
|
||||
just-clamp: ^4.2.0
|
||||
prettier: ^2.8.0
|
||||
prettier-plugin-svelte: ^2.8.1
|
||||
publint: ^0.1.9
|
||||
svelte: ^3.54.0
|
||||
svelte-check: ^3.0.1
|
||||
prettier: ^2.8.4
|
||||
prettier-plugin-svelte: ^2.9.0
|
||||
publint: ^0.1.10
|
||||
svelte: ^3.55.1
|
||||
svelte-check: ^3.0.4
|
||||
svelte-local-storage-store: ^0.4.0
|
||||
tslib: ^2.4.1
|
||||
typescript: ^4.9.3
|
||||
vite: ^4.0.0
|
||||
tslib: ^2.5.0
|
||||
typescript: ^4.9.5
|
||||
vite: ^4.1.4
|
||||
|
||||
dependencies:
|
||||
'@inqling/svelte-icons': 3.0.2_svelte@3.55.1
|
||||
clsx: 1.2.1
|
||||
just-clamp: 4.2.0
|
||||
svelte-local-storage-store: 0.4.0_svelte@3.55.1
|
||||
|
||||
devDependencies:
|
||||
'@changesets/cli': 2.26.0
|
||||
'@playwright/test': 1.31.1
|
||||
'@sveltejs/adapter-auto': 2.0.0_@sveltejs+kit@1.8.5
|
||||
'@sveltejs/kit': 1.8.5_svelte@3.55.1+vite@4.1.4
|
||||
'@playwright/test': 1.31.2
|
||||
'@sveltejs/adapter-auto': 2.0.0_@sveltejs+kit@1.10.0
|
||||
'@sveltejs/kit': 1.10.0_svelte@3.55.1+vite@4.1.4
|
||||
'@sveltejs/package': 2.0.2_4x7phaipmicbaooxtnresslofa
|
||||
'@typescript-eslint/eslint-plugin': 5.53.0_cjo54hduev4bqhpjw5znwiokqu
|
||||
'@typescript-eslint/parser': 5.53.0_ycpbpc6yetojsgtrx3mwntkhsu
|
||||
'@typescript-eslint/eslint-plugin': 5.54.0_6mj2wypvdnknez7kws2nfdgupi
|
||||
'@typescript-eslint/parser': 5.54.0_ycpbpc6yetojsgtrx3mwntkhsu
|
||||
eslint: 8.35.0
|
||||
eslint-config-prettier: 8.6.0_eslint@8.35.0
|
||||
eslint-plugin-svelte3: 4.0.0_n4ieifq2d7jq3sqoe474cgqlim
|
||||
prettier: 2.8.4
|
||||
prettier-plugin-svelte: 2.9.0_jrsxveqmsx2uadbqiuq74wlc4u
|
||||
publint: 0.1.9
|
||||
publint: 0.1.10
|
||||
svelte: 3.55.1
|
||||
svelte-check: 3.0.4_svelte@3.55.1
|
||||
tslib: 2.5.0
|
||||
@@ -500,6 +504,15 @@ packages:
|
||||
resolution: {integrity: sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==}
|
||||
dev: true
|
||||
|
||||
/@inqling/svelte-icons/3.0.2_svelte@3.55.1:
|
||||
resolution: {integrity: sha512-6ess/qqVgnFwqbBQDcZ5fFjIDDLINOyCf6cJ89y+Fx2HMepTajAIdBF18n4hWvB8OhtySog2+LWEKYyNh8p3rw==}
|
||||
engines: {pnpm: ^7.0.0}
|
||||
peerDependencies:
|
||||
svelte: ^3.55.1
|
||||
dependencies:
|
||||
svelte: 3.55.1
|
||||
dev: false
|
||||
|
||||
/@jridgewell/resolve-uri/3.1.0:
|
||||
resolution: {integrity: sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==}
|
||||
engines: {node: '>=6.0.0'}
|
||||
@@ -557,13 +570,13 @@ packages:
|
||||
fastq: 1.15.0
|
||||
dev: true
|
||||
|
||||
/@playwright/test/1.31.1:
|
||||
resolution: {integrity: sha512-IsytVZ+0QLDh1Hj83XatGp/GsI1CDJWbyDaBGbainsh0p2zC7F4toUocqowmjS6sQff2NGT3D9WbDj/3K2CJiA==}
|
||||
/@playwright/test/1.31.2:
|
||||
resolution: {integrity: sha512-BYVutxDI4JeZKV1+ups6dt5WiqKhjBtIYowyZIJ3kBDmJgsuPKsqqKNIMFbUePLSCmp2cZu+BDL427RcNKTRYw==}
|
||||
engines: {node: '>=14'}
|
||||
hasBin: true
|
||||
dependencies:
|
||||
'@types/node': 18.14.1
|
||||
playwright-core: 1.31.1
|
||||
playwright-core: 1.31.2
|
||||
optionalDependencies:
|
||||
fsevents: 2.3.2
|
||||
dev: true
|
||||
@@ -572,17 +585,17 @@ packages:
|
||||
resolution: {integrity: sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g==}
|
||||
dev: true
|
||||
|
||||
/@sveltejs/adapter-auto/2.0.0_@sveltejs+kit@1.8.5:
|
||||
/@sveltejs/adapter-auto/2.0.0_@sveltejs+kit@1.10.0:
|
||||
resolution: {integrity: sha512-b+gkHFZgD771kgV3aO4avHFd7y1zhmMYy9i6xOK7m/rwmwaRO8gnF5zBc0Rgca80B2PMU1bKNxyBTHA14OzUAQ==}
|
||||
peerDependencies:
|
||||
'@sveltejs/kit': ^1.0.0
|
||||
dependencies:
|
||||
'@sveltejs/kit': 1.8.5_svelte@3.55.1+vite@4.1.4
|
||||
'@sveltejs/kit': 1.10.0_svelte@3.55.1+vite@4.1.4
|
||||
import-meta-resolve: 2.2.1
|
||||
dev: true
|
||||
|
||||
/@sveltejs/kit/1.8.5_svelte@3.55.1+vite@4.1.4:
|
||||
resolution: {integrity: sha512-b6kbjVAivoPd3oL9IVBaZBWiuHeI0qBKfszSDXcqsPfiSMyUK7ilHDFVSWNn+2EMPO48+87iuho71yTCOXZE3w==}
|
||||
/@sveltejs/kit/1.10.0_svelte@3.55.1+vite@4.1.4:
|
||||
resolution: {integrity: sha512-0P35zHrByfbF3Ym3RdQL+RvzgsCDSyO3imSwuZ67XAD5HoCQFF3a8Mhh0V3sObz3rc5aJd4Qn82UpAihJqZ6gQ==}
|
||||
engines: {node: ^16.14 || >=18}
|
||||
hasBin: true
|
||||
requiresBuild: true
|
||||
@@ -692,8 +705,8 @@ packages:
|
||||
resolution: {integrity: sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==}
|
||||
dev: true
|
||||
|
||||
/@typescript-eslint/eslint-plugin/5.53.0_cjo54hduev4bqhpjw5znwiokqu:
|
||||
resolution: {integrity: sha512-alFpFWNucPLdUOySmXCJpzr6HKC3bu7XooShWM+3w/EL6J2HIoB2PFxpLnq4JauWVk6DiVeNKzQlFEaE+X9sGw==}
|
||||
/@typescript-eslint/eslint-plugin/5.54.0_6mj2wypvdnknez7kws2nfdgupi:
|
||||
resolution: {integrity: sha512-+hSN9BdSr629RF02d7mMtXhAJvDTyCbprNYJKrXETlul/Aml6YZwd90XioVbjejQeHbb3R8Dg0CkRgoJDxo8aw==}
|
||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||
peerDependencies:
|
||||
'@typescript-eslint/parser': ^5.0.0
|
||||
@@ -703,10 +716,10 @@ packages:
|
||||
typescript:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@typescript-eslint/parser': 5.53.0_ycpbpc6yetojsgtrx3mwntkhsu
|
||||
'@typescript-eslint/scope-manager': 5.53.0
|
||||
'@typescript-eslint/type-utils': 5.53.0_ycpbpc6yetojsgtrx3mwntkhsu
|
||||
'@typescript-eslint/utils': 5.53.0_ycpbpc6yetojsgtrx3mwntkhsu
|
||||
'@typescript-eslint/parser': 5.54.0_ycpbpc6yetojsgtrx3mwntkhsu
|
||||
'@typescript-eslint/scope-manager': 5.54.0
|
||||
'@typescript-eslint/type-utils': 5.54.0_ycpbpc6yetojsgtrx3mwntkhsu
|
||||
'@typescript-eslint/utils': 5.54.0_ycpbpc6yetojsgtrx3mwntkhsu
|
||||
debug: 4.3.4
|
||||
eslint: 8.35.0
|
||||
grapheme-splitter: 1.0.4
|
||||
@@ -720,8 +733,8 @@ packages:
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/@typescript-eslint/parser/5.53.0_ycpbpc6yetojsgtrx3mwntkhsu:
|
||||
resolution: {integrity: sha512-MKBw9i0DLYlmdOb3Oq/526+al20AJZpANdT6Ct9ffxcV8nKCHz63t/S0IhlTFNsBIHJv+GY5SFJ0XfqVeydQrQ==}
|
||||
/@typescript-eslint/parser/5.54.0_ycpbpc6yetojsgtrx3mwntkhsu:
|
||||
resolution: {integrity: sha512-aAVL3Mu2qTi+h/r04WI/5PfNWvO6pdhpeMRWk9R7rEV4mwJNzoWf5CCU5vDKBsPIFQFjEq1xg7XBI2rjiMXQbQ==}
|
||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||
peerDependencies:
|
||||
eslint: ^6.0.0 || ^7.0.0 || ^8.0.0
|
||||
@@ -730,9 +743,9 @@ packages:
|
||||
typescript:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@typescript-eslint/scope-manager': 5.53.0
|
||||
'@typescript-eslint/types': 5.53.0
|
||||
'@typescript-eslint/typescript-estree': 5.53.0_typescript@4.9.5
|
||||
'@typescript-eslint/scope-manager': 5.54.0
|
||||
'@typescript-eslint/types': 5.54.0
|
||||
'@typescript-eslint/typescript-estree': 5.54.0_typescript@4.9.5
|
||||
debug: 4.3.4
|
||||
eslint: 8.35.0
|
||||
typescript: 4.9.5
|
||||
@@ -740,16 +753,16 @@ packages:
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/@typescript-eslint/scope-manager/5.53.0:
|
||||
resolution: {integrity: sha512-Opy3dqNsp/9kBBeCPhkCNR7fmdSQqA+47r21hr9a14Bx0xnkElEQmhoHga+VoaoQ6uDHjDKmQPIYcUcKJifS7w==}
|
||||
/@typescript-eslint/scope-manager/5.54.0:
|
||||
resolution: {integrity: sha512-VTPYNZ7vaWtYna9M4oD42zENOBrb+ZYyCNdFs949GcN8Miwn37b8b7eMj+EZaq7VK9fx0Jd+JhmkhjFhvnovhg==}
|
||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||
dependencies:
|
||||
'@typescript-eslint/types': 5.53.0
|
||||
'@typescript-eslint/visitor-keys': 5.53.0
|
||||
'@typescript-eslint/types': 5.54.0
|
||||
'@typescript-eslint/visitor-keys': 5.54.0
|
||||
dev: true
|
||||
|
||||
/@typescript-eslint/type-utils/5.53.0_ycpbpc6yetojsgtrx3mwntkhsu:
|
||||
resolution: {integrity: sha512-HO2hh0fmtqNLzTAme/KnND5uFNwbsdYhCZghK2SoxGp3Ifn2emv+hi0PBUjzzSh0dstUIFqOj3bp0AwQlK4OWw==}
|
||||
/@typescript-eslint/type-utils/5.54.0_ycpbpc6yetojsgtrx3mwntkhsu:
|
||||
resolution: {integrity: sha512-WI+WMJ8+oS+LyflqsD4nlXMsVdzTMYTxl16myXPaCXnSgc7LWwMsjxQFZCK/rVmTZ3FN71Ct78ehO9bRC7erYQ==}
|
||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||
peerDependencies:
|
||||
eslint: '*'
|
||||
@@ -758,8 +771,8 @@ packages:
|
||||
typescript:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@typescript-eslint/typescript-estree': 5.53.0_typescript@4.9.5
|
||||
'@typescript-eslint/utils': 5.53.0_ycpbpc6yetojsgtrx3mwntkhsu
|
||||
'@typescript-eslint/typescript-estree': 5.54.0_typescript@4.9.5
|
||||
'@typescript-eslint/utils': 5.54.0_ycpbpc6yetojsgtrx3mwntkhsu
|
||||
debug: 4.3.4
|
||||
eslint: 8.35.0
|
||||
tsutils: 3.21.0_typescript@4.9.5
|
||||
@@ -768,13 +781,13 @@ packages:
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/@typescript-eslint/types/5.53.0:
|
||||
resolution: {integrity: sha512-5kcDL9ZUIP756K6+QOAfPkigJmCPHcLN7Zjdz76lQWWDdzfOhZDTj1irs6gPBKiXx5/6O3L0+AvupAut3z7D2A==}
|
||||
/@typescript-eslint/types/5.54.0:
|
||||
resolution: {integrity: sha512-nExy+fDCBEgqblasfeE3aQ3NuafBUxZxgxXcYfzYRZFHdVvk5q60KhCSkG0noHgHRo/xQ/BOzURLZAafFpTkmQ==}
|
||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||
dev: true
|
||||
|
||||
/@typescript-eslint/typescript-estree/5.53.0_typescript@4.9.5:
|
||||
resolution: {integrity: sha512-eKmipH7QyScpHSkhbptBBYh9v8FxtngLquq292YTEQ1pxVs39yFBlLC1xeIZcPPz1RWGqb7YgERJRGkjw8ZV7w==}
|
||||
/@typescript-eslint/typescript-estree/5.54.0_typescript@4.9.5:
|
||||
resolution: {integrity: sha512-X2rJG97Wj/VRo5YxJ8Qx26Zqf0RRKsVHd4sav8NElhbZzhpBI8jU54i6hfo9eheumj4oO4dcRN1B/zIVEqR/MQ==}
|
||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||
peerDependencies:
|
||||
typescript: '*'
|
||||
@@ -782,8 +795,8 @@ packages:
|
||||
typescript:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@typescript-eslint/types': 5.53.0
|
||||
'@typescript-eslint/visitor-keys': 5.53.0
|
||||
'@typescript-eslint/types': 5.54.0
|
||||
'@typescript-eslint/visitor-keys': 5.54.0
|
||||
debug: 4.3.4
|
||||
globby: 11.1.0
|
||||
is-glob: 4.0.3
|
||||
@@ -794,17 +807,17 @@ packages:
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/@typescript-eslint/utils/5.53.0_ycpbpc6yetojsgtrx3mwntkhsu:
|
||||
resolution: {integrity: sha512-VUOOtPv27UNWLxFwQK/8+7kvxVC+hPHNsJjzlJyotlaHjLSIgOCKj9I0DBUjwOOA64qjBwx5afAPjksqOxMO0g==}
|
||||
/@typescript-eslint/utils/5.54.0_ycpbpc6yetojsgtrx3mwntkhsu:
|
||||
resolution: {integrity: sha512-cuwm8D/Z/7AuyAeJ+T0r4WZmlnlxQ8wt7C7fLpFlKMR+dY6QO79Cq1WpJhvZbMA4ZeZGHiRWnht7ZJ8qkdAunw==}
|
||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||
peerDependencies:
|
||||
eslint: ^6.0.0 || ^7.0.0 || ^8.0.0
|
||||
dependencies:
|
||||
'@types/json-schema': 7.0.11
|
||||
'@types/semver': 7.3.13
|
||||
'@typescript-eslint/scope-manager': 5.53.0
|
||||
'@typescript-eslint/types': 5.53.0
|
||||
'@typescript-eslint/typescript-estree': 5.53.0_typescript@4.9.5
|
||||
'@typescript-eslint/scope-manager': 5.54.0
|
||||
'@typescript-eslint/types': 5.54.0
|
||||
'@typescript-eslint/typescript-estree': 5.54.0_typescript@4.9.5
|
||||
eslint: 8.35.0
|
||||
eslint-scope: 5.1.1
|
||||
eslint-utils: 3.0.0_eslint@8.35.0
|
||||
@@ -814,11 +827,11 @@ packages:
|
||||
- typescript
|
||||
dev: true
|
||||
|
||||
/@typescript-eslint/visitor-keys/5.53.0:
|
||||
resolution: {integrity: sha512-JqNLnX3leaHFZEN0gCh81sIvgrp/2GOACZNgO4+Tkf64u51kTpAyWFOY8XHx8XuXr3N2C9zgPPHtcpMg6z1g0w==}
|
||||
/@typescript-eslint/visitor-keys/5.54.0:
|
||||
resolution: {integrity: sha512-xu4wT7aRCakGINTLGeyGqDn+78BwFlggwBjnHa1ar/KaGagnmwLYmlrXIrgAaQ3AE1Vd6nLfKASm7LrFHNbKGA==}
|
||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||
dependencies:
|
||||
'@typescript-eslint/types': 5.53.0
|
||||
'@typescript-eslint/types': 5.54.0
|
||||
eslint-visitor-keys: 3.3.0
|
||||
dev: true
|
||||
|
||||
@@ -1054,6 +1067,11 @@ packages:
|
||||
engines: {node: '>=0.8'}
|
||||
dev: true
|
||||
|
||||
/clsx/1.2.1:
|
||||
resolution: {integrity: sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==}
|
||||
engines: {node: '>=6'}
|
||||
dev: false
|
||||
|
||||
/color-convert/1.9.3:
|
||||
resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==}
|
||||
dependencies:
|
||||
@@ -2471,8 +2489,8 @@ packages:
|
||||
find-up: 4.1.0
|
||||
dev: true
|
||||
|
||||
/playwright-core/1.31.1:
|
||||
resolution: {integrity: sha512-JTyX4kV3/LXsvpHkLzL2I36aCdml4zeE35x+G5aPc4bkLsiRiQshU5lWeVpHFAuC8xAcbI6FDcw/8z3q2xtJSQ==}
|
||||
/playwright-core/1.31.2:
|
||||
resolution: {integrity: sha512-a1dFgCNQw4vCsG7bnojZjDnPewZcw7tZUNFN0ZkcLYKj+mPmXvg4MpaaKZ5SgqPsOmqIf2YsVRkgqiRDxD+fDQ==}
|
||||
engines: {node: '>=14'}
|
||||
hasBin: true
|
||||
dev: true
|
||||
@@ -2521,8 +2539,8 @@ packages:
|
||||
resolution: {integrity: sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==}
|
||||
dev: true
|
||||
|
||||
/publint/0.1.9:
|
||||
resolution: {integrity: sha512-O53y7vbePxuGFmEjgcrafMSlDpOJwOkj8YdexOt7yWlv7SB3rXoT3mHknyMJ3lf2UFH5Bmt6tnIkHcOTR6dEoA==}
|
||||
/publint/0.1.10:
|
||||
resolution: {integrity: sha512-maW+K+S1cN7//l3dfZjgVKczDRyytMNeFryp7TyFJMmqAq6dQRycTyT9x5/Nc8N5yIeSc6G2H96iOShKW1M4mQ==}
|
||||
engines: {node: '>=16'}
|
||||
hasBin: true
|
||||
dependencies:
|
||||
|
||||
Vendored
+1
@@ -4,6 +4,7 @@
|
||||
declare module './lib/types' {
|
||||
interface EpisodeDetails {
|
||||
title: string;
|
||||
artwork: string;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+6
-1
@@ -6,7 +6,12 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
%sveltekit.head%
|
||||
</head>
|
||||
<body>
|
||||
<body
|
||||
style="
|
||||
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu,
|
||||
Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
|
||||
"
|
||||
>
|
||||
<div>%sveltekit.body%</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { browser } from '$app/environment';
|
||||
import { user_preferences } from '$lib/user';
|
||||
import { onMount } from 'svelte';
|
||||
import { get, readable } from 'svelte/store';
|
||||
import { user_preferences } from '../user';
|
||||
|
||||
export const audio_element = readable<HTMLAudioElement | null>(null, (set) => {
|
||||
if (!browser) return;
|
||||
const ID = 'svelte-podcast-generated-audio-element';
|
||||
const ID = 'svpod--generated-audio-element';
|
||||
|
||||
onMount(() => {
|
||||
const existing_element = document.getElementById(ID) as HTMLAudioElement | null;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<script lang="ts">
|
||||
import { audio_element } from '$lib/audio/audio-element';
|
||||
import { user_preferences } from '$lib/user';
|
||||
import { info, warn } from '$lib/utility/package/log';
|
||||
import { onMount } from 'svelte';
|
||||
import { get } from 'svelte/store';
|
||||
import { user_preferences } from '../user';
|
||||
import { info, warn } from '../utility/package/log';
|
||||
import { audio_element } from './audio-element';
|
||||
|
||||
$: $audio_element;
|
||||
/* onMount(() => {
|
||||
@@ -22,3 +22,96 @@
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<style>
|
||||
:root {
|
||||
--svpod--font: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
|
||||
'Helvetica Neue', Arial, 'Noto Sans', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji',
|
||||
'Segoe UI Symbol', 'Noto Color Emoji';
|
||||
|
||||
--svpod--surface--darker: rgb(0, 0, 0);
|
||||
--svpod--surface--base: rgb(40, 40, 40);
|
||||
--svpod--surface--lighter: rgb(60, 60, 60);
|
||||
|
||||
--svpod--content--darker: rgb(150, 150, 150);
|
||||
--svpod--content--base: rgb(200, 200, 200);
|
||||
--svpod--content--lighter: rgb(255, 255, 255);
|
||||
|
||||
--svpod--accent--darker: rgb(75, 15, 0);
|
||||
--svpod--accent--base: rgb(180, 40, 0);
|
||||
--svpod--accent--lighter: rgb(255, 55, 25);
|
||||
|
||||
--bg: var(--svpod--surface--darker);
|
||||
--fg: var(--svpod--content--darker);
|
||||
|
||||
--svpod--radius--rounded: 9999px;
|
||||
|
||||
/* element : timeline */
|
||||
|
||||
--svpod--timeline-track--shape--height: 20px;
|
||||
--svpod--timeline-track--shape--radius: 0;
|
||||
--svpod--timeline-track--shape--border: 0;
|
||||
|
||||
--svpod--timeline-thumb--shape--width: 8px;
|
||||
--svpod--timeline-thumb--shape--height: 32px;
|
||||
--svpod--timeline-thumb--shape--radius: 6px;
|
||||
--svpod--timeline-thumb--shape--border: 2px;
|
||||
}
|
||||
|
||||
.svpod--reset {
|
||||
text-transform: none;
|
||||
font-style: normal;
|
||||
text-indent: 0;
|
||||
text-shadow: none;
|
||||
text-align: left;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.svpod--a11y-hidden {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
padding: 0;
|
||||
margin: -1px;
|
||||
overflow: hidden;
|
||||
clip: rect(0, 0, 0, 0);
|
||||
white-space: nowrap;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.svpod--button,
|
||||
.svpod--icon-button {
|
||||
--fg: var(--svpod--content--base);
|
||||
--bg: var(--svpod--surface--base);
|
||||
position: relative;
|
||||
line-height: 1em;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
font-size: 16px;
|
||||
border: 0px solid transparent;
|
||||
border-radius: var(--svpod--radius--rounded);
|
||||
background-color: var(--bg);
|
||||
color: var(--fg);
|
||||
cursor: pointer;
|
||||
}
|
||||
.svpod--button:hover,
|
||||
.svpod--icon-button:hover {
|
||||
--fg: var(--svpod--content--lighter);
|
||||
--bg: var(--svpod--surface--lighter);
|
||||
}
|
||||
|
||||
.svpod--button {
|
||||
padding: 4px calc(1em);
|
||||
}
|
||||
|
||||
.svpod--icon-button {
|
||||
padding: 8px;
|
||||
min-width: 48px;
|
||||
min-height: 48px;
|
||||
}
|
||||
</style>
|
||||
</svelte:head>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { audio_element } from '$lib/audio/audio-element';
|
||||
import { episode_details } from '$lib/audio/episode-details';
|
||||
import type { EpisodeAttributes, EpisodeDetails } from '$lib/types';
|
||||
import { user_preferences, user_progress } from '$lib/user';
|
||||
import { warn } from '$lib/utility/package/log';
|
||||
import clamp from 'just-clamp';
|
||||
import { derived, get, type Readable } from 'svelte/store';
|
||||
import type { EpisodeAttributes, EpisodeDetails } from '../types';
|
||||
import { user_preferences, user_progress } from '../user';
|
||||
import { warn } from '../utility/package/log';
|
||||
import { audio_element } from './audio-element';
|
||||
import { episode_details } from './episode-details';
|
||||
|
||||
const default_episode_attributes = {
|
||||
will_autoplay: false,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { EpisodeDetails } from '$lib/types';
|
||||
import { writable } from 'svelte/store';
|
||||
import type { EpisodeDetails } from '../types';
|
||||
|
||||
export const episode_details = writable<EpisodeDetails | null>(null);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { audio_element } from '$lib/audio/audio-element';
|
||||
import type { EpisodeProgress } from '$lib/types';
|
||||
import { secondsToTimestamp } from '$lib/utility';
|
||||
import { derived, type Readable } from 'svelte/store';
|
||||
import type { EpisodeProgress } from '../types';
|
||||
import { secondsToTimestamp } from '../utility';
|
||||
import { audio_element } from './audio-element';
|
||||
|
||||
const default_episode_progress = {
|
||||
current_time: 0,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
export { default as AudioLoader } from '$lib/audio/audio-loader.svelte';
|
||||
export * from '$lib/audio/episode-data';
|
||||
export * from '$lib/audio/episode-details';
|
||||
export * from '$lib/audio/episode-progress';
|
||||
export { default as AudioLoader } from './audio-loader.svelte';
|
||||
export * from './episode-data';
|
||||
export * from './episode-details';
|
||||
export * from './episode-progress';
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
<script lang="ts">
|
||||
export let currentTime: number;
|
||||
export let paused: boolean;
|
||||
export let duration: number;
|
||||
</script>
|
||||
|
||||
<progress data-paused={paused ? 'true' : 'false'} max={duration} value={currentTime} />
|
||||
|
||||
<style>
|
||||
progress {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
padding: 0;
|
||||
margin: -1px;
|
||||
overflow: hidden;
|
||||
clip: rect(0, 0, 0, 0);
|
||||
white-space: nowrap;
|
||||
border: 0;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,145 @@
|
||||
<script lang="ts">
|
||||
import { episode_audio, episode_progress } from '../audio';
|
||||
import { info } from '../utility/package/log';
|
||||
|
||||
export let step = 10;
|
||||
|
||||
let was_paused = true;
|
||||
|
||||
function handle_drag_start(t: string) {
|
||||
info('drag_start :: ', t);
|
||||
was_paused = $episode_audio?.is_paused || true;
|
||||
episode_audio.pause();
|
||||
}
|
||||
|
||||
function handle_drag_end(t: string) {
|
||||
info('drag_end :: ', t);
|
||||
if (was_paused) return;
|
||||
else episode_audio.play();
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
input[type='range'] {
|
||||
--track--shape--height: calc(var(--svpod--timeline-track--shape--height));
|
||||
--thumb-border-offset: calc(var(--svpod--timeline-thumb--shape--border) * 2);
|
||||
--thumb--shape--height: calc(
|
||||
var(--svpod--timeline-thumb--shape--height) - var(--thumb-border-offset)
|
||||
);
|
||||
|
||||
--svpod--timeline-track--bg: var(--svpod--surface--base);
|
||||
--svpod--timeline-track--border: var(--bg);
|
||||
|
||||
--svpod--timeline-thumb--bg: var(--svpod--content--base);
|
||||
--svpod--timeline-thumb--border: var(--bg);
|
||||
|
||||
font-size: 1em;
|
||||
|
||||
--borders: calc(var(--svpod--timeline-thumb--shape--border) * 2);
|
||||
--no-shadow: 0px 0px 0px transparent;
|
||||
|
||||
height: calc(var(--thumb--shape--height) + var(--borders));
|
||||
appearance: none;
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
background: transparent;
|
||||
}
|
||||
input[type='range']:hover {
|
||||
--svpod--timeline-track--bg: var(--svpod--accent--darker);
|
||||
--svpod--timeline-track--border: var(--svpod--accent--base);
|
||||
--svpod--timeline-thumb--bg: var(--svpod--accent--lighter);
|
||||
}
|
||||
input[type='range']:focus {
|
||||
outline: none;
|
||||
}
|
||||
input[type='range']::-webkit-slider-runnable-track {
|
||||
width: 100%;
|
||||
height: var(--track--shape--height);
|
||||
cursor: pointer;
|
||||
animate: 0.2s;
|
||||
box-shadow: var(--no-shadow);
|
||||
background: var(--svpod--timeline-track--bg);
|
||||
border-radius: var(--svpod--timeline-track--shape--radius);
|
||||
border: var(--svpod--timeline-track--shape--border) solid var(--svpod--timeline-track--border);
|
||||
}
|
||||
input[type='range']::-webkit-slider-thumb {
|
||||
--offset: calc(var(--svpod--timeline-track--shape--border) * -1);
|
||||
box-shadow: var(--no-shadow);
|
||||
border: var(--svpod--timeline-thumb--shape--border) solid var(--svpod--timeline-thumb--border);
|
||||
height: var(--thumb--shape--height);
|
||||
width: var(--svpod--timeline-thumb--shape--width);
|
||||
border-radius: var(--svpod--timeline-thumb--shape--radius);
|
||||
background: var(--svpod--timeline-thumb--bg);
|
||||
cursor: pointer;
|
||||
-webkit-appearance: none;
|
||||
margin-top: var(--offset);
|
||||
}
|
||||
input[type='range']:focus::-webkit-slider-runnable-track {
|
||||
background: var(--svpod--timeline-track--bg);
|
||||
}
|
||||
input[type='range']::-moz-range-track {
|
||||
width: 100%;
|
||||
height: var(--track--shape--height);
|
||||
cursor: pointer;
|
||||
animate: 0.2s;
|
||||
box-shadow: var(--no-shadow);
|
||||
background: var(--svpod--timeline-track--bg);
|
||||
border-radius: var(--svpod--timeline-track--shape--radius);
|
||||
border: var(--svpod--timeline-track--shape--border) solid var(--svpod--timeline-track--border);
|
||||
}
|
||||
input[type='range']::-moz-range-thumb {
|
||||
box-shadow: var(--no-shadow);
|
||||
border: var(--svpod--timeline-thumb--shape--border) solid var(--svpod--timeline-thumb--border);
|
||||
height: var(--thumb--shape--height);
|
||||
width: var(--svpod--timeline-thumb--shape--width);
|
||||
border-radius: var(--svpod--timeline-thumb--shape--radius);
|
||||
background: var(--svpod--timeline-thumb--bg);
|
||||
cursor: pointer;
|
||||
}
|
||||
input[type='range']::-ms-track {
|
||||
width: 100%;
|
||||
height: var(--track--shape--height);
|
||||
cursor: pointer;
|
||||
animate: 0.2s;
|
||||
background: transparent;
|
||||
border-color: transparent;
|
||||
color: transparent;
|
||||
}
|
||||
input[type='range']::-ms-fill-lower,
|
||||
input[type='range']::-ms-fill-upper {
|
||||
background: var(--svpod--timeline-track--bg);
|
||||
border: var(--svpod--timeline-track--shape--border) solid var(--svpod--timeline-track--border);
|
||||
border-radius: calc(var(--svpod--timeline-track--shape--radius) * 2);
|
||||
box-shadow: var(--no-shadow);
|
||||
}
|
||||
input[type='range']::-ms-thumb {
|
||||
margin-top: 1px;
|
||||
box-shadow: var(--no-shadow);
|
||||
border: var(--svpod--timeline-thumb--shape--border) solid var(--svpod--timeline-thumb--border);
|
||||
height: var(--thumb--shape--height);
|
||||
width: var(--svpod--timeline-thumb--shape--width);
|
||||
border-radius: var(--svpod--timeline-thumb--shape--radius);
|
||||
background: var(--svpod--timeline-thumb--bg);
|
||||
cursor: pointer;
|
||||
}
|
||||
input[type='range']:focus::-ms-fill-lower,
|
||||
input[type='range']:focus::-ms-fill-upper {
|
||||
background: var(--svpod--timeline-track--bg);
|
||||
}
|
||||
</style>
|
||||
|
||||
<input
|
||||
class={$$props.class}
|
||||
style="display:block; width:100%;"
|
||||
type="range"
|
||||
data-paused={$episode_audio?.is_paused ? 'true' : 'false'}
|
||||
min={0}
|
||||
{step}
|
||||
max={$episode_audio?.duration || step}
|
||||
value={$episode_progress.current_time}
|
||||
on:change={(e) => episode_audio.seek(e.currentTarget.valueAsNumber)}
|
||||
on:touchstart={() => handle_drag_start('touchstart')}
|
||||
on:mousedown={() => handle_drag_start('mousedown')}
|
||||
on:touchend={() => handle_drag_end('touchend')}
|
||||
on:mouseup={() => handle_drag_end('mouseup')}
|
||||
/>
|
||||
@@ -0,0 +1,3 @@
|
||||
export { default as HeadlessTimeline } from './headless-timeline.svelte';
|
||||
export { default as PlayerStack } from './player-stack.svelte';
|
||||
export { default as PlayerWidget } from './player-widget.svelte';
|
||||
@@ -0,0 +1,318 @@
|
||||
<script lang="ts">
|
||||
import { Pause, Play, SpeakerWave } from '@inqling/svelte-icons/heroicon-20-solid';
|
||||
import { clsx } from 'clsx';
|
||||
import { HeadlessTimeline } from '.';
|
||||
import { episode_audio, episode_progress } from '../audio';
|
||||
import { user_preferences } from '../user';
|
||||
import { A11yIcon, Skip, Spinner, Timestamp } from './utility';
|
||||
|
||||
type WithElement = {
|
||||
timestamps: boolean;
|
||||
playback_rate: boolean;
|
||||
skip_back: number;
|
||||
skip_forward: number;
|
||||
};
|
||||
|
||||
const default_options = {
|
||||
timestamps: false,
|
||||
playback_rate: false,
|
||||
skip_back: 10,
|
||||
skip_forward: 10,
|
||||
} satisfies WithElement;
|
||||
|
||||
export let include: Partial<WithElement> = {};
|
||||
$: options = { ...default_options, ...include };
|
||||
|
||||
$: is_playing = $episode_audio?.is_paused === false;
|
||||
|
||||
$: timestamp_hours = Boolean($episode_audio?.duration && $episode_audio.duration >= 3600);
|
||||
|
||||
export let playback_rate_values: number[] = [1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4];
|
||||
|
||||
$: is_loaded = Boolean($episode_audio?.src);
|
||||
|
||||
const { class: ClassName, ...rest } = $$restProps;
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.svpod--container {
|
||||
--fg: var(--svpod--content--base);
|
||||
--bg: var(--svpod--surface--darker);
|
||||
|
||||
--padding: 2px;
|
||||
--radius: 0.75em;
|
||||
--inner-radius: calc(var(--radius) - var(--padding));
|
||||
|
||||
--svpod--timeline-track--shape--height: 3em;
|
||||
--svpod--timeline-track--shape--radius: var(--inner-radius);
|
||||
--svpod--timeline-track--shape--border: 2px;
|
||||
|
||||
--svpod--timeline-thumb--shape--height: var(--svpod--timeline-track--shape--height);
|
||||
--svpod--timeline-thumb--shape--width: 3px;
|
||||
--svpod--timeline-thumb--shape--radius: 1px;
|
||||
--svpod--timeline-thumb--shape--border: 0px;
|
||||
|
||||
background-color: var(--bg);
|
||||
color: var(--fg);
|
||||
display: inline-flex;
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
font-size: 16px;
|
||||
line-height: 1em;
|
||||
width: auto;
|
||||
max-width: 100%;
|
||||
border-radius: var(--radius);
|
||||
font-weight: 600;
|
||||
padding: var(--padding);
|
||||
gap: 0;
|
||||
}
|
||||
|
||||
.svpod--container--row {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: stretch;
|
||||
justify-content: flex-end;
|
||||
width: 100%;
|
||||
min-width: max-content;
|
||||
border-radius: var(--inner-radius);
|
||||
gap: var(--padding);
|
||||
}
|
||||
.svpod--container--row:last-child {
|
||||
border-top: 1px solid var(--svpod--surface--base);
|
||||
border-top-right-radius: 0;
|
||||
border-top-left-radius: 0;
|
||||
}
|
||||
|
||||
.svpod--row--artwork {
|
||||
order: 0;
|
||||
}
|
||||
.svpod--row--timeline {
|
||||
order: 1;
|
||||
}
|
||||
.svpod--row--controls {
|
||||
order: 2;
|
||||
justify-content: center;
|
||||
}
|
||||
.svpod--row--timestamps {
|
||||
order: 3;
|
||||
min-height: 32px;
|
||||
gap: 0.5em;
|
||||
}
|
||||
|
||||
div.svpod--timeline {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: stretch;
|
||||
flex-grow: 1;
|
||||
flex-shrink: 1;
|
||||
font-size: 1em;
|
||||
background: none;
|
||||
}
|
||||
|
||||
.svpod--toggle-pause {
|
||||
align-items: center;
|
||||
border: none;
|
||||
display: flex;
|
||||
font-size: 1em;
|
||||
height: 3em;
|
||||
justify-content: center;
|
||||
width: 3em;
|
||||
border-radius: var(--inner-radius);
|
||||
}
|
||||
button.svpod--toggle-pause {
|
||||
cursor: pointer;
|
||||
}
|
||||
.svpod--toggle-pause :global(svg) {
|
||||
width: 1.5em;
|
||||
height: 1.5em;
|
||||
}
|
||||
|
||||
div.svpod--timestamp {
|
||||
background-color: var(--bg);
|
||||
display: inline-flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 4px;
|
||||
font-size: 1em;
|
||||
line-height: 1em;
|
||||
pointer-events: none;
|
||||
border-radius: var(--inner-radius);
|
||||
width: max-content;
|
||||
flex-grow: 0;
|
||||
text-align: center;
|
||||
padding: 0 0.1em;
|
||||
}
|
||||
|
||||
select.svpod--playback-rate {
|
||||
background-color: var(--bg);
|
||||
appearance: none;
|
||||
padding: 0 0.75em;
|
||||
color: var(--fg);
|
||||
overflow: hidden;
|
||||
letter-spacing: 0.5px;
|
||||
font-size: 0.8em;
|
||||
line-height: 1em;
|
||||
text-transform: uppercase;
|
||||
font-weight: 600;
|
||||
text-align: center;
|
||||
min-width: 48px;
|
||||
outline-color: var(--svpod--accent--lighter);
|
||||
cursor: pointer;
|
||||
margin: 0;
|
||||
border-radius: var(--inner-radius);
|
||||
}
|
||||
select.svpod--playback-rate,
|
||||
select.svpod--playback-rate:hover,
|
||||
select.svpod--playback-rate:focus {
|
||||
border: none;
|
||||
shadow: none;
|
||||
}
|
||||
select.svpod--playback-rate:focus {
|
||||
--fg: var(--svpod--accent--lighter);
|
||||
}
|
||||
|
||||
button.svpod--toggle-pause,
|
||||
select.svpod--playback-rate {
|
||||
background-color: var(--bg);
|
||||
color: var(--fg);
|
||||
}
|
||||
button.svpod--toggle-pause:hover,
|
||||
select.svpod--playback-rate:hover {
|
||||
--bg: var(--svpod--surface--base);
|
||||
--fg: var(--svpod--content--lighter);
|
||||
background-color: var(--bg);
|
||||
color: var(--fg);
|
||||
}
|
||||
div.svpod--artwork {
|
||||
position: relative;
|
||||
border-radius: var(--inner-radius);
|
||||
overflow: hidden;
|
||||
}
|
||||
div.svpod--artwork > :global(div),
|
||||
div.svpod--artwork > :global(img) {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
.svpod--artwork--placeholder {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: var(--svpod--accent--lighter);
|
||||
font-size: 2.5em;
|
||||
}
|
||||
.svpod--artwork--placeholder :global(svg) {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
.svpod--artwork--placeholder::after {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
content: '';
|
||||
opacity: 0.5;
|
||||
background: linear-gradient(45deg, var(--svpod--accent--base), transparent);
|
||||
}
|
||||
|
||||
span.svpod--aspect--square {
|
||||
display: block;
|
||||
position: relative;
|
||||
line-height: 0;
|
||||
padding-bottom: 100%;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div
|
||||
class={clsx('svpod--container svpod--reset', ClassName)}
|
||||
data-loaded={is_loaded ? 'true' : 'false'}
|
||||
{...rest}
|
||||
>
|
||||
<div class="svpod--container--row svpod--row--controls">
|
||||
<button
|
||||
on:click={() => options.skip_back && episode_audio.skip(options.skip_back, 'backward')}
|
||||
class="svpod--reset svpod--toggle-pause"
|
||||
type="button"
|
||||
>
|
||||
<Skip value={options.skip_back} type="backward" />
|
||||
</button>
|
||||
|
||||
<!-- toggle play -->
|
||||
{#if is_loaded}
|
||||
<button
|
||||
on:click={() => is_loaded && episode_audio.play('toggle')}
|
||||
class="svpod--reset svpod--toggle-pause"
|
||||
type="button"
|
||||
>
|
||||
<A11yIcon icon={is_playing ? Pause : Play} label={is_playing ? 'Pause' : 'Play'} />
|
||||
</button>
|
||||
{:else}
|
||||
<div class="svpod--reset svpod--toggle-pause">
|
||||
<A11yIcon icon={Spinner} label="Waiting for audio..." />
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<button
|
||||
on:click={() => options.skip_forward && episode_audio.skip(options.skip_forward, 'forward')}
|
||||
class="svpod--reset svpod--toggle-pause"
|
||||
type="button"
|
||||
>
|
||||
<Skip value={options.skip_forward} type="forward" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="svpod--artwork svpod--row--artwork">
|
||||
<span class="svpod--aspect--square" />
|
||||
<slot>
|
||||
<div class="svpod--artwork--placeholder">
|
||||
<span><SpeakerWave style="width:1em; height: 1em;" /></span>
|
||||
</div>
|
||||
</slot>
|
||||
</div>
|
||||
|
||||
<div class="svpod--timeline svpod--row--timeline">
|
||||
<HeadlessTimeline />
|
||||
</div>
|
||||
|
||||
{#if options.timestamps || options.playback_rate}
|
||||
<div class="svpod--container--row svpod--row--timestamps">
|
||||
{#if options.timestamps}
|
||||
<div class="svpod--timestamp">
|
||||
<Timestamp value={$episode_progress?.current_time || 0} force_hours={timestamp_hours} />
|
||||
</div>
|
||||
|
||||
<div style="flex-grow: 1" />
|
||||
|
||||
<div class="svpod--timestamp">
|
||||
<Timestamp value={$episode_audio?.duration || 0} />
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if options.playback_rate}
|
||||
<select
|
||||
class="svpod--playback-rate"
|
||||
value={$user_preferences.playback_rate}
|
||||
on:change={(e) => {
|
||||
const value = parseFloat(e.currentTarget.value);
|
||||
user_preferences.set.playback_rate(value);
|
||||
}}
|
||||
>
|
||||
{#each playback_rate_values as value}
|
||||
<option {value}>
|
||||
{#if Number.isInteger(value)}
|
||||
{value}.0
|
||||
{:else}
|
||||
{value}
|
||||
{/if}
|
||||
</option>
|
||||
{/each}
|
||||
</select>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
@@ -0,0 +1,234 @@
|
||||
<script lang="ts">
|
||||
import { Pause, Play } from '@inqling/svelte-icons/heroicon-20-solid';
|
||||
import { clsx } from 'clsx';
|
||||
import { HeadlessTimeline } from '.';
|
||||
import { episode_audio, episode_progress } from '../audio';
|
||||
import { user_preferences } from '../user';
|
||||
import { A11yIcon, Skip, Spinner, Timestamp } from './utility';
|
||||
|
||||
type WithElement = {
|
||||
duration: boolean;
|
||||
current_time: boolean;
|
||||
playback_rate: boolean;
|
||||
skip_back: number | null;
|
||||
skip_forward: number | null;
|
||||
};
|
||||
|
||||
const default_options = {
|
||||
duration: false,
|
||||
current_time: false,
|
||||
playback_rate: false,
|
||||
skip_back: null,
|
||||
skip_forward: null,
|
||||
} satisfies WithElement;
|
||||
|
||||
export let include: Partial<WithElement> = {};
|
||||
$: options = { ...default_options, ...include };
|
||||
|
||||
$: is_playing = $episode_audio?.is_paused === false;
|
||||
|
||||
$: current_time = options.current_time && ($episode_progress?.current_time || 0);
|
||||
$: duration = options.duration && ($episode_audio?.duration || 0);
|
||||
$: timestamp_hours = Boolean($episode_audio?.duration && $episode_audio.duration >= 3600);
|
||||
|
||||
export let playback_rate_values: number[] = [1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4];
|
||||
|
||||
$: is_loaded = Boolean($episode_audio?.src);
|
||||
|
||||
const { class: ClassName, ...rest } = $$restProps;
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.svpod--container {
|
||||
--fg: var(--svpod--content--base);
|
||||
--bg: var(--svpod--surface--darker);
|
||||
|
||||
--padding: 2px;
|
||||
--radius: 0.5em;
|
||||
--inner-radius: calc(var(--radius) - var(--padding));
|
||||
|
||||
--svpod--timeline-track--shape--height: 3em;
|
||||
--svpod--timeline-track--shape--radius: var(--inner-radius);
|
||||
--svpod--timeline-track--shape--border: 2px;
|
||||
|
||||
--svpod--timeline-thumb--shape--height: var(--svpod--timeline-track--shape--height);
|
||||
--svpod--timeline-thumb--shape--width: 3px;
|
||||
--svpod--timeline-thumb--shape--radius: 1px;
|
||||
--svpod--timeline-thumb--shape--border: 0px;
|
||||
|
||||
background-color: var(--bg);
|
||||
color: var(--fg);
|
||||
display: inline-flex;
|
||||
flex-direction: row;
|
||||
align-items: stretch;
|
||||
font-size: 16px;
|
||||
line-height: 1em;
|
||||
min-width: 320px;
|
||||
max-width: 100%;
|
||||
border-radius: var(--radius);
|
||||
font-weight: 600;
|
||||
padding: var(--padding);
|
||||
gap: var(--padding);
|
||||
}
|
||||
|
||||
div.svpod--timeline {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: stretch;
|
||||
flex-grow: 1;
|
||||
flex-shrink: 1;
|
||||
font-size: 1em;
|
||||
background: none;
|
||||
}
|
||||
|
||||
.svpod--toggle-pause {
|
||||
align-items: center;
|
||||
border: none;
|
||||
display: flex;
|
||||
font-size: 1em;
|
||||
height: 3em;
|
||||
justify-content: center;
|
||||
width: 3em;
|
||||
border-radius: var(--inner-radius);
|
||||
}
|
||||
button.svpod--toggle-pause {
|
||||
cursor: pointer;
|
||||
}
|
||||
.svpod--toggle-pause :global(svg) {
|
||||
width: 1.5em;
|
||||
height: 1.5em;
|
||||
}
|
||||
|
||||
div.svpod--timestamp {
|
||||
background-color: var(--bg);
|
||||
display: inline-flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 4px;
|
||||
font-size: 1em;
|
||||
line-height: 1em;
|
||||
pointer-events: none;
|
||||
border-radius: var(--inner-radius);
|
||||
width: max-content;
|
||||
flex-grow: 0;
|
||||
text-align: center;
|
||||
padding: 0 0.1em;
|
||||
}
|
||||
|
||||
select.svpod--playback-rate {
|
||||
background-color: var(--bg);
|
||||
appearance: none;
|
||||
padding: 0 0.75em;
|
||||
color: var(--fg);
|
||||
overflow: hidden;
|
||||
letter-spacing: 0.5px;
|
||||
font-size: 0.8em;
|
||||
line-height: 1em;
|
||||
text-transform: uppercase;
|
||||
font-weight: 600;
|
||||
text-align: center;
|
||||
min-width: 48px;
|
||||
outline-color: var(--svpod--accent--lighter);
|
||||
cursor: pointer;
|
||||
margin: 0;
|
||||
border-radius: var(--inner-radius);
|
||||
}
|
||||
select.svpod--playback-rate,
|
||||
select.svpod--playback-rate:hover,
|
||||
select.svpod--playback-rate:focus {
|
||||
border: none;
|
||||
shadow: none;
|
||||
}
|
||||
select.svpod--playback-rate:focus {
|
||||
--fg: var(--svpod--accent--lighter);
|
||||
}
|
||||
|
||||
button.svpod--toggle-pause,
|
||||
select.svpod--playback-rate {
|
||||
background-color: var(--bg);
|
||||
color: var(--fg);
|
||||
}
|
||||
button.svpod--toggle-pause:hover,
|
||||
select.svpod--playback-rate:hover {
|
||||
--bg: var(--svpod--surface--base);
|
||||
--fg: var(--svpod--content--lighter);
|
||||
background-color: var(--bg);
|
||||
color: var(--fg);
|
||||
}
|
||||
</style>
|
||||
|
||||
<div
|
||||
class={clsx('svpod--container svpod--reset', ClassName)}
|
||||
data-loaded={is_loaded ? 'true' : 'false'}
|
||||
{...rest}
|
||||
>
|
||||
{#if options.skip_back}
|
||||
<button
|
||||
on:click={() => options.skip_back && episode_audio.skip(options.skip_back, 'backward')}
|
||||
class="svpod--reset svpod--toggle-pause"
|
||||
type="button"
|
||||
>
|
||||
<Skip value={options.skip_back} type="backward" />
|
||||
</button>
|
||||
{/if}
|
||||
|
||||
<!-- toggle play -->
|
||||
{#if is_loaded}
|
||||
<button
|
||||
on:click={() => is_loaded && episode_audio.play('toggle')}
|
||||
class="svpod--reset svpod--toggle-pause"
|
||||
type="button"
|
||||
>
|
||||
<A11yIcon icon={is_playing ? Pause : Play} label={is_playing ? 'Pause' : 'Play'} />
|
||||
</button>
|
||||
{:else}
|
||||
<div class="svpod--reset svpod--toggle-pause">
|
||||
<A11yIcon icon={Spinner} label="Waiting for audio..." />
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if options.skip_forward}
|
||||
<button
|
||||
on:click={() => options.skip_forward && episode_audio.skip(options.skip_forward, 'forward')}
|
||||
class="svpod--reset svpod--toggle-pause"
|
||||
type="button"
|
||||
>
|
||||
<Skip value={options.skip_forward} type="forward" />
|
||||
</button>
|
||||
{/if}
|
||||
|
||||
{#if current_time !== false}
|
||||
<div class="svpod--timestamp">
|
||||
<Timestamp value={current_time} force_hours={timestamp_hours} />
|
||||
</div>
|
||||
{/if}
|
||||
<div class="svpod--timeline">
|
||||
<HeadlessTimeline />
|
||||
</div>
|
||||
{#if duration !== false}
|
||||
<div class="svpod--timestamp">
|
||||
<Timestamp value={duration} />
|
||||
</div>
|
||||
{/if}
|
||||
{#if options.playback_rate}
|
||||
<select
|
||||
class="svpod--playback-rate"
|
||||
value={$user_preferences.playback_rate}
|
||||
on:change={(e) => {
|
||||
const value = parseFloat(e.currentTarget.value);
|
||||
user_preferences.set.playback_rate(value);
|
||||
}}
|
||||
>
|
||||
{#each playback_rate_values as value}
|
||||
<option {value}>
|
||||
{#if Number.isInteger(value)}
|
||||
{value}.0
|
||||
{:else}
|
||||
{value}
|
||||
{/if}
|
||||
</option>
|
||||
{/each}
|
||||
</select>
|
||||
{/if}
|
||||
</div>
|
||||
@@ -0,0 +1,9 @@
|
||||
<script lang="ts">
|
||||
import type { SvelteIcon } from '@inqling/svelte-icons';
|
||||
|
||||
export let icon: SvelteIcon;
|
||||
export let label: string;
|
||||
</script>
|
||||
|
||||
<svelte:component this={icon} />
|
||||
<span class="svpod--a11y-hidden">{label}</span>
|
||||
@@ -0,0 +1,4 @@
|
||||
export { default as A11yIcon } from './a11y-icon.svelte';
|
||||
export { default as Skip } from './skip.svelte';
|
||||
export { default as Spinner } from './spinner.svelte';
|
||||
export { default as Timestamp } from './timestamp.svelte';
|
||||
@@ -0,0 +1,155 @@
|
||||
<script lang="ts">
|
||||
export let size = 24;
|
||||
export let value: number;
|
||||
export let type: 'forward' | 'backward';
|
||||
</script>
|
||||
|
||||
<style>
|
||||
div.svpod--skip-container {
|
||||
display: inline-flex;
|
||||
position: relative;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 1.5em;
|
||||
height: 1.5em;
|
||||
}
|
||||
|
||||
div.svpod--skip-icon,
|
||||
span.svpod--skip-mask {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
span.svpod--skip-mask {
|
||||
z-index: 1;
|
||||
background: linear-gradient(0deg, var(--bg), transparent);
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
div.svpod--skip-icon {
|
||||
z-index: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: var(--svpod--content--base);
|
||||
}
|
||||
|
||||
span.svpod--skip-label {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
font-size: 0.5em;
|
||||
line-height: 1em;
|
||||
color: var(--svpod--content--lighter);
|
||||
}
|
||||
|
||||
@keyframes Spin {
|
||||
0%,
|
||||
100% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
svg {
|
||||
/* animation: Spin 1s cubic-bezier(0.5, 0.1, 0.1, 0.8) infinite; */
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="svpod--skip-container" style="font-size:{size}px">
|
||||
<span class="svpod--skip-label">
|
||||
<span class="svpod--a11y-hidden">skip {type}</span>
|
||||
{value}
|
||||
<span class="svpod--a11y-hidden">seconds</span>
|
||||
</span>
|
||||
<span
|
||||
class="svpod--skip-mask"
|
||||
class:svpod--skip-mask--right={type === 'backward'}
|
||||
class:svpod--skip-mask--left={type === 'backward'}
|
||||
/>
|
||||
<div class="svpod--skip-icon">
|
||||
{#if type === 'forward'}
|
||||
<!-- forward -->
|
||||
<svg
|
||||
width={size}
|
||||
height={size}
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
style="font-size:{size}px"
|
||||
>
|
||||
<path
|
||||
d="M15.5922 0.913446C14.1363 0.310389 12.5759 -1.8792e-08 11 0L11 2C12.3132 2 13.6136 2.25866 14.8268 2.7612C16.0401 3.26375 17.1425 4.00035 18.0711 4.92893C18.9997 5.85752 19.7362 6.95991 20.2388 8.17317C20.6133 9.07731 20.8524 10.0298 20.9499 11H22.9583C22.8551 9.76677 22.5617 8.55484 22.0866 7.4078C21.4835 5.95189 20.5996 4.62902 19.4853 3.51472C18.371 2.40042 17.0481 1.5165 15.5922 0.913446Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path
|
||||
d="M22.9583 13C22.9338 13.2923 22.8987 13.5835 22.853 13.8728L20.9082 13.3517C20.9242 13.2348 20.9381 13.1175 20.9499 13H22.9583Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path
|
||||
d="M20.4408 15.297L22.3771 15.8159C22.2894 16.0775 22.1925 16.3365 22.0866 16.5922C22.078 16.6128 22.0694 16.6334 22.0608 16.6539L20.3123 15.6444C20.3573 15.5293 20.4002 15.4135 20.4408 15.297Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path
|
||||
d="M19.3994 17.4268L21.1333 18.4278C20.9675 18.6892 20.7916 18.944 20.6061 19.1919L19.1744 17.7602C19.2516 17.6505 19.3267 17.5394 19.3994 17.4268Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path
|
||||
d="M17.8609 19.2751L19.2757 20.6899C19.0399 20.9144 18.7954 21.1289 18.5429 21.333L17.5281 19.5752C17.6412 19.4778 17.7521 19.3777 17.8609 19.2751Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path
|
||||
d="M15.8834 20.7265L16.8835 22.4587C16.5838 22.6273 16.2766 22.7831 15.9628 22.9257L15.4366 20.9619C15.5876 20.8872 15.7366 20.8087 15.8834 20.7265Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path
|
||||
d="M13.5553 21.668L14.0729 23.5999C13.7189 23.6937 13.3609 23.7712 13 23.8322V21.798C13.1862 21.7599 13.3714 21.7166 13.5553 21.668Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
{:else}
|
||||
<!-- back -->
|
||||
<svg
|
||||
width={size}
|
||||
height={size}
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
style="font-size:{size}px"
|
||||
>
|
||||
<path
|
||||
d="M11.0003 21.9499V23.9583C10.6215 23.9266 10.2447 23.877 9.87115 23.8096L10.391 21.8697C10.5931 21.9026 10.7963 21.9294 11.0003 21.9499Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path
|
||||
d="M7.8915 21.1169C7.98478 21.159 8.07876 21.1996 8.17342 21.2388C8.26813 21.278 8.36337 21.3158 8.45911 21.352L7.9393 23.292C7.7608 23.2278 7.58367 23.1593 7.40805 23.0866C7.23249 23.0138 7.05886 22.937 6.8873 22.8562L7.8915 21.1169Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path
|
||||
d="M5.67173 19.7427C5.8305 19.8725 5.99315 19.9973 6.15943 20.1169L5.15524 21.8563C4.84345 21.6397 4.54188 21.4084 4.25159 21.1629L5.67173 19.7427Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path
|
||||
d="M3.88341 17.841C4.00303 18.0072 4.12778 18.1698 4.25752 18.3285L2.83737 19.7487C2.59192 19.4584 2.36059 19.1569 2.14409 18.8452L3.88341 17.841Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path
|
||||
d="M2.64825 15.5412C2.68449 15.6369 2.72223 15.7321 2.76146 15.8268C2.80069 15.9215 2.84134 16.0156 2.88341 16.1089L1.14409 17.1131C1.06327 16.9415 0.986441 16.7678 0.913699 16.5922C0.840963 16.4166 0.772485 16.2395 0.708295 16.061L2.64825 15.5412Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path
|
||||
d="M2.05038 13C2.07088 13.204 2.09764 13.4072 2.1306 13.6093L0.19065 14.1291C0.123306 13.7556 0.0736704 13.3788 0.0419922 13H2.05038Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path
|
||||
d="M2.05038 11C2.14788 10.0298 2.38695 9.07731 2.76146 8.17317C3.26401 6.95991 4.0006 5.85752 4.92919 4.92893C5.85777 4.00035 6.96016 3.26375 8.17342 2.7612C9.38667 2.25866 10.687 2 12.0003 2V0C10.4244 1.8792e-08 8.86396 0.310389 7.40805 0.913446C5.95215 1.5165 4.62928 2.40042 3.51497 3.51472C2.40067 4.62902 1.51676 5.95189 0.913699 7.4078C0.438578 8.55484 0.145121 9.76677 0.0419922 11H2.05038Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,62 @@
|
||||
<script lang="ts">
|
||||
export let size = 24;
|
||||
</script>
|
||||
|
||||
<style>
|
||||
div.svpod--spinner-container {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
div.svpod--item {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
opacity: 0.5;
|
||||
color: var(--svpod--accent--lighter);
|
||||
}
|
||||
div.svpod--item:nth-child(1) {
|
||||
opacity: 1;
|
||||
color: var(--svpod--content--lighter);
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
@keyframes Spin {
|
||||
0%,
|
||||
100% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
svg {
|
||||
animation: Spin 1s cubic-bezier(0.5, 0.1, 0.1, 0.8) infinite;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="svpod--spinner-container" style="width:{size}px; height:{size}px">
|
||||
{#each [0, 0.075, 0.15] as delay}
|
||||
<div class="svpod--item">
|
||||
<svg
|
||||
width={size}
|
||||
height={size}
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
style="animation-delay: {delay}s;"
|
||||
>
|
||||
<path
|
||||
d="M24 12C24 14.3734 23.2962 16.6935 21.9776 18.6668C20.6591 20.6402 18.7849 22.1783 16.5922 23.0866C14.3995 23.9948 11.9867 24.2324 9.65892 23.7694C7.33114 23.3064 5.19295 22.1635 3.51472 20.4853C1.83649 18.8071 0.693599 16.6689 0.230577 14.3411C-0.232446 12.0133 0.00519403 9.60051 0.913446 7.4078C1.8217 5.21509 3.35977 3.34094 5.33316 2.02236C7.30655 0.703788 9.62662 -2.83022e-08 12 0L12 3C10.22 3 8.47991 3.52784 6.99987 4.51677C5.51983 5.50571 4.36627 6.91131 3.68508 8.55585C3.0039 10.2004 2.82567 12.01 3.17293 13.7558C3.5202 15.5016 4.37737 17.1053 5.63604 18.364C6.89471 19.6226 8.49836 20.4798 10.2442 20.8271C11.99 21.1743 13.7996 20.9961 15.4442 20.3149C17.0887 19.6337 18.4943 18.4802 19.4832 17.0001C20.4722 15.5201 21 13.78 21 12H24Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
@@ -0,0 +1,19 @@
|
||||
<script lang="ts">
|
||||
import { secondsToTimestamp } from '../../utility';
|
||||
|
||||
export let value: number;
|
||||
export let force_hours = false;
|
||||
|
||||
$: timestamp = secondsToTimestamp(value, force_hours);
|
||||
</script>
|
||||
|
||||
<style>
|
||||
span {
|
||||
letter-spacing: 0.5px;
|
||||
font-size: 0.8em;
|
||||
display: block;
|
||||
width: 8ch;
|
||||
}
|
||||
</style>
|
||||
|
||||
<span style="width:{timestamp.length}ch">{timestamp}</span>
|
||||
+4
-3
@@ -1,3 +1,4 @@
|
||||
export * from '$lib/audio';
|
||||
export * from '$lib/user';
|
||||
export * from '$lib/utility';
|
||||
export * from './audio';
|
||||
export * from './components';
|
||||
export * from './user';
|
||||
export * from './utility';
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { UserPreferences } from '$lib/types';
|
||||
import clamp from 'just-clamp';
|
||||
import { persisted } from 'svelte-local-storage-store';
|
||||
import type { UserPreferences } from '../types';
|
||||
|
||||
const _default_user_preferences = { playback_rate: 1, volume: 1 } satisfies UserPreferences;
|
||||
const _user_preferences = persisted<UserPreferences>('USER_PREFERENCE', _default_user_preferences);
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { episode_audio, episode_progress } from '$lib/audio';
|
||||
import type { UserProgress } from '$lib/types';
|
||||
import { get_pathname_from_url } from '$lib/utility/get-pathname-from-url';
|
||||
import { info } from '$lib/utility/package/log';
|
||||
import { persisted } from 'svelte-local-storage-store';
|
||||
import { get } from 'svelte/store';
|
||||
import { episode_audio, episode_progress } from '../audio';
|
||||
import type { UserProgress } from '../types';
|
||||
import { get_pathname_from_url } from '../utility';
|
||||
import { info } from '../utility/package/log';
|
||||
|
||||
const _default_user_progress = {} satisfies UserProgress;
|
||||
const _user_progress = persisted<UserProgress>('USER_PROGRESS', _default_user_progress);
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
export * from './get-pathname-from-url';
|
||||
export * from './seconds-to-timestamp';
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { dev } from '$app/environment';
|
||||
|
||||
const useLogger = {
|
||||
error: console.error,
|
||||
info: console.info,
|
||||
error: console.error,
|
||||
warn: console.warn,
|
||||
};
|
||||
|
||||
@@ -13,7 +13,11 @@ export function log(type: Logger, ...content: unknown[]) {
|
||||
|
||||
if (type === 'info' && !dev) return;
|
||||
|
||||
logger('🔊 svelte-podcast: ', ...content);
|
||||
logger(
|
||||
'%c🔊 svelte-podcast:',
|
||||
'color: #FF3E00; background-color: rgba(255, 62, 0, 0.15); padding: 4px 8px; border-radius:4px;',
|
||||
...content,
|
||||
);
|
||||
}
|
||||
|
||||
export const info = (...content: unknown[]) => log('info', ...content);
|
||||
|
||||
@@ -3,14 +3,14 @@ function section(t: number) {
|
||||
return t;
|
||||
}
|
||||
|
||||
export function secondsToTimestamp(seconds: number) {
|
||||
export function secondsToTimestamp(seconds: number, force_hours = false) {
|
||||
const hh = Math.floor(seconds / 3600);
|
||||
const hh_remainder = seconds % 3600;
|
||||
const mm = Math.floor(hh_remainder / 60);
|
||||
const mm_remainder = hh_remainder % 60;
|
||||
const ss = Math.floor(mm_remainder);
|
||||
|
||||
const hrs = hh > 0 ? `${section(hh)}:` : '';
|
||||
const hrs = force_hours || hh > 0 ? `${section(hh)}:` : '';
|
||||
const mins = mm ? `${section(mm)}:` : '00:';
|
||||
const secs = ss ? `${section(ss)}` : '00';
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { AudioLoader } from '$lib/audio';
|
||||
import { AudioLoader } from 'svelte-podcast';
|
||||
</script>
|
||||
|
||||
<AudioLoader />
|
||||
|
||||
+60
-12
@@ -1,5 +1,12 @@
|
||||
<script lang="ts">
|
||||
import { episode_audio, episode_progress, user_preferences, user_progress } from '$lib';
|
||||
import {
|
||||
episode_audio,
|
||||
episode_progress,
|
||||
PlayerStack,
|
||||
PlayerWidget,
|
||||
user_preferences,
|
||||
user_progress,
|
||||
} from 'svelte-podcast';
|
||||
|
||||
const sources = {
|
||||
syntax: {
|
||||
@@ -16,6 +23,8 @@
|
||||
|
||||
let current_time = 0;
|
||||
$: current_time = $episode_progress.current_time;
|
||||
|
||||
$: console.log('details :: ', $episode_audio?.details);
|
||||
</script>
|
||||
|
||||
<pre>{JSON.stringify(
|
||||
@@ -25,7 +34,7 @@
|
||||
$user_preferences,
|
||||
},
|
||||
null,
|
||||
3,
|
||||
2,
|
||||
)}</pre>
|
||||
|
||||
<h1>Demo</h1>
|
||||
@@ -34,7 +43,7 @@
|
||||
<button type="button" on:click={user_preferences.clear}>Clear all preferences</button>
|
||||
|
||||
<h5>Load Audio</h5>
|
||||
<button type="button" on:click={() => episode_audio.load(sources['syntax'].src, sources['knomii'])}
|
||||
<button type="button" on:click={() => episode_audio.load(sources['syntax'].src, sources['syntax'])}
|
||||
>Syntax</button
|
||||
>
|
||||
<button type="button" on:click={() => episode_audio.load(sources['knomii'].src, sources['knomii'])}
|
||||
@@ -58,15 +67,6 @@
|
||||
|
||||
<h6>Seeking</h6>
|
||||
|
||||
<input
|
||||
type="range"
|
||||
min={0}
|
||||
max={$episode_audio?.duration}
|
||||
style="width:100%"
|
||||
bind:value={current_time}
|
||||
on:change={(e) => episode_audio.seek(parseInt(e.currentTarget.value))}
|
||||
/>
|
||||
|
||||
<button type="button" on:click={() => episode_audio.seek(30)}>Go to 30s from start </button>
|
||||
<button type="button" on:click={() => episode_audio.seek(30, 'from-end')}>Go to 30s from end</button
|
||||
>
|
||||
@@ -80,3 +80,51 @@
|
||||
{rate}x
|
||||
</button>
|
||||
{/each}
|
||||
|
||||
<hr />
|
||||
|
||||
<!-- <PodcastPlayer
|
||||
artwork={$episode_audio?.details?.artwork}
|
||||
title={$episode_audio?.details?.title || 'Podcast Name'}
|
||||
/> -->
|
||||
|
||||
<br />
|
||||
<PlayerWidget />
|
||||
<br />
|
||||
<br />
|
||||
<PlayerWidget include={{ playback_rate: true }} />
|
||||
<br />
|
||||
<br />
|
||||
<PlayerWidget include={{ skip_back: 10, skip_forward: 30 }} />
|
||||
<br />
|
||||
<br />
|
||||
<PlayerWidget
|
||||
style="width: 100%;"
|
||||
include={{
|
||||
current_time: true,
|
||||
playback_rate: true,
|
||||
duration: true,
|
||||
skip_back: 10,
|
||||
skip_forward: 30,
|
||||
}}
|
||||
/>
|
||||
|
||||
<br />
|
||||
<br />
|
||||
<PlayerStack style="width:400px;" />
|
||||
<br />
|
||||
<br />
|
||||
<PlayerStack include={{ playback_rate: true }} />
|
||||
<br />
|
||||
<br />
|
||||
<PlayerStack include={{ skip_back: 10, skip_forward: 30, timestamps: true }} />
|
||||
<br />
|
||||
<br />
|
||||
<PlayerStack
|
||||
include={{
|
||||
playback_rate: true,
|
||||
timestamps: true,
|
||||
skip_back: 10,
|
||||
skip_forward: 30,
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -1,16 +1,108 @@
|
||||
<script lang="ts">
|
||||
import { episode_audio, episode_progress } from '$lib/audio';
|
||||
import {
|
||||
episode_audio,
|
||||
episode_progress,
|
||||
PlayerWidget,
|
||||
user_preferences,
|
||||
user_progress,
|
||||
} from 'svelte-podcast';
|
||||
|
||||
const sources = {
|
||||
syntax: {
|
||||
src: `/example-syntax.mp3`,
|
||||
title: `Supper Club × Rich Harris, Author of Svelte`,
|
||||
artwork: `https://ssl-static.libsyn.com/p/assets/b/3/c/d/b3cdf28da11ad39fe5bbc093207a2619/Syntax_-_499.jpg`,
|
||||
},
|
||||
knomii: {
|
||||
src: `/example-knomii.mp3`,
|
||||
title: `Empowerment starts with letting go of control`,
|
||||
artwork: `https://ssl-static.libsyn.com/p/assets/f/a/8/d/fa8d56d5226884335f2e77a3093c12a1/ep-6.png`,
|
||||
},
|
||||
} as const;
|
||||
|
||||
let current_time = 0;
|
||||
$: current_time = $episode_progress.current_time;
|
||||
|
||||
$: console.log('details :: ', $episode_audio?.details);
|
||||
</script>
|
||||
|
||||
<h1>Demo</h1>
|
||||
|
||||
<a href="/">Demo</a>
|
||||
|
||||
<pre>{JSON.stringify(
|
||||
{
|
||||
$episode_audio,
|
||||
$episode_progress,
|
||||
$user_preferences,
|
||||
},
|
||||
null,
|
||||
3,
|
||||
2,
|
||||
)}</pre>
|
||||
|
||||
<h1>Demo</h1>
|
||||
<a href="/another-page">Another Page</a>
|
||||
<button type="button" on:click={user_progress.clear}>Clear progress for all episodes</button>
|
||||
<button type="button" on:click={user_preferences.clear}>Clear all preferences</button>
|
||||
|
||||
<h5>Load Audio</h5>
|
||||
<button type="button" on:click={() => episode_audio.load(sources['syntax'].src, sources['syntax'])}
|
||||
>Syntax</button
|
||||
>
|
||||
<button type="button" on:click={() => episode_audio.load(sources['knomii'].src, sources['knomii'])}
|
||||
>Knomii</button
|
||||
>
|
||||
<button type="button" on:click={() => episode_audio.unload()}>None</button>
|
||||
|
||||
<h5>Custom audio controls</h5>
|
||||
|
||||
<h6>Play / Pause Actions</h6>
|
||||
|
||||
<button type="button" on:click={() => episode_audio.play()}>Play</button>
|
||||
<button type="button" on:click={() => episode_audio.pause()}>Pause</button>
|
||||
<button type="button" on:click={() => episode_audio.pause('toggle')}>Toggle</button>
|
||||
|
||||
<h6>Audio Actions</h6>
|
||||
|
||||
<button type="button" on:click={() => episode_audio.mute()}>Mute</button>
|
||||
<button type="button" on:click={() => episode_audio.unmute()}>Unmute</button>
|
||||
<button type="button" on:click={() => episode_audio.mute('toggle')}>Toggle</button>
|
||||
|
||||
<h6>Seeking</h6>
|
||||
|
||||
<button type="button" on:click={() => episode_audio.seek(30)}>Go to 30s from start </button>
|
||||
<button type="button" on:click={() => episode_audio.seek(30, 'from-end')}>Go to 30s from end</button
|
||||
>
|
||||
<button type="button" on:click={() => episode_audio.skip(10, 'forward')}>Skip 10s</button>
|
||||
<button type="button" on:click={() => episode_audio.skip(10, 'backward')}>Rewind 10s</button>
|
||||
|
||||
<h6>Playback Rate</h6>
|
||||
|
||||
{#each [0.5, 1, 2, 3] as rate}
|
||||
<button type="button" on:click={() => user_preferences.set.playback_rate(rate)}>
|
||||
{rate}x
|
||||
</button>
|
||||
{/each}
|
||||
|
||||
<hr />
|
||||
|
||||
<!-- <PodcastPlayer
|
||||
artwork={$episode_audio?.details?.artwork}
|
||||
title={$episode_audio?.details?.title || 'Podcast Name'}
|
||||
/> -->
|
||||
|
||||
<br />
|
||||
<PlayerWidget />
|
||||
<br />
|
||||
<br />
|
||||
<PlayerWidget include={{ playback_rate: true }} />
|
||||
<br />
|
||||
<br />
|
||||
<PlayerWidget include={{ skip_back: 10, skip_forward: 30 }} />
|
||||
<br />
|
||||
<br />
|
||||
<PlayerWidget
|
||||
include={{
|
||||
current_time: true,
|
||||
playback_rate: true,
|
||||
duration: true,
|
||||
skip_back: 10,
|
||||
skip_forward: 30,
|
||||
}}
|
||||
/>
|
||||
|
||||
+1
-2
@@ -10,8 +10,7 @@ const config = {
|
||||
kit: {
|
||||
adapter: adapter(),
|
||||
alias: {
|
||||
$lib: 'src/lib',
|
||||
$pkg: 'src/lib/utility/package/',
|
||||
'svelte-podcast': 'src/lib',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user