Merge branch 'main' of https://github.com/OllieJT/svelte-podcast
@@ -1,7 +0,0 @@
|
||||
---
|
||||
'svelte-podcast': minor
|
||||
---
|
||||
|
||||
- Restructures lib to make dist cleaner and dev easier to grock
|
||||
- Refactors <audio /> bindings to make it easier to change values when loading a different source
|
||||
- Moves more values to micro-stores
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'svelte-podcast': minor
|
||||
---
|
||||
|
||||
Adds mechanic for managing user preferences
|
||||
@@ -1,8 +1,8 @@
|
||||
name: 'CI'
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- '**'
|
||||
branches-ignore:
|
||||
- 'main'
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
@@ -1,5 +1,15 @@
|
||||
# svelte-podcast
|
||||
|
||||
## 0.2.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- 6f89448: Adds the ability to bind episode metadata to the audio store
|
||||
- a70d68d: - Restructures lib to make dist cleaner and dev easier to grock
|
||||
- Refactors <audio /> bindings to make it easier to change values when loading a different source
|
||||
- Moves more values to micro-stores
|
||||
- 1841199: Adds save/load mechanic for episode progress
|
||||
|
||||
## 0.1.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "svelte-podcast",
|
||||
"version": "0.1.1",
|
||||
"version": "0.2.0",
|
||||
"license": "MIT",
|
||||
"author": "Ollie Taylor",
|
||||
"homepage": "https://github.com/OllieJT/svelte-podcast/blob/main/README.md",
|
||||
|
||||
@@ -2,44 +2,53 @@
|
||||
import {
|
||||
audio_autoplay,
|
||||
audio_current_time,
|
||||
audio_duration,
|
||||
audio_element,
|
||||
audio_metadata,
|
||||
audio_ended,
|
||||
audio_loading,
|
||||
audio_muted,
|
||||
audio_playback_rate,
|
||||
audio_paused,
|
||||
audio_src,
|
||||
audio_start_at,
|
||||
audio_volume,
|
||||
} from '$lib/context/audio-internals';
|
||||
import { user_preferences } from '$lib/context/user-preferences';
|
||||
import type { PlayerElement } from '$lib/types/types';
|
||||
import { isBoolean, isNumber } from '$pkg/type-guards';
|
||||
import { load_podcast_state } from '$lib/utility/use-state';
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
// readonly values
|
||||
let element: PlayerElement;
|
||||
let currentTime = $audio_start_at;
|
||||
let volume = $audio_volume;
|
||||
|
||||
let current_time = $audio_start_at;
|
||||
let duration = 0;
|
||||
let ended = true;
|
||||
let muted = false;
|
||||
let paused = false;
|
||||
let playbackRate = 1;
|
||||
|
||||
// Update stores based on bindings
|
||||
// handler values
|
||||
let volume = $user_preferences.volume;
|
||||
let playbackRate = $user_preferences.playback_rate;
|
||||
let muted = false;
|
||||
|
||||
// readonly - stores will update when bindings change
|
||||
$: audio_element.set(element);
|
||||
$: audio_current_time.set(currentTime);
|
||||
$: audio_metadata.update((x) => ({ ...x, duration, ended, paused }));
|
||||
$: audio_current_time.set(current_time);
|
||||
$: audio_duration.set(duration);
|
||||
$: audio_ended.set(ended);
|
||||
$: audio_paused.set(paused);
|
||||
|
||||
// Update bindings based on stores
|
||||
$: volume = $audio_volume;
|
||||
// handlers - when store value changes, the binding will update the audio element
|
||||
$: volume = $user_preferences.volume;
|
||||
$: playbackRate = $user_preferences.playback_rate;
|
||||
$: muted = $audio_muted;
|
||||
$: playbackRate = $audio_playback_rate;
|
||||
$: currentTime = $audio_start_at;
|
||||
$: current_time = $audio_start_at;
|
||||
|
||||
onMount(load_podcast_state);
|
||||
</script>
|
||||
|
||||
<audio
|
||||
bind:this={element}
|
||||
src={$audio_src}
|
||||
autoplay={$audio_autoplay}
|
||||
bind:currentTime
|
||||
bind:currentTime={current_time}
|
||||
bind:muted
|
||||
bind:duration
|
||||
bind:paused
|
||||
@@ -62,34 +71,6 @@
|
||||
// TODO: reset player state
|
||||
// console.log('emptied', e);
|
||||
}}
|
||||
on:loadeddata={() => {
|
||||
audio_metadata.update((m) => ({ ...m, loading: false }));
|
||||
}}
|
||||
on:loadstart={() => {
|
||||
audio_metadata.update((m) => ({ ...m, loading: true }));
|
||||
}}
|
||||
on:playing={(e) => {
|
||||
// @ts-expect-error event is not fully typed
|
||||
const is_paused = e.target.paused;
|
||||
if (!isBoolean(is_paused)) return;
|
||||
audio_metadata.update((m) => ({ ...m, paused: is_paused }));
|
||||
}}
|
||||
on:pause={(e) => {
|
||||
// @ts-expect-error event is not fully typed
|
||||
const is_paused = e.target.paused;
|
||||
if (!isBoolean(is_paused)) return;
|
||||
audio_metadata.update((m) => ({ ...m, paused: is_paused }));
|
||||
}}
|
||||
on:ratechange={(e) => {
|
||||
// @ts-expect-error event is not fully typed
|
||||
const playbackRate = e.target.playbackRate;
|
||||
if (!isNumber(playbackRate)) return;
|
||||
audio_metadata.update((m) => ({ ...m, playbackRate }));
|
||||
}}
|
||||
on:durationchange={(e) => {
|
||||
// @ts-expect-error event is not fully typed
|
||||
const duration = e.target?.duration;
|
||||
if (!isNumber(duration)) return;
|
||||
audio_metadata.update((m) => ({ ...m, duration }));
|
||||
}}
|
||||
on:loadeddata={() => audio_loading.set(false)}
|
||||
on:loadstart={() => audio_loading.set(true)}
|
||||
/>
|
||||
|
||||
@@ -1,30 +1,16 @@
|
||||
import type { PlayerElement } from '$lib/types/types';
|
||||
import { writable } from 'svelte/store';
|
||||
|
||||
export const audio_element = writable<PlayerElement>();
|
||||
export const audio_start_at = writable<number>(0);
|
||||
|
||||
export const audio_autoplay = writable<boolean>(false);
|
||||
// readonly - stores will update when bindings change
|
||||
export const audio_current_time = writable<number>(0);
|
||||
export const audio_element = writable<PlayerElement>();
|
||||
export const audio_duration = writable<number>(0);
|
||||
export const audio_ended = writable<boolean>(false);
|
||||
export const audio_loading = writable<boolean>(false);
|
||||
export const audio_paused = writable<boolean>(true);
|
||||
|
||||
// handlers - when store value changes, the binding will update the audio element
|
||||
export const audio_start_at = writable<number>(0);
|
||||
export const audio_autoplay = writable<boolean>(false);
|
||||
export const audio_muted = writable<boolean>(false);
|
||||
export const audio_playback_rate = writable<number>(1);
|
||||
export const audio_src = writable<string>('');
|
||||
export const audio_volume = writable<number>(1);
|
||||
|
||||
export type PlayerMetadata = {
|
||||
duration: number;
|
||||
// muted: boolean;
|
||||
paused: boolean;
|
||||
// playbackRate: number;
|
||||
loading: boolean;
|
||||
ended: boolean;
|
||||
};
|
||||
|
||||
export const audio_metadata = writable<PlayerMetadata>({
|
||||
duration: 0,
|
||||
ended: false,
|
||||
loading: false,
|
||||
paused: true,
|
||||
// muted: false,
|
||||
// playbackRate: 1,
|
||||
});
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
import { writable } from 'svelte/store';
|
||||
|
||||
export type AudioMetadata = {
|
||||
title: string;
|
||||
artwork: string;
|
||||
};
|
||||
|
||||
export const audio_metadata = writable<AudioMetadata | null>(null);
|
||||
@@ -1,14 +1,18 @@
|
||||
import {
|
||||
audio_autoplay,
|
||||
audio_current_time,
|
||||
audio_duration,
|
||||
audio_element,
|
||||
audio_metadata,
|
||||
audio_ended,
|
||||
audio_loading,
|
||||
audio_muted,
|
||||
audio_playback_rate,
|
||||
audio_paused,
|
||||
audio_src,
|
||||
audio_start_at,
|
||||
audio_volume,
|
||||
} from '$lib/context/audio-internals';
|
||||
import { audio_metadata, type AudioMetadata } from '$lib/context/audio-metadata';
|
||||
import { episode_progress } from '$lib/context/episode-progress';
|
||||
import { user_preferences } from '$lib/context/user-preferences';
|
||||
import { secondsToTimestamp } from '$lib/utility/seconds-to-timestamp';
|
||||
import { info, warn } from '$pkg/log';
|
||||
import clamp from 'just-clamp';
|
||||
@@ -16,24 +20,45 @@ import { derived } from 'svelte/store';
|
||||
|
||||
const audio_state = derived(
|
||||
[
|
||||
audio_metadata,
|
||||
audio_autoplay,
|
||||
audio_current_time,
|
||||
audio_duration,
|
||||
audio_ended,
|
||||
audio_loading,
|
||||
audio_paused,
|
||||
audio_start_at,
|
||||
audio_autoplay,
|
||||
audio_muted,
|
||||
audio_playback_rate,
|
||||
audio_src,
|
||||
audio_volume,
|
||||
audio_metadata,
|
||||
user_preferences,
|
||||
],
|
||||
([$metadata, $autoplay, $current_time, $muted, $playback_rate, $src, $volume]) => {
|
||||
([
|
||||
$current_time,
|
||||
$duration,
|
||||
$ended,
|
||||
$loading,
|
||||
$paused,
|
||||
$start_at,
|
||||
$autoplay,
|
||||
$muted,
|
||||
$src,
|
||||
$metadata,
|
||||
$user_preferences,
|
||||
]) => {
|
||||
return {
|
||||
...$metadata,
|
||||
autoplay: $autoplay,
|
||||
current_time: $current_time,
|
||||
timestamp: secondsToTimestamp($current_time),
|
||||
duration: $duration,
|
||||
ended: $ended,
|
||||
loading: $loading,
|
||||
paused: $paused,
|
||||
start_at: $start_at,
|
||||
autoplay: $autoplay,
|
||||
muted: $muted,
|
||||
playback_rate: $playback_rate,
|
||||
src: $src,
|
||||
volume: $volume,
|
||||
metadata: $metadata,
|
||||
playback_rate: $user_preferences.playback_rate,
|
||||
volume: $user_preferences.volume,
|
||||
timestamp: secondsToTimestamp($current_time),
|
||||
};
|
||||
},
|
||||
);
|
||||
@@ -80,34 +105,40 @@ function unmute(type: HandleType = 'set') {
|
||||
}
|
||||
}
|
||||
|
||||
type LoadOptions = {
|
||||
export type AudioLoadData = AudioMetadata & { src: string };
|
||||
|
||||
export type AudioLoadOptions = {
|
||||
autoplay: boolean;
|
||||
start_at?: number;
|
||||
};
|
||||
|
||||
function load(src: string, opts: LoadOptions) {
|
||||
const load = (data: AudioLoadData, opts: AudioLoadOptions) => {
|
||||
episode_progress.stash();
|
||||
|
||||
const { src, ...metadata } = data;
|
||||
const progress = episode_progress.use(src);
|
||||
const start_at = progress?.start_at || opts.start_at || 0;
|
||||
|
||||
console.log('progress', progress);
|
||||
|
||||
info('load: ', src);
|
||||
audio_autoplay.set(opts.autoplay);
|
||||
audio_volume.set(1);
|
||||
audio_src.set(src);
|
||||
audio_start_at.set(opts.start_at || 0);
|
||||
audio_metadata.set(metadata);
|
||||
audio_start_at.set(start_at);
|
||||
// opts.current_time ? seek(opts.current_time) : null;
|
||||
//opts.autoplay && play();
|
||||
}
|
||||
};
|
||||
function unload() {
|
||||
episode_progress.stash();
|
||||
info('unload: ');
|
||||
pause();
|
||||
audio_autoplay.set(false);
|
||||
audio_volume.set(1);
|
||||
audio_src.set('');
|
||||
audio_metadata.set(null);
|
||||
audio_start_at.set(0);
|
||||
}
|
||||
|
||||
function setPlaybackRate(rate: number) {
|
||||
info('setPlaybackRate: ', rate);
|
||||
audio_playback_rate.set(clamp(0, 10, rate));
|
||||
}
|
||||
|
||||
function seek(seconds: number, from: 'from-start' | 'from-end' = 'from-start') {
|
||||
info('seek: ', seconds, from);
|
||||
return audio_element.subscribe((el) => {
|
||||
@@ -140,7 +171,6 @@ export const audio = {
|
||||
unmute,
|
||||
load,
|
||||
unload,
|
||||
setPlaybackRate,
|
||||
seek,
|
||||
skip,
|
||||
};
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
import { browser } from '$app/environment';
|
||||
import { audio, type AudioLoadOptions } from '$lib/context/audio';
|
||||
import { error, info, warn } from '$lib/utility/package/log';
|
||||
|
||||
export type EpisodeProgress = {
|
||||
current_time: number;
|
||||
};
|
||||
|
||||
const episode_progress_map = new Map<string, EpisodeProgress>();
|
||||
|
||||
function stash_episode() {
|
||||
return audio.subscribe((state) => {
|
||||
info('saving progress: ', state.src);
|
||||
if (!state.src) return;
|
||||
episode_progress_map.set(state.src, { current_time: state.current_time });
|
||||
save_all();
|
||||
})();
|
||||
}
|
||||
|
||||
function use_episode(src: string): Pick<AudioLoadOptions, 'start_at'> | null {
|
||||
const progress = episode_progress_map.get(src);
|
||||
|
||||
if (!progress) return null;
|
||||
|
||||
info('found saved progress: ', progress);
|
||||
|
||||
return { start_at: progress.current_time };
|
||||
}
|
||||
|
||||
type SavedEpisodeProgress = EpisodeProgress & { src: string };
|
||||
const EPISODE_PROGRESS_KEY = 'EPISODE_PROGRESS' as const;
|
||||
|
||||
function save_all() {
|
||||
if (!browser || !localStorage) {
|
||||
warn('localStorage not available, skipping save');
|
||||
return;
|
||||
}
|
||||
const items = [...episode_progress_map];
|
||||
|
||||
const episodes = items.reduce((prev, [src, progress]) => {
|
||||
return [...prev, { src, ...progress }];
|
||||
}, [] as SavedEpisodeProgress[]);
|
||||
|
||||
info(`Saving progress for ${episodes.length} episodes to localStorage`, episodes);
|
||||
localStorage.setItem(EPISODE_PROGRESS_KEY, JSON.stringify(episodes));
|
||||
}
|
||||
|
||||
function load_all() {
|
||||
if (!browser || !localStorage) {
|
||||
warn('localStorage not available, skipping load');
|
||||
return;
|
||||
}
|
||||
const data = localStorage.getItem(EPISODE_PROGRESS_KEY);
|
||||
|
||||
if (!data) {
|
||||
info('No saved episode progress found');
|
||||
return;
|
||||
}
|
||||
|
||||
const episodes = JSON.parse(data) as SavedEpisodeProgress[];
|
||||
|
||||
try {
|
||||
episodes.forEach(({ src, ...progress }) => {
|
||||
episode_progress_map.set(src, progress);
|
||||
});
|
||||
info(`Loaded ${episode_progress_map.size} episodes progress from localStorage`);
|
||||
} catch (e) {
|
||||
error('Error loading episode progress', e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
function clear_all() {
|
||||
episode_progress_map.clear();
|
||||
save_all();
|
||||
}
|
||||
|
||||
export const episode_progress = {
|
||||
episodes: {
|
||||
get: episode_progress_map.get,
|
||||
includes: episode_progress_map.has,
|
||||
forEach: episode_progress_map.forEach,
|
||||
map: [...episode_progress_map].map,
|
||||
},
|
||||
stash: stash_episode,
|
||||
use: use_episode,
|
||||
save_all,
|
||||
load_all,
|
||||
clear_all,
|
||||
};
|
||||
@@ -0,0 +1,70 @@
|
||||
import { browser } from '$app/environment';
|
||||
import { info, warn } from '$lib/utility/package/log';
|
||||
import clamp from 'just-clamp';
|
||||
import { writable } from 'svelte/store';
|
||||
|
||||
export type UserPreferences = {
|
||||
playback_rate: number;
|
||||
volume: number;
|
||||
};
|
||||
|
||||
const default_user_preferences = {
|
||||
playback_rate: 1,
|
||||
volume: 1,
|
||||
} satisfies UserPreferences;
|
||||
|
||||
const user_preference_store = writable<UserPreferences>(default_user_preferences);
|
||||
|
||||
function clamp_preferences(prefs: UserPreferences) {
|
||||
return {
|
||||
playback_rate: clamp(prefs.playback_rate, 0.5, 5),
|
||||
volume: clamp(prefs.volume, 0, 1),
|
||||
};
|
||||
}
|
||||
|
||||
function set_preference(prefs: Partial<UserPreferences>) {
|
||||
user_preference_store.update((prev) => clamp_preferences({ ...prev, ...prefs }));
|
||||
save();
|
||||
}
|
||||
const USER_PREFERENCE_KEY = 'USER_PREFERENCE' as const;
|
||||
|
||||
function save() {
|
||||
if (!browser || !localStorage) {
|
||||
warn('localStorage not available, skipping save');
|
||||
return;
|
||||
}
|
||||
return user_preference_store.subscribe((prefs) => {
|
||||
info(`Saving user preferences to localStorage`, prefs);
|
||||
localStorage.setItem(USER_PREFERENCE_KEY, JSON.stringify(prefs));
|
||||
})();
|
||||
}
|
||||
|
||||
function load() {
|
||||
if (!browser || !localStorage) {
|
||||
warn('localStorage not available, skipping load');
|
||||
return;
|
||||
}
|
||||
const data = localStorage.getItem(USER_PREFERENCE_KEY);
|
||||
|
||||
if (!data) {
|
||||
info('No saved user preferences found');
|
||||
return;
|
||||
}
|
||||
|
||||
const preferences = JSON.parse(data) as UserPreferences;
|
||||
set_preference(preferences);
|
||||
info(`Loaded user preferences from localStorage`, preferences);
|
||||
}
|
||||
|
||||
function clear() {
|
||||
user_preference_store.set(default_user_preferences);
|
||||
save();
|
||||
}
|
||||
|
||||
export const user_preferences = {
|
||||
subscribe: user_preference_store.subscribe,
|
||||
set: set_preference,
|
||||
save,
|
||||
load,
|
||||
clear,
|
||||
};
|
||||
@@ -1,2 +1,5 @@
|
||||
export { default as AudioLoader } from '$lib/components/audio-loader.svelte';
|
||||
export { audio } from '$lib/context/audio';
|
||||
export * from '$lib/context/audio';
|
||||
export * from '$lib/context/episode-progress';
|
||||
export * from '$lib/context/user-preferences';
|
||||
export * from '$lib/utility';
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
export * from './seconds-to-timestamp';
|
||||
export * from './use-state';
|
||||
@@ -13,9 +13,9 @@ export function log(type: Logger, ...content: unknown[]) {
|
||||
|
||||
if (type === 'info' && !dev) return;
|
||||
|
||||
logger('🔊 svelte-podcast: ', content);
|
||||
logger('🔊 svelte-podcast: ', ...content);
|
||||
}
|
||||
|
||||
export const info = (...content: unknown[]) => log('info', ...content);
|
||||
export const warn = (...content: unknown[]) => log('warn', content);
|
||||
export const error = (...content: unknown[]) => log('error', content);
|
||||
export const warn = (...content: unknown[]) => log('warn', ...content);
|
||||
export const error = (...content: unknown[]) => log('error', ...content);
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
import { episode_progress } from '$lib/context/episode-progress';
|
||||
import { user_preferences } from '$lib/context/user-preferences';
|
||||
|
||||
export function save_podcast_state() {
|
||||
episode_progress.save_all();
|
||||
user_preferences.save();
|
||||
}
|
||||
|
||||
export function load_podcast_state() {
|
||||
episode_progress.load_all();
|
||||
user_preferences.load();
|
||||
}
|
||||
@@ -1,10 +1,24 @@
|
||||
<script lang="ts">
|
||||
import { audio } from '$lib';
|
||||
import {
|
||||
audio,
|
||||
episode_progress,
|
||||
save_podcast_state,
|
||||
user_preferences,
|
||||
type AudioLoadData,
|
||||
} from '$lib';
|
||||
|
||||
const sources = {
|
||||
syntax: '/example-syntax.mp3',
|
||||
knomii: '/example-knomii.mp3',
|
||||
} as const;
|
||||
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`,
|
||||
},
|
||||
} satisfies Record<string, AudioLoadData>;
|
||||
|
||||
let current_time = 0;
|
||||
$: current_time = $audio.current_time;
|
||||
@@ -14,6 +28,9 @@
|
||||
|
||||
<h1>Demo</h1>
|
||||
<a href="/another-page">Another Page</a>
|
||||
<button type="button" on:click={episode_progress.save_all}>Save progress</button>
|
||||
<button type="button" on:click={user_preferences.save}>Save preferences</button>
|
||||
<button type="button" on:click={save_podcast_state}>Save state (all)</button>
|
||||
|
||||
<h5>Load Audio</h5>
|
||||
<button
|
||||
@@ -58,5 +75,7 @@
|
||||
<h6>Playback Rate</h6>
|
||||
|
||||
{#each [0.5, 1, 2, 3] as rate}
|
||||
<button type="button" on:click={() => audio.setPlaybackRate(rate)}>{rate}x</button>
|
||||
<button type="button" on:click={() => user_preferences.set({ playback_rate: rate })}
|
||||
>{rate}x</button
|
||||
>
|
||||
{/each}
|
||||
|
||||
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 3.1 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 142 KiB |
@@ -1,6 +1,77 @@
|
||||
<svg width="512" height="512" viewBox="0 0 512 512" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M356.446 98.5659L282.016 145.075C247.749 166.487 230.65 204.98 234.858 242.622C234.423 245.597 234.197 248.64 234.197 251.736V366.479C234.197 400.945 262.138 428.885 296.604 428.885C331.07 428.885 359.01 400.945 359.01 366.479V336.629L389.678 317.465C389.692 317.457 389.706 317.448 389.721 317.439L464.151 270.93C511.748 241.188 526.222 178.492 496.48 130.895C466.738 83.2984 404.043 68.824 356.446 98.5659Z" fill="#FF3E00"/>
|
||||
<path d="M317.392 230.903C317.513 224.873 320.58 219.022 326.077 215.588L400.507 169.079C409.161 163.671 420.56 166.303 425.967 174.957C431.375 183.611 428.743 195.01 420.089 200.417L392.981 217.357L392.982 217.359C392.878 217.423 392.774 217.487 392.67 217.551C392.591 217.601 392.512 217.65 392.433 217.699C367.272 233.421 353.747 260.719 354.448 288.376C359 286.891 363.446 284.834 367.69 282.183L442.12 235.674C470.245 218.099 478.798 181.052 461.224 152.926C443.649 124.801 406.602 116.248 378.476 133.822L304.046 180.331C285.941 191.644 275.947 211.026 275.811 230.903H275.771V366.479C275.771 377.984 285.098 387.311 296.603 387.311C308.109 387.311 317.436 377.984 317.436 366.479V230.903H317.392Z" fill="white"/>
|
||||
<path d="M259.477 133.498C228.549 89.233 167.464 76.1126 123.3 104.251L45.7519 153.688C35.2703 160.278 26.2794 168.983 19.3542 179.246C12.429 189.51 7.72191 201.105 5.53403 213.292C1.83501 233.816 5.08752 254.988 14.7767 273.455C8.13637 283.526 3.60719 294.84 1.46288 306.711C-0.746055 319.142 -0.450838 331.888 2.33122 344.203C5.11328 356.518 10.3262 368.154 17.6642 378.428C48.5919 422.699 109.68 435.822 153.838 407.675L231.404 358.239C241.883 351.647 250.871 342.942 257.794 332.678C264.716 322.415 269.42 310.82 271.604 298.634C275.309 278.112 272.065 256.94 262.386 238.468C269.021 228.396 273.547 217.084 275.69 205.215C277.902 192.785 277.609 180.038 274.827 167.722C272.045 155.407 266.832 143.771 259.492 133.498" fill="#FF3E00"/>
|
||||
<path d="M115.768 382.594C103.561 385.767 90.6753 385.118 78.8495 380.734C67.0238 376.351 56.8281 368.444 49.6391 358.081C45.2269 351.904 42.0925 344.908 40.4196 337.504C38.7467 330.099 38.5689 322.435 39.8969 314.961C40.3463 312.507 40.9653 310.088 41.7495 307.72L43.2097 303.259L47.1857 306.176C56.3596 312.922 66.618 318.051 77.5186 321.343L80.4002 322.217L80.1356 325.096C79.7855 329.186 80.8948 333.269 83.267 336.619C85.4316 339.743 88.5033 342.127 92.0667 343.449C95.6301 344.77 99.5134 344.966 103.192 344.009C104.874 343.562 106.479 342.86 107.95 341.927L185.513 292.485C187.411 291.29 189.039 289.713 190.294 287.854C191.549 285.994 192.403 283.894 192.802 281.687C193.199 279.434 193.144 277.124 192.64 274.892C192.136 272.66 191.192 270.551 189.863 268.688C187.698 265.565 184.627 263.181 181.064 261.858C177.501 260.535 173.618 260.338 169.939 261.292C168.256 261.74 166.652 262.442 165.181 263.374L135.582 282.243C130.711 285.338 125.396 287.67 119.821 289.16C107.615 292.333 94.7285 291.684 82.9026 287.301C71.0768 282.917 60.8811 275.01 53.6924 264.647C49.279 258.47 46.1435 251.474 44.4701 244.07C42.7966 236.665 42.6189 229.001 43.9472 221.527C45.2653 214.199 48.0983 207.227 52.265 201.056C56.4318 194.886 61.8404 189.653 68.1451 185.692L145.717 136.258C150.587 133.159 155.902 130.824 161.478 129.332C173.685 126.159 186.571 126.809 198.397 131.192C210.222 135.576 220.418 143.483 227.607 153.846C232.02 160.023 235.155 167.018 236.828 174.423C238.502 181.827 238.68 189.492 237.352 196.966C236.899 199.419 236.279 201.838 235.497 204.207L234.036 208.668L230.063 205.756C220.89 199.01 210.632 193.881 199.731 190.59L196.846 189.716L197.114 186.837C197.463 182.744 196.355 178.66 193.985 175.305C191.82 172.181 188.749 169.797 185.185 168.476C181.622 167.154 177.739 166.958 174.061 167.915C172.378 168.362 170.773 169.064 169.302 169.996L91.7186 219.442C89.8207 220.636 88.1927 222.213 86.9387 224.071C85.6847 225.93 84.8322 228.03 84.4357 230.237C84.0359 232.49 84.0893 234.801 84.5928 237.033C85.0962 239.265 86.0397 241.375 87.3679 243.238C89.5332 246.362 92.6047 248.746 96.1676 250.068C99.7306 251.391 103.614 251.588 107.292 250.634C108.975 250.186 110.579 249.484 112.051 248.553L141.646 229.693C146.515 226.593 151.83 224.258 157.407 222.77C169.613 219.597 182.499 220.246 194.324 224.629C206.15 229.013 216.345 236.92 223.533 247.283C227.946 253.459 231.082 260.455 232.755 267.86C234.429 275.265 234.606 282.929 233.278 290.403C231.963 297.731 229.133 304.704 224.97 310.876C220.807 317.048 215.402 322.284 209.101 326.25L131.529 375.668C126.66 378.768 121.344 381.104 115.768 382.594Z" fill="white"/>
|
||||
<g clip-path="url(#clip0_13_1060)">
|
||||
<g clip-path="url(#clip1_13_1060)">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M460.943 364.935C457.828 370.79 459.637 378.159 465.324 381.571V381.571C471.006 384.98 478.403 383.151 481.544 377.317C533.752 280.363 518.924 156.846 437.058 74.9807C355.193 -6.88471 231.676 -21.7134 134.722 30.4946C128.888 33.6358 127.059 41.0332 130.468 46.7145V46.7145C133.88 52.4018 141.249 54.2111 147.104 51.0956C234.816 4.42589 346.181 18.0444 420.088 91.9512C493.994 165.858 507.613 277.223 460.943 364.935Z" fill="url(#paint0_linear_13_1060)"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M413.862 350.694C419.543 354.103 426.951 352.275 430.007 346.396C468.403 272.551 456.612 179.387 394.631 117.407C332.651 55.4269 239.488 43.6349 165.642 82.0313C159.764 85.0878 157.935 92.495 161.344 98.1763V98.1763C164.756 103.864 172.122 105.662 178.033 102.654C242.623 69.7813 323.639 80.356 377.661 134.378C431.682 188.399 442.257 269.416 409.385 334.006C406.376 339.917 408.175 347.282 413.862 350.694V350.694Z" fill="url(#paint1_linear_13_1060)"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M362.401 319.818C368.082 323.226 375.505 321.405 378.4 315.446C403.031 264.752 394.3 201.928 352.205 159.834C310.111 117.739 247.287 109.008 196.593 133.639C190.634 136.534 188.813 143.957 192.221 149.638V149.638C195.634 155.326 202.994 157.1 209.014 154.317C250.417 135.175 301.101 142.671 335.235 176.804C369.368 210.938 376.864 261.622 357.722 303.025C354.939 309.045 356.713 316.405 362.401 319.818V319.818Z" fill="url(#paint2_linear_13_1060)"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M310.939 288.94C316.621 292.349 324.091 290.53 326.562 284.382C337.575 256.981 331.981 224.461 309.779 202.26C287.578 180.058 255.058 174.464 227.657 185.477C221.509 187.948 219.69 195.418 223.099 201.099V201.099C226.511 206.787 233.881 208.474 240.2 206.459C258.142 200.739 278.575 204.996 292.809 219.23C307.043 233.464 311.3 253.897 305.58 271.839C303.565 278.158 305.252 285.527 310.939 288.94V288.94Z" fill="url(#paint3_linear_13_1060)"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M266.333 262.177C268.227 263.313 270.74 262.702 271.371 260.585C272.995 255.135 271.656 248.99 267.352 244.686C263.049 240.383 256.903 239.043 251.453 240.668C249.337 241.299 248.725 243.811 249.862 245.705V245.705C250.999 247.601 253.51 248.094 255.719 248.006C257.871 247.921 260.052 248.7 261.695 250.343C263.339 251.987 264.118 254.167 264.032 256.32C263.944 258.529 264.438 261.039 266.333 262.177V262.177Z" fill="url(#paint4_linear_13_1060)"/>
|
||||
</g>
|
||||
<g clip-path="url(#clip2_13_1060)">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M51.1339 147.065C54.2494 141.21 52.4401 133.841 46.7528 130.429V130.429C41.0715 127.02 33.6741 128.849 30.5329 134.683C-21.6751 231.637 -6.8464 355.154 75.019 437.019C156.884 518.885 280.401 533.713 377.355 481.505C383.189 478.364 385.018 470.967 381.61 465.286V465.286C378.197 459.598 370.828 457.789 364.973 460.904C277.261 507.574 165.896 493.956 91.9895 420.049C18.0827 346.142 4.4642 234.777 51.1339 147.065Z" fill="url(#paint5_linear_13_1060)"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M98.215 161.306C92.5337 157.897 85.1265 159.725 82.0701 165.604C43.6737 239.449 55.4656 332.613 117.446 394.593C179.426 456.573 272.589 468.365 346.435 429.969C352.313 426.912 354.142 419.505 350.733 413.824V413.824C347.321 408.136 339.955 406.338 334.044 409.346C269.454 442.219 188.438 431.644 134.416 377.622C80.3947 323.601 69.8201 242.584 102.692 177.994C105.701 172.083 103.902 164.718 98.215 161.306V161.306Z" fill="url(#paint6_linear_13_1060)"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M149.676 192.182C143.995 188.774 136.573 190.595 133.677 196.554C109.046 247.248 117.777 310.072 159.872 352.166C201.966 394.261 264.79 402.992 315.484 378.361C321.443 375.466 323.264 368.043 319.856 362.362V362.362C316.443 356.674 309.083 354.9 303.063 357.683C261.66 376.825 210.976 369.329 176.842 335.196C142.709 301.062 135.213 250.378 154.355 208.975C157.139 202.955 155.364 195.595 149.676 192.182V192.182Z" fill="url(#paint7_linear_13_1060)"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M201.138 223.06C195.456 219.651 187.986 221.47 185.516 227.618C174.502 255.019 180.097 287.539 202.298 309.74C224.499 331.942 257.019 337.536 284.42 326.523C290.568 324.052 292.387 316.582 288.978 310.901V310.901C285.566 305.213 278.196 303.526 271.877 305.541C253.935 311.261 233.502 307.004 219.268 292.77C205.035 278.536 200.778 258.103 206.498 240.161C208.512 233.842 206.825 226.473 201.138 223.06V223.06Z" fill="url(#paint8_linear_13_1060)"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M245.744 249.823C243.85 248.687 241.337 249.298 240.706 251.415C239.082 256.865 240.421 263.01 244.725 267.314C249.029 271.617 255.174 272.957 260.624 271.332C262.74 270.701 263.352 268.189 262.216 266.295V266.295C261.078 264.399 258.567 263.906 256.358 263.994C254.206 264.079 252.025 263.3 250.382 261.657C248.738 260.013 247.959 257.833 248.045 255.68C248.133 253.471 247.639 250.961 245.744 249.823V249.823Z" fill="url(#paint9_linear_13_1060)"/>
|
||||
</g>
|
||||
<path d="M364.198 146.539C336.555 106.975 281.957 95.2476 242.484 120.397L173.171 164.584C163.803 170.474 155.767 178.255 149.577 187.428C143.387 196.602 139.18 206.966 137.225 217.858C133.918 236.202 136.826 255.125 145.486 271.632C139.551 280.633 135.502 290.745 133.586 301.356C131.611 312.466 131.875 323.859 134.362 334.866C136.849 345.873 141.508 356.273 148.067 365.456C175.71 405.025 230.31 416.755 269.778 391.597L339.107 347.411C348.473 341.52 356.506 333.738 362.693 324.565C368.88 315.392 373.085 305.028 375.037 294.137C378.349 275.794 375.449 256.87 366.798 240.36C372.729 231.358 376.774 221.248 378.689 210.639C380.666 199.529 380.404 188.135 377.918 177.128C375.431 166.121 370.771 155.721 364.211 146.539" fill="#FF3E00"/>
|
||||
<path d="M364.198 146.539C336.555 106.975 281.957 95.2476 242.484 120.397L173.171 164.584C163.803 170.474 155.767 178.255 149.577 187.428C143.387 196.602 139.18 206.966 137.225 217.858C133.918 236.202 136.826 255.125 145.486 271.632C139.551 280.633 135.502 290.745 133.586 301.356C131.611 312.466 131.875 323.859 134.362 334.866C136.849 345.873 141.508 356.273 148.067 365.456C175.71 405.025 230.31 416.755 269.778 391.597L339.107 347.411C348.473 341.52 356.506 333.738 362.693 324.565C368.88 315.392 373.085 305.028 375.037 294.137C378.349 275.794 375.449 256.87 366.798 240.36C372.729 231.358 376.774 221.248 378.689 210.639C380.666 199.529 380.404 188.135 377.918 177.128C375.431 166.121 370.771 155.721 364.211 146.539" fill="url(#paint10_linear_13_1060)"/>
|
||||
<path d="M235.751 369.18C224.841 372.016 213.324 371.436 202.754 367.518C192.184 363.599 183.071 356.532 176.646 347.27C172.702 341.749 169.901 335.496 168.405 328.878C166.91 322.26 166.751 315.41 167.938 308.729C168.34 306.536 168.893 304.374 169.594 302.257L170.899 298.27L174.453 300.878C182.653 306.907 191.822 311.491 201.564 314.433L204.14 315.215L203.903 317.788C203.591 321.444 204.582 325.093 206.702 328.088C208.637 330.88 211.382 333.01 214.567 334.192C217.752 335.373 221.223 335.548 224.511 334.693C226.015 334.293 227.449 333.665 228.764 332.832L298.089 288.64C299.786 287.572 301.241 286.163 302.363 284.501C303.485 282.839 304.248 280.962 304.604 278.989C304.96 276.975 304.91 274.911 304.46 272.916C304.009 270.921 303.165 269.036 301.978 267.371C300.043 264.579 297.297 262.448 294.113 261.266C290.928 260.084 287.457 259.908 284.169 260.761C282.665 261.161 281.231 261.788 279.917 262.621L253.462 279.486C249.108 282.252 244.357 284.337 239.374 285.669C228.464 288.505 216.947 287.925 206.377 284.007C195.807 280.089 186.694 273.021 180.269 263.759C176.324 258.238 173.522 251.985 172.026 245.367C170.53 238.749 170.371 231.899 171.558 225.218C172.737 218.668 175.269 212.437 178.993 206.922C182.717 201.407 187.551 196.729 193.186 193.189L262.52 149.006C266.873 146.236 271.623 144.148 276.607 142.815C287.517 139.979 299.035 140.56 309.605 144.478C320.175 148.396 329.287 155.463 335.713 164.725C339.657 170.246 342.459 176.499 343.955 183.117C345.45 189.735 345.61 196.585 344.423 203.266C344.018 205.458 343.464 207.621 342.765 209.738L341.46 213.725L337.908 211.123C329.709 205.093 320.54 200.509 310.797 197.567L308.219 196.786L308.458 194.213C308.77 190.555 307.78 186.904 305.662 183.905C303.727 181.113 300.981 178.983 297.796 177.801C294.612 176.62 291.141 176.445 287.853 177.3C286.349 177.7 284.915 178.327 283.601 179.161L214.256 223.355C212.56 224.422 211.105 225.831 209.984 227.493C208.863 229.154 208.101 231.031 207.747 233.003C207.39 235.017 207.437 237.082 207.887 239.078C208.337 241.073 209.181 242.958 210.368 244.624C212.303 247.416 215.048 249.546 218.233 250.729C221.417 251.911 224.888 252.087 228.176 251.235C229.68 250.834 231.114 250.206 232.429 249.374L258.881 232.517C263.233 229.746 267.984 227.659 272.969 226.329C283.878 223.493 295.395 224.073 305.965 227.991C316.534 231.91 325.647 238.977 332.072 248.239C336.016 253.76 338.819 260.012 340.314 266.631C341.81 273.249 341.969 280.099 340.782 286.78C339.606 293.329 337.077 299.562 333.356 305.078C329.635 310.595 324.804 315.275 319.172 318.819L249.839 362.99C245.486 365.76 240.736 367.848 235.751 369.18Z" fill="white"/>
|
||||
</g>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_13_1060" x1="193.598" y1="-29.0621" x2="541.101" y2="318.441" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#FF3E00"/>
|
||||
<stop offset="1" stop-color="#FF0697" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear_13_1060" x1="208.5" y1="37.8468" x2="473.681" y2="303.458" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#FF0697"/>
|
||||
<stop offset="1" stop-color="#AC06FF" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint2_linear_13_1060" x1="223.306" y1="106.083" x2="405.956" y2="288.733" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#AC06FF"/>
|
||||
<stop offset="1" stop-color="#5A00CC" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint3_linear_13_1060" x1="238.284" y1="173.491" x2="338.36" y2="273.72" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#FF0697"/>
|
||||
<stop offset="1" stop-color="#AC06FF" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint4_linear_13_1060" x1="252.468" y1="239.138" x2="272.901" y2="259.571" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#FF3E00"/>
|
||||
<stop offset="1" stop-color="#FF0697" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint5_linear_13_1060" x1="318.479" y1="541.062" x2="-29.0238" y2="193.559" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#FF3E00"/>
|
||||
<stop offset="1" stop-color="#FF0697" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint6_linear_13_1060" x1="303.577" y1="474.153" x2="38.3958" y2="208.542" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#FF0697"/>
|
||||
<stop offset="1" stop-color="#AC06FF" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint7_linear_13_1060" x1="288.771" y1="405.917" x2="106.121" y2="223.267" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#AC06FF"/>
|
||||
<stop offset="1" stop-color="#5A00CC" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint8_linear_13_1060" x1="273.794" y1="338.509" x2="173.717" y2="238.28" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#FF0697"/>
|
||||
<stop offset="1" stop-color="#AC06FF" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint9_linear_13_1060" x1="259.609" y1="272.862" x2="239.176" y2="252.429" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#FF3E00"/>
|
||||
<stop offset="1" stop-color="#FF0697" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint10_linear_13_1060" x1="379.74" y1="107.15" x2="132.278" y2="404.849" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#FF3E00"/>
|
||||
<stop offset="0.510417" stop-color="#FF0697"/>
|
||||
<stop offset="1" stop-color="#AC06FF"/>
|
||||
</linearGradient>
|
||||
<clipPath id="clip0_13_1060">
|
||||
<rect width="512" height="512" fill="white"/>
|
||||
</clipPath>
|
||||
<clipPath id="clip1_13_1060">
|
||||
<rect width="512" height="512" fill="white" transform="translate(-106 256) rotate(-45)"/>
|
||||
</clipPath>
|
||||
<clipPath id="clip2_13_1060">
|
||||
<rect width="512" height="512" fill="white" transform="translate(618.077 256) rotate(135)"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 4.5 KiB After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 309 KiB |
|
After Width: | Height: | Size: 844 KiB |