Initial Docs (#39)
This commit is contained in:
@@ -0,0 +1,58 @@
|
||||
# Hello World
|
||||
|
||||
this is some content from @OllieJT on pr #32
|
||||
|
||||
this is on https://github.com/OllieJT/svelte-podcast/pull/39
|
||||
|
||||
> this is a quote
|
||||
|
||||
## this is a subheading
|
||||
|
||||
`const this_is = "code"`
|
||||
|
||||
```js
|
||||
const this_is = 'code_block';
|
||||
console.log(this_is);
|
||||
```
|
||||
|
||||
```js {1,3-4} showLineNumbers
|
||||
function fancyAlert(arg) {
|
||||
if (arg) {
|
||||
$.facebox({ div: '#foo' });
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```svelte {1,3-4} showLineNumbers
|
||||
<script>
|
||||
import { episode_audio } from 'svelte-podcast';
|
||||
</script>
|
||||
|
||||
<!-- load the episode on click -->
|
||||
<button
|
||||
on:click={() =>
|
||||
episode_audio.load('/episode-audio.mp3', {
|
||||
/* optional metadata */
|
||||
})}
|
||||
>
|
||||
Load Episode
|
||||
</button>
|
||||
|
||||
<!-- unload the episode on click -->
|
||||
<button on:click={() => episode_audio.unload()}>Unload Episode</button>
|
||||
```
|
||||
|
||||
[This is a link](https://www.google.com)
|
||||
|
||||
- this is a list
|
||||
- this is a list
|
||||
- this is a list
|
||||
|
||||
1. this is a numbered list
|
||||
2. this is a numbered list
|
||||
3. this is a numbered list
|
||||
|
||||
| this | is | a | table |
|
||||
| ---- | --- | --- | ----- |
|
||||
| this | is | a | table |
|
||||
| this | is | a | table |
|
||||
@@ -0,0 +1,16 @@
|
||||
<script lang="ts">
|
||||
import { topics } from '$content/utility/anchor-registry';
|
||||
import { Hashtag } from '@inqling/svelte-icons/heroicon-24-outline';
|
||||
|
||||
export let value: string;
|
||||
$: id = topics.slugify(value || '');
|
||||
topics.add(value);
|
||||
</script>
|
||||
|
||||
<a href="/#{id}" class="group relative -ml-[1em] hover:text-black">
|
||||
<Hashtag class="h-[1em] w-[1em] -translate-x-1 text-mono-300 group-hover:text-primary-600" /><span
|
||||
class="underline decoration-primary-50/0 decoration-4 group-hover:decoration-primary-500"
|
||||
>{value}</span
|
||||
>
|
||||
<span class="pointer-events-none absolute -top-10 h-px w-px" {id} />
|
||||
</a>
|
||||
@@ -0,0 +1,19 @@
|
||||
<script lang="ts">
|
||||
import Anchor from './anchor.svelte';
|
||||
|
||||
export let title: string;
|
||||
export let anchor: boolean = false;
|
||||
</script>
|
||||
|
||||
<li class="list-item list-disc text-primary-800">
|
||||
<h4 class="text-lg font-medium leading-normal text-primary-600">
|
||||
{#if anchor}
|
||||
<Anchor value={title} />
|
||||
{:else}
|
||||
{title}
|
||||
{/if}
|
||||
</h4>
|
||||
<p class="mt-0 text-mono-600">
|
||||
<slot />
|
||||
</p>
|
||||
</li>
|
||||
@@ -0,0 +1,11 @@
|
||||
<script lang="ts">
|
||||
import Anchor from './anchor.svelte';
|
||||
|
||||
export let title: string;
|
||||
export let subtitle: string | undefined = undefined;
|
||||
</script>
|
||||
|
||||
<h3 class="text-xl font-medium leading-relaxed text-mono-900"><Anchor value={title} /></h3>
|
||||
{#if subtitle}
|
||||
<p class="text-xl font-normal leading-normal text-mono-500">{subtitle}</p>
|
||||
{/if}
|
||||
@@ -0,0 +1,8 @@
|
||||
<script lang="ts">
|
||||
</script>
|
||||
|
||||
<article
|
||||
class="section-content my-4 w-full border-t-2 border-mono-100 py-4 text-mono-600 first-of-type:border-t-0 first-of-type:pt-0"
|
||||
>
|
||||
<slot />
|
||||
</article>
|
||||
@@ -0,0 +1,29 @@
|
||||
<script lang="ts">
|
||||
import type { SvelteIcon } from '@inqling/svelte-icons';
|
||||
|
||||
type Feature = {
|
||||
icon: SvelteIcon;
|
||||
title: string;
|
||||
description: string;
|
||||
};
|
||||
|
||||
export let features: Feature[];
|
||||
</script>
|
||||
|
||||
<dl
|
||||
class="mx-auto grid max-w-2xl grid-cols-1 gap-6 py-6 text-base leading-7 text-mono-600 sm:grid-cols-2 sm:gap-y-10 lg:mx-0 lg:max-w-none"
|
||||
>
|
||||
{#each features as feature}
|
||||
<div class="relative pl-9">
|
||||
<dt class="inline font-semibold text-mono-900">
|
||||
<svelte:component
|
||||
this={feature.icon}
|
||||
class="absolute top-1 left-1 h-5 w-5 text-primary-600"
|
||||
/>
|
||||
|
||||
{feature.title}
|
||||
</dt>
|
||||
<dd class="inline">{feature.description}</dd>
|
||||
</div>
|
||||
{/each}
|
||||
</dl>
|
||||
@@ -0,0 +1,6 @@
|
||||
export { default as ContentListItem } from './content-list-item.svelte';
|
||||
export { default as ContentTitle } from './content-title.svelte';
|
||||
export { default as Content } from './content.svelte';
|
||||
export { default as Features } from './features.svelte';
|
||||
export { default as SectionTitle } from './section-title.svelte';
|
||||
export { default as Section } from './section.svelte';
|
||||
@@ -0,0 +1,9 @@
|
||||
<script lang="ts">
|
||||
import Anchor from './anchor.svelte';
|
||||
|
||||
export let title: string;
|
||||
</script>
|
||||
|
||||
<h2 class="text-3xl font-normal leading-loose text-primary-600">
|
||||
<Anchor value={title} />
|
||||
</h2>
|
||||
@@ -0,0 +1,9 @@
|
||||
<script lang="ts">
|
||||
</script>
|
||||
|
||||
<!-- <div class="relative mx-auto w-full max-w-7xl p-4 sm:px-6 lg:px-8"> -->
|
||||
<div class="relative mx-auto w-full max-w-prose p-4 text-lg sm:px-6 lg:px-8">
|
||||
<section class="w-full">
|
||||
<slot />
|
||||
</section>
|
||||
</div>
|
||||
@@ -0,0 +1,184 @@
|
||||
## Table of contents
|
||||
|
||||
## What is svelte-podcast?
|
||||
|
||||
### Motivation
|
||||
|
||||
At it's core svelte-podcast provides tooling to make the following tasks easier:
|
||||
|
||||
#### I want to build a custom audio player
|
||||
|
||||
The default audio element is quite nice, but there are 2 main reasons why people want to build their own;
|
||||
|
||||
1. to customize the look and feel
|
||||
2. to add custom functionality.
|
||||
|
||||
svelte-podcast provides a simple API enabling you to resolve both of these desires.
|
||||
|
||||
#### I find managing the state of audio difficult
|
||||
|
||||
It's one thing to load, play, and pause audio on a single page, but what if you want to build more custom or advanced behaviours like
|
||||
|
||||
> Can audio continue playing when the user navigates to a new page.
|
||||
|
||||
> Can I play/pause an episode from content like an article?
|
||||
|
||||
> Can I highlight show notes relevant to the current timestamp?
|
||||
|
||||
svelte-podcast makes solving these challenges easier by simplifying your interactions with the audio element.
|
||||
|
||||
#### I find RSS frustrating to work with
|
||||
|
||||
We're still working on this, it'll be coming soon™️!
|
||||
|
||||
### Features
|
||||
|
||||
| Feature | Description |
|
||||
| ------------------- | ---------------------------------------------------------------------------------------------- |
|
||||
| **Simple API** | Get essential audio data like the current timestamp, duration, play state and more... |
|
||||
| **Simple Controls** | Control your active audio source with play, pause, seek, and more methods... |
|
||||
| **Extensible** | Define your own metadata for each episode to easily association content with the current audio |
|
||||
| **Save Progress** | Save and load a users progress and preferences to localStorage, or your own database |
|
||||
| **Persistence** | If your site makes use of client side routing, audio will continue playing when users navigate |
|
||||
| **Typescript** | 1st class types, with type overrides for defining your own metadata requirements |
|
||||
|
||||
## Get Started
|
||||
|
||||
### Install
|
||||
|
||||
Install the latest version of svelte-podcast with your preferred package manager.
|
||||
|
||||
```sh
|
||||
# with npm
|
||||
npm install svelte-podcast@latest
|
||||
|
||||
# with yarn
|
||||
yarn add svelte-podcast@latest
|
||||
|
||||
# with pnpm
|
||||
pnpm add svelte-podcast@latest
|
||||
```
|
||||
|
||||
### Setup
|
||||
|
||||
Add the AudioLoader component to your root layout. You must have one of these for svelte-podcast to work. You should also only load one instance of this at a time, and so we recommend you loading it at the base of your app.
|
||||
|
||||
```svelte
|
||||
<!-- /routes/+layout.svelte -->
|
||||
<script>
|
||||
import { AudioLoader } from 'svelte-podcast';
|
||||
</script>
|
||||
|
||||
<AudioLoader />
|
||||
|
||||
<!-- your layout -->
|
||||
|
||||
<slot />
|
||||
```
|
||||
|
||||
### Load an episode
|
||||
|
||||
All you need to load an episode is a URL to an audio file. svelte-podcast uses a html audio element under the hood, so any audio file compatible with the autio element is also compatible with this package.
|
||||
|
||||
#### Using a URL (Most Common)
|
||||
|
||||
An **audio url** could be a URL to an MP3 file from an RSS feed, like this: `https://media.transistor.fm/27a058c9/27b595e2.mp3`. It could also be a path to a static file on your server.
|
||||
|
||||
#### Using a static file
|
||||
|
||||
If you're using SvelteKit, you can store **static files** in the /static directory. When your site is built, everything in the static directory will be at the root of your site. If you have a file in `/static/episides/episode-01.mp3` you could load it as `/episides/episode-01.mp3`
|
||||
|
||||
#### Using the AudioLoader component
|
||||
|
||||
Only one piece of audio can be loaded at a time, however a users progress for each audio is saved in localStorage when they play, pause, or skip. You can load audio via the `episode_audio` store from anywhere in your project.
|
||||
|
||||
##### Example: Load audio after page is loaded
|
||||
|
||||
```svelte
|
||||
<script>
|
||||
import { onMount } from 'svelte';
|
||||
import { episode_audio } from 'svelte-podcast';
|
||||
|
||||
onMount(() => {
|
||||
// load the episode on mount without any metadata
|
||||
episode_audio.load('/episode-audio.mp3', {
|
||||
/* optional metadata */
|
||||
});
|
||||
});
|
||||
</script>
|
||||
```
|
||||
|
||||
##### Example: Load audio after a user clicks a button
|
||||
|
||||
```svelte
|
||||
<script>
|
||||
import { episode_audio } from 'svelte-podcast';
|
||||
</script>
|
||||
|
||||
<!-- load the episode on click -->
|
||||
<button
|
||||
on:click={() =>
|
||||
episode_audio.load('/episode-audio.mp3', {
|
||||
/* optional metadata */
|
||||
})}
|
||||
>
|
||||
Load Episode
|
||||
</button>
|
||||
|
||||
<!-- unload the episode on click -->
|
||||
<button on:click={() => episode_audio.unload()}>Unload Episode</button>
|
||||
```
|
||||
|
||||
## Components
|
||||
|
||||
### `<PlayerWidget/>`
|
||||
|
||||
Coming soon...
|
||||
|
||||
### `<PlayerStack/>`
|
||||
|
||||
Coming soon...
|
||||
|
||||
### `<HeadlessTimeline/>`
|
||||
|
||||
Coming soon...
|
||||
|
||||
## Utilities
|
||||
|
||||
### Debug Mode
|
||||
|
||||
Coming soon...
|
||||
|
||||
### audio
|
||||
|
||||
Coming soon...
|
||||
|
||||
### episode_progress
|
||||
|
||||
Coming soon...
|
||||
|
||||
### episode_details
|
||||
|
||||
Coming soon...
|
||||
|
||||
### user_progress & user_preferences
|
||||
|
||||
Coming soon...
|
||||
|
||||
### save_podcast_state
|
||||
|
||||
Coming soon...
|
||||
|
||||
### secondsToTimestamp
|
||||
|
||||
Coming soon...
|
||||
|
||||
## Typescript
|
||||
|
||||
### Override types
|
||||
|
||||
Coming soon...
|
||||
|
||||
### Exported types
|
||||
|
||||
Coming soon...
|
||||
@@ -0,0 +1,14 @@
|
||||
import { assets } from '$app/paths';
|
||||
|
||||
export const episodes = {
|
||||
syntax: {
|
||||
src: `${assets}/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: `${assets}/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;
|
||||
@@ -0,0 +1,9 @@
|
||||
<script>
|
||||
import { AudioLoader } from 'svelte-podcast';
|
||||
</script>
|
||||
|
||||
<AudioLoader />
|
||||
|
||||
<!-- your layout -->
|
||||
|
||||
<slot />
|
||||
@@ -0,0 +1,5 @@
|
||||
import example_add_loader from './add-loader.svelte?raw';
|
||||
import example_load_audio_click from './load-audio-click.svelte?raw';
|
||||
import example_load_audio_mount from './load-audio-mount.svelte?raw';
|
||||
|
||||
export { example_add_loader, example_load_audio_click, example_load_audio_mount };
|
||||
@@ -0,0 +1,16 @@
|
||||
<script>
|
||||
import { episode_audio } from 'svelte-podcast';
|
||||
</script>
|
||||
|
||||
<!-- load the episode on click -->
|
||||
<button
|
||||
on:click={() =>
|
||||
episode_audio.load('/episode-audio.mp3', {
|
||||
/* optional metadata */
|
||||
})}
|
||||
>
|
||||
Load Episode
|
||||
</button>
|
||||
|
||||
<!-- unload the episode on click -->
|
||||
<button on:click={() => episode_audio.unload()}>Unload Episode</button>
|
||||
@@ -0,0 +1,11 @@
|
||||
<script>
|
||||
import { onMount } from 'svelte';
|
||||
import { episode_audio } from 'svelte-podcast';
|
||||
|
||||
onMount(() => {
|
||||
// load the episode on mount without any metadata
|
||||
episode_audio.load('/episode-audio.mp3', {
|
||||
/* optional metadata */
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,36 @@
|
||||
import { writable } from 'svelte/store';
|
||||
|
||||
type Topic = string;
|
||||
|
||||
const content = new Set<Topic>();
|
||||
|
||||
const ropic_registry = writable<Topic[]>([...content]);
|
||||
|
||||
function add(value: Topic) {
|
||||
ropic_registry.update((prev) => {
|
||||
const content = new Set<Topic>(prev.filter(Boolean));
|
||||
Boolean(value) && content.add(value);
|
||||
return [...content];
|
||||
});
|
||||
}
|
||||
|
||||
function slugify(value: string) {
|
||||
// replace all apaces with dashes
|
||||
const slug = value
|
||||
.replace(/\s+/g, '-')
|
||||
.toLowerCase()
|
||||
.trim()
|
||||
.replace(/[^\x00-\x7F]/g, '-')
|
||||
.replace(/[^a-z0-9-]/g, '-')
|
||||
.replace(/^-+/, '')
|
||||
.replace(/-+$/, '')
|
||||
.replace(/--+/g, '-');
|
||||
|
||||
return encodeURI(slug);
|
||||
}
|
||||
|
||||
export const topics = {
|
||||
subscribe: ropic_registry.subscribe,
|
||||
add,
|
||||
slugify,
|
||||
};
|
||||
@@ -0,0 +1,8 @@
|
||||
import type { CodeBlockProps } from 'svhighlight/code/CodeBlock.svelte';
|
||||
|
||||
export const code_opts = {
|
||||
background: 'bg-mono-900',
|
||||
focusType: 'highlight',
|
||||
highlightColor: 'bg-mono-800',
|
||||
headerClasses: 'bg-mono-800',
|
||||
} satisfies CodeBlockProps;
|
||||
Reference in New Issue
Block a user