master 2a67a6834ccb cached
48 files
91.2 KB
27.1k tokens
24 symbols
1 requests
Download .txt
Repository: vojtaholik/gatsby-theme-simplecast
Branch: master
Commit: 2a67a6834ccb
Files: 48
Total size: 91.2 KB

Directory structure:
gitextract_3x8h2ftp/

├── .gitignore
├── LICENSE
├── README.md
├── demo/
│   ├── content/
│   │   └── episodes/
│   │       ├── 1/
│   │       │   └── index.md
│   │       ├── 2/
│   │       │   └── index.md
│   │       ├── 3/
│   │       │   └── index.md
│   │       ├── 4/
│   │       │   └── index.md
│   │       ├── 5/
│   │       │   └── index.md
│   │       ├── 6/
│   │       │   └── index.md
│   │       ├── 7/
│   │       │   └── index.md
│   │       └── 8/
│   │           └── index.md
│   ├── gatsby-config.js
│   ├── package.json
│   └── src/
│       ├── @vojtaholik/
│       │   └── gatsby-theme-simplecast/
│       │       └── lib/
│       │           └── config/
│       │               └── index.js
│       ├── gatsby-plugin-theme-ui/
│       │   └── index.js
│       └── pages/
│           ├── 404.js
│           ├── index.js
│           └── page-2.js
├── gatsby-theme-simplecast/
│   ├── .gitignore
│   ├── .prettierrc
│   ├── LICENSE
│   ├── README.md
│   ├── data/
│   │   └── mockupEpisodes.json
│   ├── gatsby-browser.js
│   ├── gatsby-config.js
│   ├── gatsby-node.js
│   ├── gatsby-ssr.js
│   ├── index.js
│   ├── package.json
│   └── src/
│       ├── components/
│       │   ├── aside.js
│       │   ├── bars.js
│       │   ├── context.js
│       │   ├── header.js
│       │   ├── image.js
│       │   ├── layout.css
│       │   ├── layout.js
│       │   ├── link.js
│       │   ├── navigation.js
│       │   ├── player.js
│       │   ├── seo.js
│       │   └── volumeBars.js
│       ├── gatsby-plugin-theme-ui/
│       │   └── index.js
│       ├── lib/
│       │   ├── config/
│       │   │   └── index.js
│       │   └── formatTime.js
│       ├── pages/
│       │   ├── 404.js
│       │   └── index.js
│       └── templates/
│           └── episode.js
└── package.json

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# Runtime data
pids
*.pid
*.seed
*.pid.lock

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# Coverage directory used by tools like istanbul
coverage

# nyc test coverage
.nyc_output

# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt

# Bower dependency directory (https://bower.io/)
bower_components

# node-waf configuration
.lock-wscript

# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release

# Dependency directories
node_modules/
jspm_packages/

# Typescript v1 declaration files
typings/

# Optional npm cache directory
.npm

# Optional eslint cache
.eslintcache

# Optional REPL history
.node_repl_history

# Output of 'npm pack'
*.tgz

# dotenv environment variables file
.env.*

# gatsby files
.cache/
public

# Mac files
.DS_Store

# Yarn
yarn-error.log
.pnp/
.pnp.js
# Yarn Integrity file
.yarn-integrity


================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2019 Vojta Holik

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: README.md
================================================
# Gatsby Theme Simplecast

### [→ Preview](https://gatsby-theme-simplecast.netlify.com)

<img src="https://cl.ly/c53006ad1d5d/ezgif.com-optimize%252520(1).gif" />

## Running demo
- `yarn`
- `yarn workspace demo develop`

## Using theme
- [Read me →](https://github.com/vojtaholik/gatsby-theme-simplecast/blob/master/gatsby-theme-simplecast/README.md)

================================================
FILE: demo/content/episodes/1/index.md
================================================
---
id: a
summary: 'Summary. Lorem ipsum dolor sit amet.'
resources: ['[Link](/)','[Link](/)','[Link](/)']
guestName: 'Anna Doe'
guestSummary: '[Twitter](/)'
image: null
guestPhoto: ./guest.png
---

Ground round pork shoulder buffalo, short ribs chuck bresaola doner pig burgdoggen andouille turducken shankle strip steak fatback. Beef alcatra swine boudin corned beef drumstick hamburger tri-tip shankle. Meatball leberkas strip steak, burgdoggen biltong swine boudin. T-bone short ribs sausage tri-tip, rump pancetta bacon filet mignon jowl turducken ham.

Tri-tip bacon cow rump. Andouille pig fatback kielbasa, venison bacon burgdoggen sirloin sausage tri-tip ground round frankfurter pork chop. Kielbasa shoulder hamburger salami drumstick pork tail landjaeger meatball burgdoggen ribeye pork belly cupim beef ribs. Biltong bacon bresaola, ribeye shank turducken pancetta beef ribs turkey t-bone. Biltong jerky buffalo venison ground round boudin. Picanha fatback t-bone, boudin capicola sirloin biltong.

================================================
FILE: demo/content/episodes/2/index.md
================================================
---
id: b
summary: 'Summary. Lorem ipsum dolor sit amet.'
image: './banner.png'
guestName: 'Charlie Doe'
guestSummary: 'Lorem ipsum dolor sit amet. [Twitter](/)'
guestPhoto: './guest.png'
---

Maecenas a augue vitae mauris interdum accumsan. Morbi auctor velit sed justo tempor, nec blandit purus iaculis. In hac habitasse platea dictumst. Ut in rutrum justo. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Nam ullamcorper libero id sapien eleifend, et ornare lectus rhoncus. Donec tempor dapibus metus, quis ornare diam rhoncus eget. Ut molestie cursus ornare. Aenean iaculis eget enim congue vestibulum. Curabitur imperdiet nisi quis interdum auctor.

Vivamus semper consectetur purus, at blandit nibh congue vel. Phasellus rhoncus, leo et commodo tempus, quam metus commodo tellus, sit amet interdum sem leo id nisl. Cras a pretium mauris. Sed nec justo ultricies, tincidunt lorem eu, accumsan dolor. Pellentesque eu auctor nisi, non tincidunt ante. Quisque lacinia dictum risus, a dignissim ipsum malesuada in. Morbi tristique risus metus, at porta mauris fermentum id. Quisque sit amet tincidunt enim, a auctor est. Morbi condimentum vestibulum ex non varius.

================================================
FILE: demo/content/episodes/3/index.md
================================================
---
id: c
summary: 'Summary. Lorem ipsum dolor sit amet.'
resources: ['[Badass: Making Users Awesome](https://www.goodreads.com/book/show/24737268-badass)',
'[Shambhala: The Sacred Path of the Warrior](https://www.goodreads.com/book/show/336248.Shambhala)']
guestName: 'Robert Doe'
guestSummary: '[Twitter](https://twitter.com/janelleallen?ref_src=twsrc%5Egoogle%7Ctwcamp%5Eserp%7Ctwgr%5Eauthor)'
guestPhoto: './guest.png'
image: './banner.png'
---

Flank pork belly leberkas pig jowl corned beef. Hamburger ham salami, venison pastrami strip steak spare ribs pork belly brisket. Bacon chicken tenderloin shankle tongue doner, corned beef shoulder burgdoggen cupim chuck pig turkey. Biltong brisket doner sirloin kielbasa short ribs porchetta prosciutto turkey leberkas ground round landjaeger spare ribs. Kevin strip steak tongue, bacon cupim chicken drumstick rump.

Short ribs jowl biltong corned beef chuck, shoulder landjaeger burgdoggen frankfurter sausage hamburger meatball pork loin. Sausage doner beef ribs salami beef shankle. Jerky ground round rump, cow strip steak short loin doner ham hock meatloaf porchetta andouille beef ribs. Tri-tip ribeye doner sirloin, frankfurter meatball tail beef drumstick shankle. Kielbasa pancetta ham, flank fatback jowl turkey boudin leberkas. Tongue drumstick shankle, strip steak picanha pork filet mignon leberkas andouille flank landjaeger short loin corned beef meatball.

================================================
FILE: demo/content/episodes/4/index.md
================================================
---
id: d
summary: 'Summary. Lorem ipsum dolor sit amet.'
image: './banner.png'
resources: ['[Link](/)', '[Link](/)', '[Link](/)']
---

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam vel volutpat nulla, vitae egestas dui. Donec sodales eros id purus gravida, quis egestas lorem mollis. Morbi sapien velit, finibus eget dapibus eget, auctor a justo. Etiam cursus congue luctus. Nam dui dolor, convallis vel hendrerit fringilla, interdum vitae odio. Vivamus at interdum nunc, sed cursus magna. Praesent malesuada hendrerit magna nec euismod. Curabitur placerat pellentesque tincidunt.

Donec odio justo, ornare ac massa vel, ullamcorper aliquam leo. Fusce facilisis diam vitae mi consequat suscipit. Nunc pharetra magna sit amet dui rhoncus malesuada. Integer hendrerit pellentesque lorem a pulvinar. Aenean at nunc tempus, semper nisi vitae, imperdiet nibh. Donec accumsan lacus ac sapien egestas aliquet. Sed lectus ex, auctor et varius quis, accumsan sed felis. Duis sit amet mattis nisi, sed tempor libero. Curabitur in tristique justo. Cras eu ipsum at nibh tristique iaculis. Suspendisse rhoncus laoreet fringilla. Cras tincidunt odio non facilisis fermentum. Quisque vel odio vel purus molestie pellentesque nec sit amet augue. Duis a neque sodales est pretium porttitor. Praesent at lacus rutrum, elementum dui ac, consequat mi. 

================================================
FILE: demo/content/episodes/5/index.md
================================================
---
id: e
summary: 'Summary. Lorem ipsum dolor sit amet.'
image: './banner.png'
---

Pancetta ham hock tongue t-bone pork loin. Boudin burgdoggen alcatra, buffalo beef meatball ham corned beef short loin flank pancetta. Pastrami short loin spare ribs, alcatra shoulder rump pork kielbasa fatback meatball doner swine ribeye. Leberkas tongue shank ball tip prosciutto flank turkey frankfurter pig hamburger. Corned beef swine short ribs boudin. Drumstick rump salami shank jerky flank meatloaf strip steak bresaola bacon brisket tail meatball t-bone pig. Capicola boudin salami porchetta beef turkey brisket.

Boudin buffalo chuck capicola. Short ribs meatloaf pork chop drumstick short loin pig frankfurter ground round meatball buffalo shoulder swine prosciutto tail ham. Venison meatball drumstick capicola, pig ribeye biltong fatback porchetta picanha sirloin. Ground round jowl porchetta shankle chuck pancetta. Kevin hamburger corned beef filet mignon tongue prosciutto beef ribs pancetta drumstick short loin chuck capicola. Shankle sausage porchetta shank. Sirloin short loin pork, ground round turkey ribeye chuck tail tri-tip t-bone buffalo bresaola.

================================================
FILE: demo/content/episodes/6/index.md
================================================
---
id: f
summary: 'Summary. Lorem ipsum dolor sit amet.'
image: './banner.png'
---

Ham salami biltong pastrami turkey frankfurter. Ham hock prosciutto pork, tenderloin beef ribs kielbasa swine biltong pancetta boudin turkey hamburger tongue strip steak jowl. Bresaola pork loin venison boudin. Shank pig porchetta boudin. Swine prosciutto short ribs kielbasa shankle ball tip flank ribeye biltong meatloaf salami bacon.

Brisket burgdoggen shank corned beef, chicken frankfurter boudin pork loin shankle porchetta jowl ground round sirloin alcatra biltong. Turkey drumstick porchetta alcatra pork jerky, turducken biltong fatback cupim tail. Jerky filet mignon capicola, leberkas jowl brisket venison. Rump jerky boudin prosciutto, sausage short loin chicken pastrami buffalo.

================================================
FILE: demo/content/episodes/7/index.md
================================================
---
id: g
summary: 'Summary. Lorem ipsum dolor sit amet.'
image: './banner.png'
---

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam vel volutpat nulla, vitae egestas dui. Donec sodales eros id purus gravida, quis egestas lorem mollis. Morbi sapien velit, finibus eget dapibus eget, auctor a justo. Etiam cursus congue luctus. Nam dui dolor, convallis vel hendrerit fringilla, interdum vitae odio. Vivamus at interdum nunc, sed cursus magna. Praesent malesuada hendrerit magna nec euismod. Curabitur placerat pellentesque tincidunt.

Donec odio justo, ornare ac massa vel, ullamcorper aliquam leo. Fusce facilisis diam vitae mi consequat suscipit. Nunc pharetra magna sit amet dui rhoncus malesuada. Integer hendrerit pellentesque lorem a pulvinar. Aenean at nunc tempus, semper nisi vitae, imperdiet nibh. Donec accumsan lacus ac sapien egestas aliquet. Sed lectus ex, auctor et varius quis, accumsan sed felis. Duis sit amet mattis nisi, sed tempor libero. Curabitur in tristique justo. Cras eu ipsum at nibh tristique iaculis. Suspendisse rhoncus laoreet fringilla. Cras tincidunt odio non facilisis fermentum. Quisque vel odio vel purus molestie pellentesque nec sit amet augue. Duis a neque sodales est pretium porttitor. Praesent at lacus rutrum, elementum dui ac, consequat mi.

================================================
FILE: demo/content/episodes/8/index.md
================================================
---
id: h
summary: 'Summary. Lorem ipsum dolor sit amet.'
image: './banner.png'
---

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam vel volutpat nulla, vitae egestas dui. Donec sodales eros id purus gravida, quis egestas lorem mollis. Morbi sapien velit, finibus eget dapibus eget, auctor a justo. Etiam cursus congue luctus. Nam dui dolor, convallis vel hendrerit fringilla, interdum vitae odio. Vivamus at interdum nunc, sed cursus magna. Praesent malesuada hendrerit magna nec euismod. Curabitur placerat pellentesque tincidunt.

Donec odio justo, ornare ac massa vel, ullamcorper aliquam leo. Fusce facilisis diam vitae mi consequat suscipit. Nunc pharetra magna sit amet dui rhoncus malesuada. Integer hendrerit pellentesque lorem a pulvinar. Aenean at nunc tempus, semper nisi vitae, imperdiet nibh. Donec accumsan lacus ac sapien egestas aliquet. Sed lectus ex, auctor et varius quis, accumsan sed felis. Duis sit amet mattis nisi, sed tempor libero. Curabitur in tristique justo. Cras eu ipsum at nibh tristique iaculis. Suspendisse rhoncus laoreet fringilla. Cras tincidunt odio non facilisis fermentum. Quisque vel odio vel purus molestie pellentesque nec sit amet augue. Duis a neque sodales est pretium porttitor. Praesent at lacus rutrum, elementum dui ac, consequat mi.

================================================
FILE: demo/gatsby-config.js
================================================
require('dotenv').config({
  path: `.env.${process.env.NODE_ENV}`,
})

module.exports = {
  plugins: [
    {
      resolve: '@vojtaholik/gatsby-theme-simplecast',
      options: {
        simplecastApiSecret: process.env.SIMPLECAST_API_SECRET,
        //podcastId: process.env.PODCAST_ID,
        markdownPath: 'content/episodes',
        episodeSlug: 'show',
      },
    },
    `gatsby-plugin-theme-ui`,
  ],
}


================================================
FILE: demo/package.json
================================================
{
  "private": true,
  "name": "demo",
  "version": "1.0.0",
  "license": "MIT",
  "scripts": {
    "build": "gatsby build",
    "develop": "gatsby develop",
    "clean": "gatsby clean"
  },
  "dependencies": {
    "@vojtaholik/gatsby-theme-simplecast": "^1.0.8",
    "gatsby": "^2.13.41",
    "gatsby-plugin-theme-ui": "^0.2.18",
    "gatsby-theme-ui": "^0.2.0",
    "react": "^16.8.6",
    "react-dom": "^16.8.6",
    "theme-ui": "^0.2.21"
  }
}


================================================
FILE: demo/src/@vojtaholik/gatsby-theme-simplecast/lib/config/index.js
================================================
export default {
  podcastSeason: '1',
  headerImageHeight: [300, 400],
  spotifyUrl: '/',
  applePodcastsUrl: '/',
  googlePodcastsUrl: '/',
}


================================================
FILE: demo/src/gatsby-plugin-theme-ui/index.js
================================================
export default {
  useCustomProperties: true,
  initialColorMode: 'dark',
  colors: {
    text: 'rgba(255, 255, 255, 0.9)',
    background: '#1A2232',
    backgroundLighten10: '#232B3B',
    backgroundLighten20: '#2C3648',
    primaryDarken: '#7A5EFF',
    primary: '#A085FF',
    primaryLighten10: '#9D82FF',
    primaryLighten50: '#B298FF',
    primaryLighten70: '#D2C8FF',
    secondary: '#85FFD0',
  },
}


================================================
FILE: demo/src/pages/404.js
================================================
import React from 'react'

export default function NotFound() {
  return (
    <div>
      <h1>404 - page not found.</h1>
    </div>
  )
}


================================================
FILE: demo/src/pages/index.js
================================================
import React from 'react'
import {graphql} from 'gatsby'
import IndexPage from '@vojtaholik/gatsby-theme-simplecast/src/pages/index'

export default function Index({data: {allEpisode, allMarkdownRemark}}) {
  return <IndexPage data={{allEpisode, allMarkdownRemark}} />
}

export const indexQuery = graphql`
  query {
    allEpisode {
      totalCount
      nodes {
        id
        title
        description
        number
        enclosure_url
        fields {
          slug
        }
      }
    }
    allMarkdownRemark {
      edges {
        node {
          html
          frontmatter {
            id
            title
            resources
            guestSummary
            guestName
            guestPhoto {
              childImageSharp {
                fluid(maxWidth: 200) {
                  ...GatsbyImageSharpFluid_noBase64
                }
              }
            }
            image {
              childImageSharp {
                original {
                  src
                }
                fluid(maxWidth: 700) {
                  ...GatsbyImageSharpFluid_noBase64
                }
              }
            }
          }
        }
      }
    }
  }
`


================================================
FILE: demo/src/pages/page-2.js
================================================
import React from 'react'

export default function Page() {
  return (
    <div>
      <h1>hello!</h1>
    </div>
  )
}


================================================
FILE: gatsby-theme-simplecast/.gitignore
================================================
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# Runtime data
pids
*.pid
*.seed
*.pid.lock

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# Coverage directory used by tools like istanbul
coverage

# nyc test coverage
.nyc_output

# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt

# Bower dependency directory (https://bower.io/)
bower_components

# node-waf configuration
.lock-wscript

# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release

# Dependency directories
node_modules/
jspm_packages/

# Typescript v1 declaration files
typings/

# Optional npm cache directory
.npm

# Optional eslint cache
.eslintcache

# Optional REPL history
.node_repl_history

# Output of 'npm pack'
*.tgz

# dotenv environment variables file
.env.development

# gatsby files
.cache/
public

# Mac files
.DS_Store

# Yarn
yarn-error.log
.pnp/
.pnp.js
# Yarn Integrity file
.yarn-integrity


================================================
FILE: gatsby-theme-simplecast/.prettierrc
================================================
{
  "endOfLine": "lf",
  "semi": false,
  "singleQuote": false,
  "tabWidth": 2,
  "trailingComma": "es5"
}


================================================
FILE: gatsby-theme-simplecast/LICENSE
================================================
The MIT License (MIT)

Copyright (c) 2015 gatsbyjs

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.



================================================
FILE: gatsby-theme-simplecast/README.md
================================================
# Gatsby Theme Simplecast

Gatsby theme that sources data from Simplecast API which can be combined with Markdown files to associate more information to each episode. Inspired by [syntax.fm](http://syntax.fm).

[→ Preview Theme](https://gatsby-theme-simplecast.netlify.com)

## Installation
To use this theme in your Gatsby sites, follow these instructions:

1. Install the theme
   ```  
   yarn add @vojtaholik/gatsby-theme-simplecast
   ```
2. Add the theme to your `gatsby-config.js`:
  ```
  module.exports = {
  plugins: [
    {
      resolve: '@vojtaholik/gatsby-theme-simplecast',
      options: {
        podcastId: PODCAST_ID, // theme uses mockup data if no podcastId provided
        simplecastApiSecret: SIMPLECAST_API_SECRET, 
        markdownPath: 'content/episodes',
      },
    },
  ],
}
  ```
Plugin options
   - `simplecastApiSecret`: Grab [your Simplecast API token here](https://dashboard.simplecast.com/account/private-apps).
   - `podcastId`: Podcast ID can be found in your Simplecast account under embeds settings.
   - `markdownPath`: Path to your markdown files. For markdown file to show up, it's `frontmatter.id` must match `episode.id`.
   - `episodeSlug`: default "show". (`/show/05/episode-title`)

3. Create index page in `src/pages/index.js` 
   - You can use [this example](https://github.com/vojtaholik/gatsby-theme-simplecast/blob/master/demo/src/pages/index.js) which displays latest episode by default.

4. Start your site
   ```
   gatsby develop
   ```


================================================
FILE: gatsby-theme-simplecast/data/mockupEpisodes.json
================================================
{
  "href": "https://api.simplecast.com/podcasts/2ac34cab-4949-40aa-bac7-d7e3a70f0a39/episodes?limit=10",
  "pages": {
    "total": 5,
    "previous": null,
    "next": {
      "href": "https://api.simplecast.com/podcasts/2ac34cab-4949-40aa-bac7-d7e3a70f0a39/episodes?limit=10&offset=10&private=true&status=importing%2Caudio_imported%2Ctranscoding%2Ctranscoding_error%2Cdraft%2Cscheduled%2Cpublished%2Cdirty%2Cprivate"
    },
    "limit": 10,
    "current": 1
  },
  "dashboard_link": "https://dashboard.simplecast.com/shows/2ac34cab-4949-40aa-bac7-d7e3a70f0a39/episodes",
  "create": null,
  "count": 47,
  "collection": [
    {
      "updated_at": "2019-07-07T21:18:37.988516-07:00",
      "type": "full",
      "token": "GRGoBLV0",
      "title": "Podcast Episode Title",
      "status": "published",
      "slug": "podcast-episode-1",
      "season": {
        "href": "https://api.simplecast.com/seasons/37157954-19df-4d38-9087-6fe015020f77",
        "number": 1
      },
      "scheduled_for": null,
      "published_at": "2019-07-07T21:18:37.988429-07:00",
      "number": 8,
      "is_hidden": false,
      "image_url": "https://cdn.simplecast.com/images/55d7f360-05c1-43af-9c2d-eb89e156734a/2965154a-b5d2-4479-ab85-6a8b06242014/j_c_hiatt.jpg",
      "image_path": "/prod/images/55d7f360-05c1-43af-9c2d-eb89e156734a/2965154a-b5d2-4479-ab85-6a8b06242014/j_c_hiatt.jpg",
      "id": "a",
      "href": "https://api.simplecast.com/episodes/39917452-ddc9-4b42-8471-a07fc4487277?preview=true",
      "guid": "a985054e-dc9b-45dd-93c9-3691c3690bec",
      "enclosure_url": "https://cl.ly/3ba8c8b8d9fe/Helping_Hands_ID_1202.mp3",
      "description": "Spicy jalapeno biltong kielbasa swine buffalo, prosciutto meatball shank. Tenderloin sirloin ground round, short ribs biltong cow tri-tip sausage buffalo andouille chicken turkey beef ribs shoulder. Tongue bresaola kielbasa picanha salami doner rump. Picanha corned beef tri-tip strip steak drumstick leberkas rump brisket pork loin sausage short ribs frankfurter. Hamburger jerky corned beef biltong kielbasa. Sirloin filet mignon shankle ribeye jerky pig. Spare ribs pork kielbasa tri-tip corned beef capicola.",
      "analytics": {
        "href": "https://api.simplecast.com/analytics/downloads?episode=39917452-ddc9-4b42-8471-a07fc4487277&preview=true"
      }
    },
    {
      "updated_at": "2019-06-12T12:44:03.792833-07:00",
      "type": "full",
      "token": "cMNsoKRl",
      "title": "Podcast Episode Title",
      "status": "published",
      "slug": "podcast-episode-2",
      "season": {
        "href": "https://api.simplecast.com/seasons/37157954-19df-4d38-9087-6fe015020f77",
        "number": 1
      },
      "scheduled_for": null,
      "published_at": "2019-05-31T17:22:47.299272-07:00",
      "number": 7,
      "is_hidden": false,
      "image_url": "https://cdn.simplecast.com/images/55d7f360-05c1-43af-9c2d-eb89e156734a/3d52cc0a-c791-4b98-95d1-1b2e45b005b1/Jason_Avatar.jpg",
      "image_path": "/prod/images/55d7f360-05c1-43af-9c2d-eb89e156734a/3d52cc0a-c791-4b98-95d1-1b2e45b005b1/Jason_Avatar.jpg",
      "id": "b",
      "href": "https://api.simplecast.com/episodes/7979994f-c577-4728-aa5e-4f2c1e9314ae?preview=true",
      "guid": "39623c8c-1401-45e9-a803-382f9136eb26",
      "enclosure_url": "https://cl.ly/195b9e8b7c60/Lobo_Loco_-_10_-_Pianoman_Play_Sofa_Again_ID_1159.mp3",
      "description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla at sagittis felis. Quisque lacus sapien, consectetur quis nibh nec, dapibus euismod mi. Mauris vel luctus libero. Donec semper suscipit nibh eu mattis. Donec aliquet lacinia laoreet. Ut gravida hendrerit orci eu hendrerit. Nam vel tristique ex.",
      "analytics": {
        "href": "https://api.simplecast.com/analytics/downloads?episode=7979994f-c577-4728-aa5e-4f2c1e9314ae&preview=true"
      }
    },
    {
      "updated_at": "2019-06-12T12:45:07.197070-07:00",
      "type": "full",
      "token": "edf78b39",
      "title": "Podcast Episode Title",
      "status": "published",
      "slug": "edf78b39",
      "season": {
        "href": "https://api.simplecast.com/seasons/37157954-19df-4d38-9087-6fe015020f77",
        "number": 1
      },
      "scheduled_for": null,
      "published_at": "2019-05-17T08:00:00.000000-07:00",
      "number": 6,
      "is_hidden": false,
      "image_url": "https://cdn.simplecast.com/images/2ac34c/2ac34cab-4949-40aa-bac7-d7e3a70f0a39/815fd97a-c688-43a0-89ec-adb51771d245/1558126643artwork.jpg",
      "image_path": "/images/2ac34c/2ac34cab-4949-40aa-bac7-d7e3a70f0a39/815fd97a-c688-43a0-89ec-adb51771d245/1558126643artwork.jpg",
      "id": "c",
      "href": "https://api.simplecast.com/episodes/815fd97a-c688-43a0-89ec-adb51771d245?preview=true",
      "guid": "471b8cf9-bd58-425a-9809-823c37e24389",
      "enclosure_url": "https://cl.ly/4a27ffd65e7c/Lobo_Loco_-_05_-_Overchill_ID_1140.mp3",
      "description": "Corned beef pork belly prosciutto tenderloin shank capicola. Swine spare ribs pork chop shank ham hock leberkas meatball. Prosciutto venison flank drumstick, bacon sausage pancetta leberkas meatloaf doner shoulder. Sausage ball tip pork chop alcatra, fatback brisket short loin.",
      "analytics": {
        "href": "https://api.simplecast.com/analytics/downloads?episode=815fd97a-c688-43a0-89ec-adb51771d245&preview=true"
      }
    },
    {
      "updated_at": "2019-06-12T13:00:33.648782-07:00",
      "type": "full",
      "token": "b14002cc",
      "title": "Podcast Episode Title",
      "status": "published",
      "slug": "b14002cc",
      "season": {
        "href": "https://api.simplecast.com/seasons/37157954-19df-4d38-9087-6fe015020f77",
        "number": 1
      },
      "scheduled_for": null,
      "published_at": "2019-05-13T18:59:00.000000-07:00",
      "number": 5,
      "is_hidden": false,
      "image_url": "https://cdn.simplecast.com/images/2ac34c/2ac34cab-4949-40aa-bac7-d7e3a70f0a39/831dd452-0017-431f-a6aa-e67f656bcbb5/1557799504artwork.jpg",
      "image_path": "/images/2ac34c/2ac34cab-4949-40aa-bac7-d7e3a70f0a39/831dd452-0017-431f-a6aa-e67f656bcbb5/1557799504artwork.jpg",
      "id": "d",
      "href": "https://api.simplecast.com/episodes/831dd452-0017-431f-a6aa-e67f656bcbb5?preview=true",
      "guid": "38e0bb24-2c80-4448-8435-585ef9b87e97",
      "enclosure_url": "https://cl.ly/b0adbc9c58d4/Art_Of_Escapism_-_Dont_Feel_So_Low.mp3",
      "description": "Filet mignon leberkas meatball burgdoggen. Pastrami doner chuck, shank tenderloin ground round shankle ham hock burgdoggen cupim swine shoulder. Tenderloin burgdoggen turkey sausage, leberkas tail short ribs strip steak bacon filet mignon. Pork loin leberkas picanha swine.",
      "analytics": {
        "href": "https://api.simplecast.com/analytics/downloads?episode=831dd452-0017-431f-a6aa-e67f656bcbb5&preview=true"
      }
    },
    {
      "updated_at": "2019-06-12T13:01:22.454517-07:00",
      "type": "full",
      "token": "e855d7bc",
      "title": "Podcast Episode Title",
      "status": "published",
      "slug": "e855d7bc",
      "season": {
        "href": "https://api.simplecast.com/seasons/37157954-19df-4d38-9087-6fe015020f77",
        "number": 1
      },
      "scheduled_for": null,
      "published_at": "2019-05-03T08:00:00.000000-07:00",
      "number": 4,
      "is_hidden": false,
      "image_url": "https://cdn.simplecast.com/images/2ac34c/2ac34cab-4949-40aa-bac7-d7e3a70f0a39/27992848-007f-4c0e-b0b6-74518b758f7e/1556842744artwork.jpg",
      "image_path": "/images/2ac34c/2ac34cab-4949-40aa-bac7-d7e3a70f0a39/27992848-007f-4c0e-b0b6-74518b758f7e/1556842744artwork.jpg",
      "id": "e",
      "href": "https://api.simplecast.com/episodes/27992848-007f-4c0e-b0b6-74518b758f7e?preview=true",
      "guid": "b61428c6-2974-4deb-a74f-6c22a9c2991f",
      "enclosure_url": "https://cl.ly/100c0e77180c/Ainst_Char_-_03_-_Your_Cellar_My_Shrine.mp3",
      "description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla at sagittis felis. Quisque lacus sapien, consectetur quis nibh nec, dapibus euismod mi. Mauris vel luctus libero. Donec semper suscipit nibh eu mattis. Donec aliquet lacinia laoreet. Ut gravida hendrerit orci eu hendrerit. Nam vel tristique ex.",
      "analytics": {
        "href": "https://api.simplecast.com/analytics/downloads?episode=27992848-007f-4c0e-b0b6-74518b758f7e&preview=true"
      }
    },
    {
      "updated_at": "2019-06-12T13:01:58.802141-07:00",
      "type": "full",
      "token": "49266475",
      "title": "Podcast Episode Title",
      "status": "published",
      "slug": "49266475",
      "season": {
        "href": "https://api.simplecast.com/seasons/37157954-19df-4d38-9087-6fe015020f77",
        "number": 1
      },
      "scheduled_for": null,
      "published_at": "2019-04-29T16:19:00.000000-07:00",
      "number": 3,
      "is_hidden": false,
      "image_url": "https://cdn.simplecast.com/images/2ac34c/2ac34cab-4949-40aa-bac7-d7e3a70f0a39/a593adfa-6bc5-4774-9fe9-cc5e34e6f2c4/1556580270artwork.jpg",
      "image_path": "/images/2ac34c/2ac34cab-4949-40aa-bac7-d7e3a70f0a39/a593adfa-6bc5-4774-9fe9-cc5e34e6f2c4/1556580270artwork.jpg",
      "id": "f",
      "href": "https://api.simplecast.com/episodes/a593adfa-6bc5-4774-9fe9-cc5e34e6f2c4?preview=true",
      "guid": "5943566f-d1d5-48e1-9498-6972121ff6fd",
      "enclosure_url": "https://cl.ly/196987184667/Andrew_Walton_-_02_-_The_Curse_Of_The_Albatross.mp3",
      "description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla at sagittis felis. Quisque lacus sapien, consectetur quis nibh nec, dapibus euismod mi. Mauris vel luctus libero. Donec semper suscipit nibh eu mattis. Donec aliquet lacinia laoreet. Ut gravida hendrerit orci eu hendrerit. Nam vel tristique ex.",
      "analytics": {
        "href": "https://api.simplecast.com/analytics/downloads?episode=a593adfa-6bc5-4774-9fe9-cc5e34e6f2c4&preview=true"
      }
    },
    {
      "updated_at": "2019-06-12T13:01:58.802141-07:00",
      "type": "full",
      "token": "49266475",
      "title": "Podcast Episode Title",
      "status": "published",
      "slug": "49266475",
      "season": {
        "href": "https://api.simplecast.com/seasons/37157954-19df-4d38-9087-6fe015020f77",
        "number": 1
      },
      "scheduled_for": null,
      "published_at": "2019-04-29T16:19:00.000000-07:00",
      "number": 2,
      "is_hidden": false,
      "image_url": "https://cdn.simplecast.com/images/2ac34c/2ac34cab-4949-40aa-bac7-d7e3a70f0a39/a593adfa-6bc5-4774-9fe9-cc5e34e6f2c4/1556580270artwork.jpg",
      "image_path": "/images/2ac34c/2ac34cab-4949-40aa-bac7-d7e3a70f0a39/a593adfa-6bc5-4774-9fe9-cc5e34e6f2c4/1556580270artwork.jpg",
      "id": "g",
      "href": "https://api.simplecast.com/episodes/a593adfa-6bc5-4774-9fe9-cc5e34e6f2c4?preview=true",
      "guid": "5943566f-d1d5-48e1-9498-6972121ff6fd",
      "enclosure_url": "https://cl.ly/196987184667/Andrew_Walton_-_02_-_The_Curse_Of_The_Albatross.mp3",
      "description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla at sagittis felis. Quisque lacus sapien, consectetur quis nibh nec, dapibus euismod mi. Mauris vel luctus libero. Donec semper suscipit nibh eu mattis. Donec aliquet lacinia laoreet. Ut gravida hendrerit orci eu hendrerit. Nam vel tristique ex.",
      "analytics": {
        "href": "https://api.simplecast.com/analytics/downloads?episode=a593adfa-6bc5-4774-9fe9-cc5e34e6f2c4&preview=true"
      }
    },
    {
      "updated_at": "2019-06-12T13:01:58.802141-07:00",
      "type": "full",
      "token": "49266475",
      "title": "Podcast Episode Title",
      "status": "published",
      "slug": "49266475",
      "season": {
        "href": "https://api.simplecast.com/seasons/37157954-19df-4d38-9087-6fe015020f77",
        "number": 1
      },
      "scheduled_for": null,
      "published_at": "2019-04-29T16:19:00.000000-07:00",
      "number": 1,
      "is_hidden": false,
      "image_url": "https://cdn.simplecast.com/images/2ac34c/2ac34cab-4949-40aa-bac7-d7e3a70f0a39/a593adfa-6bc5-4774-9fe9-cc5e34e6f2c4/1556580270artwork.jpg",
      "image_path": "/images/2ac34c/2ac34cab-4949-40aa-bac7-d7e3a70f0a39/a593adfa-6bc5-4774-9fe9-cc5e34e6f2c4/1556580270artwork.jpg",
      "id": "h",
      "href": "https://api.simplecast.com/episodes/a593adfa-6bc5-4774-9fe9-cc5e34e6f2c4?preview=true",
      "guid": "5943566f-d1d5-48e1-9498-6972121ff6fd",
      "enclosure_url": "https://cl.ly/196987184667/Andrew_Walton_-_02_-_The_Curse_Of_The_Albatross.mp3",
      "description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla at sagittis felis. Quisque lacus sapien, consectetur quis nibh nec, dapibus euismod mi. Mauris vel luctus libero. Donec semper suscipit nibh eu mattis. Donec aliquet lacinia laoreet. Ut gravida hendrerit orci eu hendrerit. Nam vel tristique ex.",
      "analytics": {
        "href": "https://api.simplecast.com/analytics/downloads?episode=a593adfa-6bc5-4774-9fe9-cc5e34e6f2c4&preview=true"
      }
    }
  ],
  "average_duration": 1994.26
}


================================================
FILE: gatsby-theme-simplecast/gatsby-browser.js
================================================
import React from "react"
import Layout from "./src/components/layout"
import Player from "./src/components/player"
import { EpisodeProvider, EpisodeConsumer } from "./src/components/context"
import { ThemeProvider, Styled } from "theme-ui"
import theme from "./src/gatsby-plugin-theme-ui/index"
import { SkipNavLink } from "@reach/skip-nav"

export const wrapPageElement = ({ element, props }, options) => {
  const episodeSlug = options.episodeSlug ? options.episodeSlug : "show"
  return (
    <ThemeProvider theme={theme}>
      <EpisodeProvider>
        <Styled.root>
          <SkipNavLink />
          <Layout {...props}>
            {props.location.pathname.includes(episodeSlug) ||
            props.location.pathname === "/" ? (
              <EpisodeConsumer>
                {context => <Player episode={context.state} />}
              </EpisodeConsumer>
            ) : null}
            {element}
          </Layout>
        </Styled.root>
      </EpisodeProvider>
    </ThemeProvider>
  )
}


================================================
FILE: gatsby-theme-simplecast/gatsby-config.js
================================================
module.exports = ({ markdownPath = `${__dirname}/content/episodes` }) => ({
  siteMetadata: {
    title: `Podcast Name`,
    description: `Podcast description.`,
    author: `@vojtaholik`,
  },
  plugins: [
    `gatsby-plugin-react-helmet`,
    `gatsby-plugin-sharp`,
    `gatsby-transformer-remark`,
    `gatsby-transformer-sharp`,
    `gatsby-plugin-theme-ui`,
    {
      resolve: `gatsby-source-filesystem`,
      options: {
        name: `images`,
        path: `${__dirname}/src/images`,
      },
    },
    {
      resolve: `gatsby-source-filesystem`,
      options: {
        path: markdownPath,
        name: `episodes`,
      },
    },
    {
      resolve: `gatsby-plugin-manifest`,
      options: {
        name: `gatsby-theme-simplecast`,
        short_name: `simplecast`,
        start_url: `/`,
        background_color: `#A085FF`,
        theme_color: `#A085FF`,
        display: `minimal-ui`,
        icon: `src/images/icon.png`, // This path is relative to the root of the site.
      },
    },
    // this (optional) plugin enables Progressive Web App + Offline functionality
    // To learn more, visit: https://gatsby.dev/offline
    // `gatsby-plugin-offline`,
  ],
})


================================================
FILE: gatsby-theme-simplecast/gatsby-node.js
================================================
require("dotenv").config({
  path: `.env.${process.env.NODE_ENV}`,
})
const axios = require("axios")
const crypto = require("crypto")
const path = require("path")
const slugify = require("@sindresorhus/slugify")

exports.sourceNodes = async (
  { actions: { createNode, createNodeField }, plugins },
  options
) => {
  axios.defaults.headers.common.Authorization = `Bearer ${options.simplecastApiSecret}`
  axios.defaults.headers.common.Accept = "application/json"

  const mockupEpisodes = require("./data/mockupEpisodes.json")

  const { data } = options.podcastId
    ? await axios(
        `https://api.simplecast.com/podcasts/${options.podcastId}/episodes`
      )
    : mockupEpisodes

  const packagePodcast = p => {
    const nodeContent = JSON.stringify(p)
    const nodeContentDigest = crypto
      .createHash("md5")
      .update(nodeContent)
      .digest("hex")
    const node = {
      ...p,
      content: nodeContent,
      internal: {
        type: "Episode",
        contentDigest: nodeContentDigest,
      },
    }

    createNode(node)
  }

  // fallback to mockup data if no podcast id provided

  options.podcastId
    ? data.collection.map(packagePodcast)
    : mockupEpisodes.collection.map(packagePodcast)
}

exports.createPages = async ({ actions, graphql }, options) => {
  const { data } = await graphql(`
    {
      site {
        siteMetadata {
          title
        }
      }
      allEpisode {
        edges {
          node {
            id
            title
            number
          }
        }
      }
      allMarkdownRemark {
        edges {
          node {
            id
            html
            frontmatter {
              title
            }
          }
        }
      }
    }
  `)

  data.allEpisode.edges.forEach(({ node }, options) => {
    actions.createPage({
      path: `${options.episodeSlug ? options.episodeSlug : "show"}/${
        node.number
      }/${slugify(node.title)}`,
      component: require.resolve(`./src/templates/episode.js`),
      context: {
        slug: slugify(node.title),
        id: node.id,
        title: node.title,
      },
    })
  })
}

exports.onCreateNode = ({ node, getNode, actions }, options) => {
  const { createNodeField } = actions
  const showsSlug = options.episodeSlug ? options.episodeSlug : "show"
  createNodeField({
    name: "slug",
    node,
    value: "/" + showsSlug + "/" + node.number + "/" + slugify(`${node.title}`),
  })
}


================================================
FILE: gatsby-theme-simplecast/gatsby-ssr.js
================================================
import React from "react"
import Layout from "./src/components/layout"
import Player from "./src/components/player"
import { EpisodeProvider, EpisodeConsumer } from "./src/components/context"
import { ThemeProvider, Styled } from "theme-ui"
import theme from "./src/gatsby-plugin-theme-ui/index"
import { SkipNavLink } from "@reach/skip-nav"

export const wrapPageElement = ({ element, props }, options) => {
  const episodeSlug = options.episodeSlug ? options.episodeSlug : "show"
  return (
    <ThemeProvider theme={theme}>
      <EpisodeProvider>
        <Styled.root>
          <SkipNavLink />
          <Layout {...props}>
            {props.location.pathname.includes(episodeSlug) ||
            props.location.pathname === "/" ? (
              <EpisodeConsumer>
                {context => <Player episode={context.state} />}
              </EpisodeConsumer>
            ) : null}
            {element}
          </Layout>
        </Styled.root>
      </EpisodeProvider>
    </ThemeProvider>
  )
}


================================================
FILE: gatsby-theme-simplecast/index.js
================================================
/** @jsx jsx */
import { jsx } from "theme-ui"
import { graphql } from "gatsby"
import Episode from "gatsby-theme-simplecast/src/templates/episode"

export default function Index({ data: { allEpisode, allMarkdownRemark } }) {
  const MarkdownForLatestEpisode = allMarkdownRemark.edges.filter(
    Markdown => Markdown.node.frontmatter.id === allEpisode.nodes[0].id
  )

  const data = useStaticQuery(graphql`
    {
      allEpisode {
        totalCount
        nodes {
          id
          title
          description
          number
          enclosure_url
          fields {
            slug
          }
        }
      }
      allMarkdownRemark {
        edges {
          node {
            html
            frontmatter {
              id
              title
              resources
              guestSummary
              guestName
              guestPhoto {
                childImageSharp {
                  fluid(maxWidth: 200) {
                    ...GatsbyImageSharpFluid_noBase64
                  }
                }
              }
              image {
                childImageSharp {
                  original {
                    src
                  }
                  fluid(maxWidth: 700) {
                    ...GatsbyImageSharpFluid_noBase64
                  }
                }
              }
            }
          }
        }
      }
    }
  `)
  return (
    <Episode
      data={{
        episode: data.allEpisode.nodes[0],
        markdownRemark:
          MarkdownForLatestEpisode[0] && MarkdownForLatestEpisode[0].node,
      }}
    />
  )
}


================================================
FILE: gatsby-theme-simplecast/package.json
================================================
{
  "name": "@vojtaholik/gatsby-theme-simplecast",
  "main": "index.js",
  "description": "Simplecast gatsby theme.",
  "version": "1.0.8",
  "author": "Vojta Holik <vojta@egghead.io>",
  "dependencies": {
    "@emotion/core": "^10.0.14",
    "@emotion/styled": "^10.0.14",
    "@mdx-js/react": "^1.0.27",
    "@reach/skip-nav": "^0.1.3",
    "@reach/visually-hidden": "^0.1.4",
    "@sindresorhus/slugify": "^0.9.1",
    "axios": "^0.19.0",
    "crypto": "^1.0.1",
    "dotenv": "^8.0.0",
    "fs": "^0.0.1-security",
    "gatsby": "^2.13.41",
    "gatsby-image": "^2.2.7",
    "gatsby-plugin-manifest": "^2.2.4",
    "gatsby-plugin-offline": "^2.2.4",
    "gatsby-plugin-react-helmet": "^3.1.2",
    "gatsby-plugin-sharp": "^2.2.9",
    "gatsby-plugin-theme-ui": "^0.2.18",
    "gatsby-source-filesystem": "^2.1.6",
    "gatsby-theme-ui": "^0.2.0",
    "gatsby-transformer-json": "^2.2.2",
    "gatsby-transformer-remark": "^2.6.9",
    "gatsby-transformer-sharp": "^2.2.4",
    "lodash": "^4.17.15",
    "prop-types": "^15.7.2",
    "react": "^16.8.6",
    "react-dom": "^16.8.6",
    "react-helmet": "^5.2.1",
    "react-icons": "^3.7.0",
    "react-markdown": "^4.1.0",
    "react-onclickoutside": "^6.8.0",
    "theme-ui": "^0.2.21"
  },
  "devDependencies": {
    "gatsby": "^2.13.41",
    "prettier": "^1.18.2",
    "react": "^16.8.6",
    "react-dom": "^16.8.6",
    "theme-ui": "^0.2.21"
  },
  "keywords": [
    "gatsby",
    "gatsby-theme",
    "gatsby-plugin",
    "simplecast"
  ],
  "license": "MIT",
  "scripts": {
    "build": "gatsby build",
    "develop": "gatsby develop",
    "clean": "gatsby clean",
    "format": "prettier --write src/**/*.{js,jsx}",
    "start": "npm run develop",
    "serve": "gatsby serve",
    "test": "echo \"Write tests! -> https://gatsby.dev/unit-testing\""
  },
  "repository": {
    "type": "git",
    "url": "https://github.com/vojtaholik/gatsby-theme-simplecast"
  },
  "bugs": {
    "url": "https://github.com/vojtaholik/gatsby-theme-simplecast/issues"
  },
  "peerDependencies": {
    "gatsby": "^2.13.41",
    "react": "^16.8.6",
    "react-dom": "^16.8.6",
    "theme-ui": "^0.2.21"
  }
}


================================================
FILE: gatsby-theme-simplecast/src/components/aside.js
================================================
/** @jsx jsx */
import { jsx } from "theme-ui"
import Img from "gatsby-image"
import { FaExternalLinkAlt as ExternalLinkIcon } from "react-icons/fa"
import config from "../lib/config"
import { css } from "@emotion/core"
import styled from "@emotion/styled"
import Link from "./link"
import Markdown from "react-markdown"
import itunesIcon from "../images/apple.svg"
import spotifyImage from "../images/spotify.png"
import googleImage from "../images/google.svg"

const PodcastProvider = styled(Link)(
  css({
    mb: 5,
    display: "flex",
    alignItems: "center",
    img: { m: 0, mr: 3 },
  })
)

function Aside({ markdown }) {
  return (
    <aside className="sidebar">
      <div
        sx={{
          mb: 2,
          pr: [10, 0],
          a: { color: "text", textDecoration: "none" },
        }}
      >
        {config.spotifyUrl && (
          <PodcastProvider to={config.spotifyUrl}>
            <img src={spotifyImage} alt="Spotify logo" width="90" />
          </PodcastProvider>
        )}
        {config.applePodcastsUrl && (
          <PodcastProvider to={config.applePodcastsUrl}>
            <img src={itunesIcon} alt="Apple Podcasts" />
          </PodcastProvider>
        )}
        {config.googlePodcastsUrl && (
          <PodcastProvider to={config.googlePodcastsUrl}>
            <img src={googleImage} alt="Google Podcasts" />
          </PodcastProvider>
        )}
      </div>
      {markdown && (
        <div>
          {markdown.frontmatter.guestName && (
            <div
              sx={{
                display: "flex",
                flexDirection: "column",
              }}
            >
              <h5 className="guest">Guest</h5>
              {markdown.frontmatter.guestPhoto && (
                <Img
                  sx={{
                    borderRadius: 0,
                    width: "100%",
                    maxWidth: 100,
                  }}
                  fluid={markdown.frontmatter.guestPhoto.childImageSharp.fluid}
                  alt={markdown.frontmatter.guestName}
                />
              )}
              <h4 sx={{ mt: 3, mb: 1 }}>{markdown.frontmatter.guestName}</h4>
              <Markdown>{markdown.frontmatter.guestSummary}</Markdown>
            </div>
          )}
        </div>
      )}
      {markdown && markdown.frontmatter.resources && (
        <div>
          <h5>Resources</h5>
          <ul>
            {markdown.frontmatter.resources.map((resource, i) => (
              <li key={i}>
                <ExternalLinkIcon />
                <Markdown>{resource}</Markdown>
              </li>
            ))}
          </ul>
        </div>
      )}
    </aside>
  )
}

export default Aside


================================================
FILE: gatsby-theme-simplecast/src/components/bars.js
================================================
import React from "react"

const Bars = () => (
  <div className="bars bars--paused">
    <div className="bar" />
    <div className="bar" />
    <div className="bar" />
    <div className="bar" />
    <div className="bar" />
    <div className="bar" />
    <div className="bar" />
    <div className="bar" />
    <div className="bar" />
    <div className="bar" />
    <div className="bar" />
    <div className="bar" />
    <div className="bar" />
    <div className="bar" />
    <div className="bar" />
  </div>
)

export default Bars


================================================
FILE: gatsby-theme-simplecast/src/components/context.js
================================================
import React, { Component } from "react"
import { graphql, useStaticQuery } from "gatsby"

const EpisodeContext = React.createContext()

export function EpisodeProvider(props) {
  const data = useStaticQuery(graphql`
    {
      allEpisode {
        totalCount
        nodes {
          id
          title
          description
          number
          enclosure_url
          fields {
            slug
          }
        }
      }
    }
  `)

  const [currentPlaying, setCurrentPlaying] = React.useState(
    data.allEpisode.nodes[0]
  )

  return (
    <EpisodeContext.Provider
      value={{
        state: currentPlaying,
        setCurrentPlaying,
      }}
      {...props}
    />
  )
}

export class EpisodeConsumer extends Component {
  render() {
    return (
      <EpisodeContext.Consumer>{this.props.children}</EpisodeContext.Consumer>
    )
  }
}


================================================
FILE: gatsby-theme-simplecast/src/components/header.js
================================================
/** @jsx jsx */
import { jsx, useThemeUI, Header as ThemedHeader, Box, Flex } from "theme-ui"
import Img from "gatsby-image"
import { FaPlay as PlayIcon } from "react-icons/fa"
import VisuallyHidden from "@reach/visually-hidden"
import config from "../lib/config"

function Header({ context, episode, image }) {
  const themeContext = useThemeUI()
  const { theme } = themeContext
  return (
    <ThemedHeader
      sx={{
        backgroundImage: image
          ? "none"
          : `linear-gradient(224deg, ${theme.colors.primaryLighten50} 0%, ${theme.colors.primaryDarken} 100%)`,
      }}
    >
      {image && (
        <Img
          alt={episode.title}
          fluid={image.childImageSharp.fluid}
          sx={{ height: config.headerImageHeight }}
        />
      )}
      <Box className="header_content">
        <Flex
          sx={{
            height: "100%",
            width: "100%",
            alignItems: "flex-end",
            flexDirection: "row",
            pb: 8,
          }}
        >
          <Flex sx={{ width: "100%" }}>
            <button onClick={() => context.setCurrentPlaying(episode)}>
              <VisuallyHidden>Play</VisuallyHidden>
              <PlayIcon aria-hidden="true" />
            </button>
            <div>
              <h1>{episode.title}</h1>
              <h5>EP{episode.number}</h5>
            </div>
          </Flex>
        </Flex>
      </Box>
    </ThemedHeader>
  )
}

export default Header


================================================
FILE: gatsby-theme-simplecast/src/components/image.js
================================================
import React from "react"
import { useStaticQuery, graphql } from "gatsby"
import Img from "gatsby-image"

/*
 * This component is built using `gatsby-image` to automatically serve optimized
 * images with lazy loading and reduced file sizes. The image is loaded using a
 * `useStaticQuery`, which allows us to load the image from directly within this
 * component, rather than having to pass the image data down from pages.
 *
 * For more information, see the docs:
 * - `gatsby-image`: https://gatsby.dev/gatsby-image
 * - `useStaticQuery`: https://www.gatsbyjs.org/docs/use-static-query/
 */

const Image = () => {
  const data = useStaticQuery(graphql`
    query {
      placeholderImage: file(relativePath: { eq: "gatsby-astronaut.png" }) {
        childImageSharp {
          fluid(maxWidth: 300) {
            ...GatsbyImageSharpFluid
          }
        }
      }
    }
  `)

  return <Img fluid={data.placeholderImage.childImageSharp.fluid} />
}

export default Image


================================================
FILE: gatsby-theme-simplecast/src/components/layout.css
================================================
html {
  font-family: sans-serif;
  -ms-text-size-adjust: 100%;
  -webkit-text-size-adjust: 100%;
}
body {
  margin: 0;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

.progress {
  background: gray;
  /* height: 2rem; */
  cursor: crosshair;
  overflow: hidden;
}
.progress__time {
  background: green;
  border-right: 1px solid rgba(0, 0, 0, 0.1);
  min-width: 5px;
  height: 100%;
  transition: width 0.1s;
  background: yellow;
}

article,
aside,
details,
figcaption,
figure,
footer,
header,
main,
menu,
nav,
section,
summary {
  display: block;
}
audio,
canvas,
progress,
video {
  display: inline-block;
}
audio:not([controls]) {
  display: none;
  height: 0;
}
progress {
  vertical-align: baseline;
}
[hidden],
template {
  display: none;
}
a {
  background-color: transparent;
  -webkit-text-decoration-skip: objects;
}
a:active,
a:hover {
  outline-width: 0;
}
abbr[title] {
  border-bottom: none;
  text-decoration: underline;
  text-decoration: underline dotted;
}
b,
strong {
  font-weight: inherit;
  font-weight: bolder;
}
dfn {
  font-style: italic;
}
h1 {
  font-size: 2em;
  margin: 0.67em 0;
}
mark {
  background-color: #ff0;
  color: #000;
}
small {
  font-size: 80%;
}
sub,
sup {
  font-size: 75%;
  line-height: 0;
  position: relative;
  vertical-align: baseline;
}
sub {
  bottom: -0.25em;
}
sup {
  top: -0.5em;
}
img {
  border-style: none;
}
svg:not(:root) {
  overflow: hidden;
}
code,
kbd,
pre,
samp {
  font-family: monospace, monospace;
  font-size: 1em;
}
figure {
  margin: 1em 40px;
}
hr {
  box-sizing: content-box;
  height: 0;
  overflow: visible;
}
button,
input,
optgroup,
select,
textarea {
  font: inherit;
  margin: 0;
}
optgroup {
  font-weight: 700;
}
button,
input {
  overflow: visible;
}
button,
select {
  text-transform: none;
}
[type="reset"],
[type="submit"],
button,
html [type="button"] {
  -webkit-appearance: button;
}
[type="button"]::-moz-focus-inner,
[type="reset"]::-moz-focus-inner,
[type="submit"]::-moz-focus-inner,
button::-moz-focus-inner {
  border-style: none;
  padding: 0;
}
[type="button"]:-moz-focusring,
[type="reset"]:-moz-focusring,
[type="submit"]:-moz-focusring,
button:-moz-focusring {
  outline: 1px dotted ButtonText;
}
fieldset {
  border: 1px solid silver;
  margin: 0 2px;
  padding: 0.35em 0.625em 0.75em;
}
legend {
  box-sizing: border-box;
  color: inherit;
  display: table;
  max-width: 100%;
  padding: 0;
  white-space: normal;
}
textarea {
  overflow: auto;
}
[type="checkbox"],
[type="radio"] {
  box-sizing: border-box;
  padding: 0;
}
[type="number"]::-webkit-inner-spin-button,
[type="number"]::-webkit-outer-spin-button {
  height: auto;
}
[type="search"] {
  -webkit-appearance: textfield;
  outline-offset: -2px;
}
[type="search"]::-webkit-search-cancel-button,
[type="search"]::-webkit-search-decoration {
  -webkit-appearance: none;
}
::-webkit-input-placeholder {
  color: inherit;
  opacity: 0.54;
}
::-webkit-file-upload-button {
  -webkit-appearance: button;
  font: inherit;
}
html {
  font: 112.5%/1.45em georgia, serif;
  box-sizing: border-box;
  overflow-y: scroll;
}
* {
  box-sizing: inherit;
}
*:before {
  box-sizing: inherit;
}
*:after {
  box-sizing: inherit;
}
body {
  color: hsla(0, 0%, 0%, 0.8);
  font-family: georgia, serif;
  font-weight: normal;
  word-wrap: break-word;
  font-kerning: normal;
  -moz-font-feature-settings: "kern", "liga", "clig", "calt";
  -ms-font-feature-settings: "kern", "liga", "clig", "calt";
  -webkit-font-feature-settings: "kern", "liga", "clig", "calt";
  font-feature-settings: "kern", "liga", "clig", "calt";
}
img {
  max-width: 100%;
  margin-left: 0;
  margin-right: 0;
  margin-top: 0;
  padding-bottom: 0;
  padding-left: 0;
  padding-right: 0;
  padding-top: 0;
  margin-bottom: 1.45rem;
}
h1 {
  margin-left: 0;
  margin-right: 0;
  margin-top: 0;
  padding-bottom: 0;
  padding-left: 0;
  padding-right: 0;
  padding-top: 0;
  margin-bottom: 1.45rem;
  color: inherit;
  font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
    Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
  font-weight: bold;
  text-rendering: optimizeLegibility;
  font-size: 2.25rem;
  line-height: 1.1;
}
h2 {
  margin-left: 0;
  margin-right: 0;
  margin-top: 0;
  padding-bottom: 0;
  padding-left: 0;
  padding-right: 0;
  padding-top: 0;
  margin-bottom: 1.45rem;
  color: inherit;
  font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
    Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
  font-weight: bold;
  text-rendering: optimizeLegibility;
  font-size: 1.62671rem;
  line-height: 1.1;
}
h3 {
  margin-left: 0;
  margin-right: 0;
  margin-top: 0;
  padding-bottom: 0;
  padding-left: 0;
  padding-right: 0;
  padding-top: 0;
  margin-bottom: 1.45rem;
  color: inherit;
  font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
    Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
  font-weight: bold;
  text-rendering: optimizeLegibility;
  font-size: 1.38316rem;
  line-height: 1.1;
}
h4 {
  margin-left: 0;
  margin-right: 0;
  margin-top: 0;
  padding-bottom: 0;
  padding-left: 0;
  padding-right: 0;
  padding-top: 0;
  margin-bottom: 1.45rem;
  color: inherit;
  font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
    Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
  font-weight: bold;
  text-rendering: optimizeLegibility;
  font-size: 1rem;
  line-height: 1.1;
}
h5 {
  margin-left: 0;
  margin-right: 0;
  margin-top: 0;
  padding-bottom: 0;
  padding-left: 0;
  padding-right: 0;
  padding-top: 0;
  margin-bottom: 1.45rem;
  color: inherit;
  font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
    Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
  font-weight: bold;
  text-rendering: optimizeLegibility;
  font-size: 0.85028rem;
  line-height: 1.1;
}
h6 {
  margin-left: 0;
  margin-right: 0;
  margin-top: 0;
  padding-bottom: 0;
  padding-left: 0;
  padding-right: 0;
  padding-top: 0;
  margin-bottom: 1.45rem;
  color: inherit;
  font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
    Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
  font-weight: bold;
  text-rendering: optimizeLegibility;
  font-size: 0.78405rem;
  line-height: 1.1;
}
hgroup {
  margin-left: 0;
  margin-right: 0;
  margin-top: 0;
  padding-bottom: 0;
  padding-left: 0;
  padding-right: 0;
  padding-top: 0;
  margin-bottom: 1.45rem;
}
ul {
  list-style: none;
  margin-left: 0;
  margin-right: 0;
  margin-top: 0;
  padding-bottom: 0;
  padding-left: 0;
  padding-right: 0;
  padding-top: 0;
  margin-bottom: 1.45rem;
  list-style-position: outside;
  list-style-image: none;
}
ol {
  margin-left: 1.45rem;
  margin-right: 0;
  margin-top: 0;
  padding-bottom: 0;
  padding-left: 0;
  padding-right: 0;
  padding-top: 0;
  margin-bottom: 1.45rem;
  list-style-position: outside;
  list-style-image: none;
}
dl {
  margin-left: 0;
  margin-right: 0;
  margin-top: 0;
  padding-bottom: 0;
  padding-left: 0;
  padding-right: 0;
  padding-top: 0;
  margin-bottom: 1.45rem;
}
dd {
  margin-left: 0;
  margin-right: 0;
  margin-top: 0;
  padding-bottom: 0;
  padding-left: 0;
  padding-right: 0;
  padding-top: 0;
  margin-bottom: 1.45rem;
}
p {
  margin-left: 0;
  margin-right: 0;
  margin-top: 0;
  padding-bottom: 0;
  padding-left: 0;
  padding-right: 0;
  padding-top: 0;
  margin-bottom: 1.45rem;
}
figure {
  margin-left: 0;
  margin-right: 0;
  margin-top: 0;
  padding-bottom: 0;
  padding-left: 0;
  padding-right: 0;
  padding-top: 0;
  margin-bottom: 1.45rem;
}
pre {
  margin-left: 0;
  margin-right: 0;
  margin-top: 0;
  margin-bottom: 1.45rem;
  font-size: 0.85rem;
  line-height: 1.42;
  background: hsla(0, 0%, 0%, 0.04);
  border-radius: 3px;
  overflow: auto;
  word-wrap: normal;
  padding: 1.45rem;
}
table {
  margin-left: 0;
  margin-right: 0;
  margin-top: 0;
  padding-bottom: 0;
  padding-left: 0;
  padding-right: 0;
  padding-top: 0;
  margin-bottom: 1.45rem;
  font-size: 1rem;
  line-height: 1.45rem;
  border-collapse: collapse;
  width: 100%;
}
fieldset {
  margin-left: 0;
  margin-right: 0;
  margin-top: 0;
  padding-bottom: 0;
  padding-left: 0;
  padding-right: 0;
  padding-top: 0;
  margin-bottom: 1.45rem;
}
blockquote {
  margin-left: 1.45rem;
  margin-right: 1.45rem;
  margin-top: 0;
  padding-bottom: 0;
  padding-left: 0;
  padding-right: 0;
  padding-top: 0;
  margin-bottom: 1.45rem;
}
form {
  margin-left: 0;
  margin-right: 0;
  margin-top: 0;
  padding-bottom: 0;
  padding-left: 0;
  padding-right: 0;
  padding-top: 0;
  margin-bottom: 1.45rem;
}
noscript {
  margin-left: 0;
  margin-right: 0;
  margin-top: 0;
  padding-bottom: 0;
  padding-left: 0;
  padding-right: 0;
  padding-top: 0;
  margin-bottom: 1.45rem;
}
iframe {
  margin-left: 0;
  margin-right: 0;
  margin-top: 0;
  padding-bottom: 0;
  padding-left: 0;
  padding-right: 0;
  padding-top: 0;
  margin-bottom: 1.45rem;
}
hr {
  margin-left: 0;
  margin-right: 0;
  margin-top: 0;
  padding-bottom: 0;
  padding-left: 0;
  padding-right: 0;
  padding-top: 0;
  margin-bottom: calc(1.45rem - 1px);
  background: hsla(0, 0%, 0%, 0.2);
  border: none;
  height: 1px;
}
address {
  margin-left: 0;
  margin-right: 0;
  margin-top: 0;
  padding-bottom: 0;
  padding-left: 0;
  padding-right: 0;
  padding-top: 0;
  margin-bottom: 1.45rem;
}
b {
  font-weight: bold;
}
strong {
  font-weight: bold;
}
dt {
  font-weight: bold;
}
th {
  font-weight: bold;
}
li {
  margin-bottom: calc(1.45rem / 2);
}
ol li {
  padding-left: 0;
}
ul li {
  padding-left: 0;
}
li > ol {
  margin-left: 1.45rem;
  margin-bottom: calc(1.45rem / 2);
  margin-top: calc(1.45rem / 2);
}
li > ul {
  margin-left: 1.45rem;
  margin-bottom: calc(1.45rem / 2);
  margin-top: calc(1.45rem / 2);
}
blockquote *:last-child {
  margin-bottom: 0;
}
li *:last-child {
  margin-bottom: 0;
}
p *:last-child {
  margin-bottom: 0;
}
li > p {
  margin-bottom: calc(1.45rem / 2);
}
code {
  font-size: 0.85rem;
  line-height: 1.45rem;
}
kbd {
  font-size: 0.85rem;
  line-height: 1.45rem;
}
samp {
  font-size: 0.85rem;
  line-height: 1.45rem;
}
abbr {
  border-bottom: 1px dotted hsla(0, 0%, 0%, 0.5);
  cursor: help;
}
acronym {
  border-bottom: 1px dotted hsla(0, 0%, 0%, 0.5);
  cursor: help;
}
abbr[title] {
  border-bottom: 1px dotted hsla(0, 0%, 0%, 0.5);
  cursor: help;
  text-decoration: none;
}
thead {
  text-align: left;
}
td,
th {
  text-align: left;
  border-bottom: 1px solid hsla(0, 0%, 0%, 0.12);
  font-feature-settings: "tnum";
  -moz-font-feature-settings: "tnum";
  -ms-font-feature-settings: "tnum";
  -webkit-font-feature-settings: "tnum";
  padding-left: 0.96667rem;
  padding-right: 0.96667rem;
  padding-top: 0.725rem;
  padding-bottom: calc(0.725rem - 1px);
}
th:first-child,
td:first-child {
  padding-left: 0;
}
th:last-child,
td:last-child {
  padding-right: 0;
}
tt,
code {
  background-color: hsla(0, 0%, 0%, 0.04);
  border-radius: 3px;
  font-family: "SFMono-Regular", Consolas, "Roboto Mono", "Droid Sans Mono",
    "Liberation Mono", Menlo, Courier, monospace;
  padding: 0;
  padding-top: 0.2em;
  padding-bottom: 0.2em;
}
pre code {
  background: none;
  line-height: 1.42;
}
code:before,
code:after,
tt:before,
tt:after {
  letter-spacing: -0.2em;
  content: " ";
}
pre code:before,
pre code:after,
pre tt:before,
pre tt:after {
  content: "";
}
@media only screen and (max-width: 480px) {
  html {
    font-size: 100%;
  }
}

.bars {
  height: 45px;
  width: 0;
  position: relative;
  top: -9px;
  left: -1px;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
}

.bar {
  background: #a085ff;
  bottom: 1px;
  height: 3px;
  position: absolute;
  width: 3px;
  animation: sound 0ms -800ms linear infinite alternate;
}

.bars--paused > * {
  animation-play-state: paused;
}

@keyframes sound {
  0% {
    opacity: 0.35;
    background: #a085ff;
    width: 3px;
  }
  100% {
    opacity: 1;
    background: #a085ff;
    width: 10px;
  }
}

.bar:nth-child(1) {
  top: 1px;
  animation-duration: 474ms;
}
.bar:nth-child(2) {
  top: 5px;
  animation-duration: 433ms;
}
.bar:nth-child(3) {
  top: 9px;
  animation-duration: 407ms;
}
.bar:nth-child(4) {
  top: 13px;
  animation-duration: 458ms;
}
.bar:nth-child(5) {
  top: 17px;
  animation-duration: 400ms;
}
.bar:nth-child(6) {
  top: 21px;
  animation-duration: 427ms;
}
.bar:nth-child(7) {
  top: 25px;
  animation-duration: 441ms;
}
.bar:nth-child(8) {
  top: 29px;
  animation-duration: 419ms;
}
.bar:nth-child(9) {
  top: 33px;
  animation-duration: 487ms;
}

.bar:nth-child(10) {
  top: 37px;
  animation-duration: 442ms;
}

.bar:nth-child(11) {
  top: 41px;
  animation-duration: 435ms;
}

.bar:nth-child(12) {
  top: 45px;
  animation-duration: 510ms;
}

.bar:nth-child(13) {
  top: 49px;
  animation-duration: 485ms;
}

.bar:nth-child(14) {
  top: 53px;
  animation-duration: 455ms;
}

.bar:nth-child(15) {
  top: 57px;
  animation-duration: 425ms;
}


================================================
FILE: gatsby-theme-simplecast/src/components/layout.js
================================================
/** @jsx jsx */
import PropTypes from "prop-types"
import Navigation from "./navigation"
import "./layout.css"
import { jsx, Layout as Wrapper, Container } from "theme-ui"

function Layout({ children }) {
  return (
    <Wrapper>
      <Container
        sx={{
          p: 0,
          display: "flex",
          flexDirection: ["column", "row"],
          flexGrow: "1",
        }}
      >
        <Navigation eventTypes="click" />
        <main sx={{ width: "100%", ml: [0, 0, 0, 5] }}>{children}</main>
      </Container>
    </Wrapper>
  )
}

Layout.propTypes = {
  children: PropTypes.node.isRequired,
}

export default Layout


================================================
FILE: gatsby-theme-simplecast/src/components/link.js
================================================
import React from "react"
import GatsbyLink from "gatsby-link"

const Link = ({ children, to, ...other }) => {
  const internal = /^\/(?!\/)/.test(to)

  if (internal) {
    return (
      <GatsbyLink to={to} {...other}>
        {children}
      </GatsbyLink>
    )
  }

  return (
    <a href={to} {...other}>
      {children}
    </a>
  )
}

export default Link


================================================
FILE: gatsby-theme-simplecast/src/components/navigation.js
================================================
/** @jsx jsx */
import React from "react"
import { useStaticQuery, graphql } from "gatsby"
import { jsx, Flex } from "theme-ui"
import { EpisodeConsumer } from "./context"
import { FaPlay as PlayIcon } from "react-icons/fa"
import { MdMenu as MenuIcon, MdClose as CloseMenuIcon } from "react-icons/md"
import onClickOutside from "react-onclickoutside"
import config from "../lib/config"
import Link from "./link"
import Bars from "./bars"

function Navigation() {
  const [isOpen, setIsOpen] = React.useState(false)
  const toggleMenu = () => setIsOpen(!isOpen)
  Navigation.handleClickOutside = () => setIsOpen(false)
  const twoDigits = n => (n.toString().length < 2 ? `0${n}` : n)

  const Logo = () => (
    <>
      <Link to="/">
        <h1 sx={{ fontSize: 6, color: "primary", mb: 0 }}>
          {data.site.siteMetadata.title
            ? data.site.siteMetadata.title
            : "Podcast Name"}
        </h1>
      </Link>
      {config.podcastSeason && (
        <h5
          sx={{
            textTransform: "uppercase",
            mt: 2,
            mb: 0,
            fontWeight: 400,
            fontSize: 0,
            opacity: 0.6,
          }}
        >
          season {twoDigits(config.podcastSeason)}
        </h5>
      )}
    </>
  )

  const data = useStaticQuery(graphql`
    query navQuery {
      site {
        siteMetadata {
          title
        }
      }
      allEpisode {
        totalCount
        nodes {
          id
          title
          description
          number
          enclosure_url
          fields {
            slug
          }
        }
      }
      allMarkdownRemark {
        edges {
          node {
            id
            frontmatter {
              id
              summary
            }
          }
        }
      }
    }
  `)
  return (
    <EpisodeConsumer>
      {context => (
        <>
          <Flex
            sx={{
              variant: "header.logo.container",
            }}
          >
            <Flex
              sx={{
                variant: "header.logo",
              }}
            >
              <Logo />
            </Flex>
            <button
              sx={{
                position: "relative",
                zIndex: 998,
                display: "flex",
                p: 3,
                backgroundColor: "background",
                color: "text",
                borderColor: "backgroundLighten20",
                fontSize: 5,
              }}
              onClick={toggleMenu}
              aria-controls="menu"
              aria-haspopup="true"
              aria-expanded={isOpen ? "true" : "false"}
            >
              {isOpen ? <CloseMenuIcon /> : <MenuIcon />}
            </button>
          </Flex>
          <nav
            className="episodes_list"
            sx={{
              variant: "navigation.episodes",
              transform: [`translateX(${isOpen ? "0" : "-100%"})`, "none"],
              transition: "300ms cubic-bezier(1, 0, 0, 1)",
            }}
          >
            <div sx={{ ml: 6, pb: 4 }}>
              <Logo />
            </div>
            <ul id="menu" role="menu" sx={{ pb: 14 }}>
              {data.allEpisode.nodes.map(episode => (
                <li role="none" key={episode.id}>
                  {episode.id === context.state.id && <Bars />}
                  <Link
                    role="menuitem"
                    activeClassName="active"
                    to={episode.fields.slug}
                  >
                    <h4>{episode.title}</h4>
                    {data.allMarkdownRemark.edges.map(({ node: markdown }) => {
                      if (markdown.frontmatter.id === episode.id)
                        return (
                          markdown.frontmatter.summary && (
                            <p key={markdown.id} className="summary">
                              {markdown.frontmatter.summary}
                            </p>
                          )
                        )
                      else return null
                    })}
                  </Link>
                  {episode.id !== context.state.id && (
                    <button
                      tabIndex="-1"
                      onClick={() => context.setCurrentPlaying(episode)}
                    >
                      <PlayIcon aria-hidden="true" />
                    </button>
                  )}
                </li>
              ))}
            </ul>
          </nav>
        </>
      )}
    </EpisodeConsumer>
  )
}

const clickOutsideConfig = {
  handleClickOutside: () => Navigation.handleClickOutside,
}

export default onClickOutside(Navigation, clickOutsideConfig)


================================================
FILE: gatsby-theme-simplecast/src/components/player.js
================================================
// by Wes Bos, syntax.fm
// https://github.com/wesbos/Syntax/blob/master/components/Player.js
/** @jsx jsx */
import React from "react"
import PropTypes from "prop-types"
import { FaPlay, FaPause } from "react-icons/fa"
import { jsx, Container } from "theme-ui"
import { keyframes } from "@emotion/core"
import formatTime from "../lib/formatTime"
import VisuallyHidden from "@reach/visually-hidden"
// import VolumeBars from "./volumeBars"

export default class Player extends React.Component {
  static propTypes = {
    episode: PropTypes.object.isRequired,
  }

  constructor(props) {
    super(props)

    let lastPlayed = 0
    let lastVolumePref = 1
    // let lastPlaybackRate = 1

    // for Server Side Rendering
    if (typeof window !== "undefined") {
      const { episode } = this.props
      const lp = localStorage.getItem(`lastPlayed${episode.number}`)
      const lastVolume = localStorage.getItem(`lastVolumeSetting`)
      // const lastPlayback = localStorage.getItem(`lastPlaybackSetting`)

      if (lp) lastPlayed = JSON.parse(lp).lastPlayed
      if (lastVolume) lastVolumePref = JSON.parse(lastVolume).lastVolumePref
      // if (lastPlayback)
      //   lastPlaybackRate = JSON.parse(lastPlayback).lastPlaybackRate
    }

    this.state = {
      progressTime: 50,
      playing: false,
      duration: 0,
      currentTime: lastPlayed,
      currentVolume: lastVolumePref,
      // playbackRate: lastPlaybackRate,
      timeWasLoaded: lastPlayed !== 0,
      showTooltip: false,
      tooltipPosition: 0,
      tooltipTime: "0:00",
    }
  } // END Constructor

  componentWillUpdate(nextProps, nextState) {
    // this.audio.playbackRate = nextState.playbackRate
  }

  componentDidUpdate(prevProps, prevState) {
    const { episode } = this.props
    const {
      currentTime,
      currentVolume,
      // playbackRate
    } = this.state
    if (episode.number !== prevProps.episode.number) {
      const lp = localStorage.getItem(`lastPlayed${episode.number}`)
      if (lp) {
        const lastVolume = localStorage.getItem(`lastVolumeSetting`)
        // const lastPlayback = localStorage.getItem(`lastPlaybackSetting`)
        const data = JSON.parse(lp)
        const data2 = JSON.parse(lastVolume)
        // const data3 = JSON.parse(lastPlayback)

        this.setState({
          currentTime: data.lastPlayed,
          currentVolume: data2.lastVolumePref,
          // playbackRate: data3.lastPlaybackRate,
        })
        this.audio.currentTime = data.lastPlayed
        this.audio.volume = data2.lastVolumePref
        // this.audio.playbackRate = data3.lastPlaybackRate
      }
      this.audio.play()
    } else {
      localStorage.setItem(
        `lastPlayed${episode.number}`,
        JSON.stringify({ lastPlayed: currentTime })
      )
      localStorage.setItem(
        `lastVolumeSetting`,
        JSON.stringify({ lastVolumePref: currentVolume })
      )
      // localStorage.setItem(
      //   `lastPlaybackSetting`,
      //   JSON.stringify({ lastPlaybackRate: playbackRate })
      // )
    }
  }

  timeUpdate = e => {
    // console.log('Updating Time');
    const { episode } = this.props
    const { timeWasLoaded } = this.state
    // Check if the user already had a curent time
    if (timeWasLoaded) {
      const lp = localStorage.getItem(`lastPlayed${episode.number}`)

      if (lp) {
        e.currentTarget.currentTime = JSON.parse(lp).lastPlayed
      }
      this.setState({ timeWasLoaded: false })
    } else {
      const { currentTime = 0, duration = 0 } = e.currentTarget

      const progressTime = (currentTime / duration) * 100
      if (Number.isNaN(progressTime)) return
      this.setState({ progressTime, currentTime, duration })
    }
  }

  volumeUpdate = e => {
    const { timeWasLoaded } = this.state
    // Check if the user already had a curent volume
    if (timeWasLoaded) {
      const lastVolume = localStorage.getItem(`lastVolumeSetting`)
      if (lastVolume) {
        e.currentTarget.volume = JSON.parse(lastVolume).lastVolumePref
      }
      this.setState({ timeWasLoaded: false })
    }
  }

  groupUpdates = e => {
    this.timeUpdate(e)
    this.volumeUpdate(e)
  }

  togglePlay = () => {
    const { playing } = this.state
    const method = playing ? "pause" : "play"
    this.audio[method]()
  }

  scrubTime = eventData =>
    (eventData.nativeEvent.offsetX / this.progress.offsetWidth) *
    this.audio.duration

  scrub = e => {
    this.audio.currentTime = this.scrubTime(e)
  }

  seekTime = e => {
    this.setState({
      tooltipPosition: e.nativeEvent.offsetX,
      tooltipTime: formatTime(this.scrubTime(e)),
    })
  }

  playPause = () => {
    this.setState({ playing: !this.audio.paused })
    const method = this.audio.paused ? "add" : "remove"
    document.querySelector(".bars").classList[method]("bars--paused") // 💩
  }

  volume = e => {
    this.audio.volume = e.currentTarget.value
    this.setState({
      currentVolume: `${e.currentTarget.value}`,
    })
  }

  speedUp = () => {
    this.speed(0.25)
  }

  speedDown = e => {
    e.preventDefault()
    this.speed(-0.25)
  }

  // speed = change => {
  //   const playbackRateMax = 2.5
  //   const playbackRateMin = 0.75

  //   let playbackRate = this.state.playbackRate + change

  //   if (playbackRate > playbackRateMax) {
  //     playbackRate = playbackRateMin
  //   }

  //   if (playbackRate < playbackRateMin) {
  //     playbackRate = playbackRateMax
  //   }

  //   this.setState({ playbackRate })
  // }

  render() {
    const { episode } = this.props
    const {
      playing,
      // playbackRate,
      progressTime,
      currentTime,
      duration,
      showTooltip,
      tooltipPosition,
      tooltipTime,
    } = this.state

    const bounce = keyframes`
    from {
      transform: translateX(0)
    }
  to {
    transform: translateX(-200px)
  }
  `

    return (
      <div
        sx={{
          zIndex: 10,
          position: "fixed",
          width: "100%",
          color: "text",
          borderTop: "2px solid",
          borderColor: "backgroundLighten10",
          backgroundColor: "background",
          height: ["auto", 60],
          bottom: 0,
          left: 0,
          display: "flex",

          alignItems: "center",
        }}
        className="player"
      >
        <Container
          sx={{
            display: "flex",
            flexDirection: ["column", "row"],
            alignItems: ["flex-start", "center"],
            pb: [2, "inherit"],
            pt: [0, "inherit"],
          }}
        >
          <div
            sx={{
              width: "100%",
              maxWidth: ["100%", 310],
              display: "flex",
              alignItems: "center",
              "*": {
                m: 0,
              },
            }}
          >
            <button
              tabIndex="0"
              sx={{
                backgroundImage:
                  "linear-gradient(224deg, #B298FF 0%, #7A5EFF 100%)",
                color: "text",
                border: "none",
                width: "100%",
                maxWidth: 40,
                height: 40,
                borderRadius: 1,
                display: "flex",
                alignItems: "center",
                justifyContent: "center",
                fontSize: 1,
                cursor: "pointer",
                svg: {
                  mt: "1px",
                  ml: playing ? "0" : "2px",
                },
              }}
              onClick={this.togglePlay}
              aria-label={playing ? "pause" : `play ${episode.title}`}
              type="button"
            >
              <VisuallyHidden>{playing ? "Pause" : "Play"}</VisuallyHidden>{" "}
              {playing ? <FaPause /> : <FaPlay />}
            </button>
            <div
              sx={{
                ml: 4,
                display: "flex",
                alignItems: "center",
                justifyContent: ["flex-start", "flex-end"],
                width: "100%",
                overflow: "hidden",
                whiteSpace: "nowrap",
                height: 60,
                ".fade-out": {
                  display: ["none", "block"],
                  position: "absolute",
                  zIndex: 999,
                  width: 40,
                  height: 60,
                  backgroundImage:
                    "linear-gradient(270deg, #1A2232 20%, rgba(26,34,50,0) 100%)",
                },
                h3: {
                  overflow: "hidden",
                  position: "relative",
                  fontSize: 4,
                  display: "block",
                },
                ":hover": {
                  h3: { animation: `${bounce} 5s linear infinite` },
                },
              }}
            >
              <h3>
                {episode.title} - EP{episode.number}
              </h3>
              <div className="fade-out" />
            </div>
          </div>

          <div
            sx={{
              ml: [0, 2],
              width: "100%",
              display: "flex",
              alignItems: "center",
              span: {
                fontVariantNumeric: "tabular-nums",
                width: ["auto", 50],
                fontSize: 1,
                textAlign: "center",
                opacity: 0.6,
              },
            }}
          >
            <span>{formatTime(currentTime)}</span>
            <div
              sx={{
                mx: 2,
                height: [4, 2],
                flexGrow: "1",
                borderRadius: ["3px", "0px"],
                maxWidth: 460,
                backgroundColor: "backgroundLighten20",
              }}
              className="progress"
              onClick={this.scrub}
              onMouseMove={this.seekTime}
              onMouseEnter={() => {
                this.setState({ showTooltip: true })
              }}
              onMouseLeave={() => {
                this.setState({ showTooltip: false })
              }}
              ref={x => (this.progress = x)}
            >
              {/* eslint-enable */}
              <div
                className="progress__time"
                sx={{
                  width: `${progressTime}%`,
                  backgroundImage:
                    "linear-gradient(224deg, #B298FF 0%, #7A5EFF 100%)",
                }}
              />
            </div>
            <span>{formatTime(duration)}</span>
            <div
              style={{
                position: "absolute",
                left: `${tooltipPosition}px`,
                opacity: `${showTooltip ? "1" : "0"}`,
              }}
            >
              {tooltipTime}
            </div>
          </div>

          {/* <div className="player__section player__section--right">
          <button
            onClick={this.speedUp}
            onContextMenu={this.speedDown}
            className="player__speed"
            type="button"
          >
            <p>FASTNESS</p>
            <span className="player__speeddisplay">{playbackRate} &times;</span>
          </button>
        </div> */}
          {/* <div
          className="player__volume"
          style={{ display: "flex", width: "100%" }}
        >
          <p>LOUDNESS</p>
          <div className="player__inputs">
            <VolumeBars volume={this.volume} />
          </div>
        </div> */}
          <audio
            ref={audio => (this.audio = audio)}
            onPlay={this.playPause}
            onPause={this.playPause}
            onTimeUpdate={this.timeUpdate}
            onVolumeChange={this.volumeUpdate}
            onLoadedMetadata={this.groupUpdates}
            src={episode.enclosure_url}
          />
        </Container>
      </div>
    )
  }
}


================================================
FILE: gatsby-theme-simplecast/src/components/seo.js
================================================
/**
 * SEO component that queries for data with
 *  Gatsby's useStaticQuery React hook
 *
 * See: https://www.gatsbyjs.org/docs/use-static-query/
 */

import React from "react"
import PropTypes from "prop-types"
import Helmet from "react-helmet"
import { useStaticQuery, graphql } from "gatsby"

function SEO({ description, lang, meta, title, image }) {
  const { site } = useStaticQuery(
    graphql`
      query {
        site {
          siteMetadata {
            title
            description
            author
          }
        }
      }
    `
  )

  const metaDescription = description || site.siteMetadata.description

  return (
    <Helmet
      htmlAttributes={{
        lang,
      }}
      title={title}
      titleTemplate={`%s | ${site.siteMetadata.title}`}
      meta={[
        {
          name: `description`,
          content: metaDescription,
        },
        {
          property: `og:title`,
          content: title,
        },
        {
          property: `og:description`,
          content: metaDescription,
        },
        {
          property: `og:type`,
          content: `website`,
        },
        {
          name: `twitter:card`,
          content: `summary`,
        },
        {
          name: `twitter:creator`,
          content: site.siteMetadata.author,
        },
        {
          name: `twitter:title`,
          content: title,
        },
        {
          name: `twitter:description`,
          content: metaDescription,
        },
        {
          name: `og:image`,
          content: image,
        },
      ].concat(meta)}
    />
  )
}

SEO.defaultProps = {
  lang: `en`,
  meta: [],
  description: ``,
}

SEO.propTypes = {
  description: PropTypes.string,
  lang: PropTypes.string,
  meta: PropTypes.arrayOf(PropTypes.object),
  title: PropTypes.string.isRequired,
}

export default SEO


================================================
FILE: gatsby-theme-simplecast/src/components/volumeBars.js
================================================
import React, { Component, Fragment } from "react"

// TODO Fix all eslint issues

// data generator -> to create 10 volume bars
const getItems = count => {
  return Array.from({ length: count }, (v, i) => (i + 1) * 10).map(k => {
    let decimal = k / 100
    return {
      integer: `${k}`,
      deci: `${decimal}`,
      vol: `vol${k}`,
      level: `${k}/100`,
      checked: true,
    }
  }) // END MAP
} // END ARROW

class VolumeBars extends Component {
  state = {
    volumeBarList: getItems(10),
  }

  componentDidMount() {
    const localKey = `lastVolumeBarsOn`
    const localStorageRef = localStorage.getItem(localKey)
    if (localStorageRef) {
      this.setState({ volumeBarList: JSON.parse(localStorageRef) })
    }
  }

  componentDidUpdate(prevProps, prevState) {
    const localKey = `lastVolumeBarsOn`
    const localValue = JSON.stringify(this.state.volumeBarList)
    localStorage.setItem(localKey, localValue)
  }

  //We are going to track which volume bars are "checked"
  handleOnClick = index => {
    // make a copy of state
    const volumeBarList = [...this.state.volumeBarList]
    // Get the index positions from 0 till index (index clicked)
    for (let i = 0; i <= index; i++) {
      volumeBarList[i].checked = true
    }
    // Get the index positions of the remaining non-checked
    for (let i = index + 1; i < 10; i++) {
      volumeBarList[i].checked = null
    }
    // Update State
    this.setState({
      volumeBarList,
    })
  }

  render() {
    return (
      <Fragment>
        {this.state.volumeBarList.map((item, index) => (
          <Fragment key={item.integer}>
            <input
              onClick={() => {
                this.handleOnClick(index)
              }}
              onChange={this.props.volume}
              type="radio"
              name="volume"
              value={item.deci}
              id={item.vol}
              className="sr-only"
            />
            <label
              htmlFor={item.vol}
              style={
                item.checked
                  ? { background: "#03fff3" }
                  : { background: "#e4e4e4" }
              }
            >
              <span className="sr-only">{item.level}</span>
            </label>
          </Fragment>
        ))}
      </Fragment>
    )
  }
}

export default VolumeBars


================================================
FILE: gatsby-theme-simplecast/src/gatsby-plugin-theme-ui/index.js
================================================
import config from "../lib/config"

export default {
  useCustomProperties: true,
  initialColorMode: "dark",
  breakpoints: ["992px", "1200px", "1920px"],
  space: [0, 4, 8, 12, 16, 20, 24, 32, 40, 48, 56, 64, 72, 80, 128, 256, 512],
  sizes: [0, 4, 8, 12, 16, 20, 24, 32, 40, 48, 56, 64, 72, 80, 128, 256, 512],
  fontSizes: [12, 14, 16, 18, 20, 22, 24, 32, 40, 48, 64],
  colors: {
    modes: {
      dark: {
        text: "rgba(255, 255, 255, 0.9)",
        background: "#1A2232",
        backgroundLighten10: "#232B3B",
        backgroundLighten20: "#2C3648",
        primaryDarken: "#7A5EFF",
        primary: "#A085FF",
        primaryLighten10: "#9D82FF",
        primaryLighten50: "#B298FF",
        primaryLighten70: "#D2C8FF",
        secondary: "#85FFD0",
      },
    },
  },
  radii: [5, "50%"],
  fontWeights: {
    body: 300,
    heading: 500,
  },
  lineHeights: {
    body: 1.675,
    heading: 1.125,
  },
  letterSpacings: {
    heading: "1.5",
  },
  fonts: {
    body: "system-ui, sans-serif",
    heading: "inherit",
  },
  header: {
    logo: {
      flexDirection: "column",
      justifyContent: "center",
      a: { textDecoration: "none" },
      container: {
        p: 3,
        display: ["flex", "none"],
        visibility: ["visible", "hidden"],
        width: "100%",
        justifyContent: "space-between",
        alignItems: "center",
      },
    },
  },

  styles: {
    color: "primary",
    Header: {
      position: "relative",
      display: "flex",
      flexDirection: "column",
      width: "100%",
      height: config.headerImageHeight,
      color: "text",
      h1: { fontSize: [6, 8], textShadow: "0 2px 5px rgba(0,0,0,0.2)" },
      "h1, h5": { m: 0 },
      h5: { mt: 1, fontSize: 1, opacity: 0.6 },
      ".header_content": {
        width: "100%",
        height: "100%",
        position: "absolute",
        //pb: [5, 8],
        px: [5, 8],
        zIndex: 1,
        display: "flex",
        alignItems: "flex-start",
        justifyContent: "flex-end",
        button: {
          width: "100%",
          maxWidth: 7,
          height: 7,
          background: "transparent",
          border: "1px solid",
          borderColor: "text",
          color: "text",
          fontSize: "10px",
          borderRadius: 1,
          display: "flex",
          alignItems: "center",
          justifyContent: "center",
          cursor: "pointer",
          mr: 3,
          mt: 2,
          svg: {
            mt: "1px",
            ml: "2px",
          },
        },
      },
    },
    root: {
      ".episodes_list": {
        backgroundColor: "background",
        position: ["absolute", "static"],
        zIndex: 2,
        width: "100%",
        maxWidth: [375, 300],
        px: 5,
        pt: 40,
        a: {
          textDecoration: "none",
          color: "text",
          fontSize: 3,
          fontWeight: "heading",
        },
        li: {
          py: 0,
          display: "flex",
          justifyContent: "flex-start",
          alignItems: "center",
          ".summary": {
            fontSize: 2,
            lineHeight: 1.4,
            fontWeight: 300,
            opacity: 0.7,
            mt: 3,
          },
          ".active": {
            borderLeft: "3px solid",
            borderColor: "primary",
            backgroundColor: "backgroundLighten10",
          },
          a: {
            px: 5,
            py: 4,
            borderLeft: "3px solid",
            borderColor: "background",
            fontSize: 4,
            width: "100%",
          },
          ":hover": {
            a: { borderColor: "backgroundLighten10" },
            ".active": {
              borderColor: "primary",
            },
            button: {
              opacity: 1,
              ":hover": {
                opacity: 1,
              },
            },
          },
          h4: {
            mb: 0,
          },
          button: {
            position: "absolute",
            opacity: 0,
            ml: -3,
            backgroundColor: "background",
            border: "1px solid",
            borderColor: "text",
            color: "text",
            display: "flex",
            width: "100%",
            maxWidth: "24px",
            height: "24px",
            flexGrow: "1",
            borderRadius: "50%",
            alignItems: "center",
            justifyContent: "center",
            svg: { mt: "1px", ml: "1px" },
            cursor: "pointer",
          },
        },
      },
      "[data-reach-skip-link]": {
        border: "0",
        clip: "rect(0 0 0 0)",
        height: "1px",
        width: "1px",
        margin: "-1px",
        padding: "0",
        overflow: "hidden",
        position: "absolute",
        zIndex: "999",
      },
      "[data-reach-skip-link]:focus": {
        padding: "1rem",
        position: "fixed",
        top: "10px",
        left: "10px",
        backgroundColor: "background",
        width: "auto",
        height: "auto",
        clip: "auto",
      },
      backgroundColor: "background",
      lineHeight: "body",
      fontFamily: "body",
      fontSize: [2, 3],
      color: "text",
      bg: "background",
      a: {
        color: "primaryLighten50",
      },
      "a:hover": {
        color: "primaryLighten70",
      },
      article: {
        p: [5, 8],
        pb: [2, 14],
        borderLeft: "2px solid",
        borderRight: "2px solid",
        borderColor: "backgroundLighten10",
      },
      ".sidebar": {
        display: "flex",
        flexDirection: "column",
        p: [5, 8],
        pb: [13, 8],
        width: "100%",
        maxWidth: ["100%", 250],
        fontSize: "15px",
        h5: { my: 4, fontSize: 3 },
        "h5:not(:first-of-type)": { mb: 10, mt: 0 },
        ".guest": {
          fontSize: 1,
          textTransform: "uppercase",
          opacity: 0.8,
          fontWeight: "body",
        },

        li: {
          mb: 2,
          display: "flex",
          a: { color: "text" },
          svg: {
            mt: 1,
            mr: 1,
            width: "100%",
            maxWidth: 3,
            color: "text",
            opacity: 0.5,
          },
        },
      },
      hr: {
        backgroundColor: "backgroundLighten10",
        height: "2px",
      },
    },
    Container: {
      maxWidth: 1200,
    },
    a: {
      color: "primary",
      textDecoration: "none",
      ":hover": {
        color: "secondary",
        textDecoration: "underline",
      },
    },
  },
}


================================================
FILE: gatsby-theme-simplecast/src/lib/config/index.js
================================================
export default {
  podcastSeason: "1",
  headerImageHeight: [300, 400],
  spotifyUrl: "/",
  applePodcastsUrl: "/",
  googlePodcastsUrl: "/",
}


================================================
FILE: gatsby-theme-simplecast/src/lib/formatTime.js
================================================
// TODO figure out the ~~ thing

export default function formatTime(time) {
  // Hours, minutes and seconds
  const hrs = Math.floor(~~(time / 3600)) // eslint-disable-line
  const mins = Math.floor(~~((time % 3600) / 60)) // eslint-disable-line
  const secs = Math.floor(time % 60)

  // Output like "1:01" or "4:03:59" or "123:03:59"
  let ret = ""

  if (hrs > 0) {
    ret += `${hrs}:${mins < 10 ? "0" : ""}`
  }

  ret += `${mins}:${secs < 10 ? "0" : ""}`
  ret += `${secs}`
  return ret
}


================================================
FILE: gatsby-theme-simplecast/src/pages/404.js
================================================
import React from "react"

import Layout from "../components/layout"
import SEO from "../components/seo"

const NotFoundPage = () => (
  <Layout>
    <SEO title="404: Not found" />
    <h1>NOT FOUND</h1>
    <p>You just hit a route that doesn&#39;t exist... the sadness.</p>
  </Layout>
)

export default NotFoundPage


================================================
FILE: gatsby-theme-simplecast/src/pages/index.js
================================================
/** @jsx jsx */
import { jsx } from "theme-ui"
import { graphql, useStaticQuery } from "gatsby"
import Episode from "../templates/episode"

export default function Index({ data: { allEpisode, allMarkdownRemark } }) {
  const MarkdownForLatestEpisode = allMarkdownRemark.edges.filter(
    Markdown => Markdown.node.frontmatter.id === allEpisode.nodes[0].id
  )

  const data = useStaticQuery(graphql`
    {
      allEpisode {
        totalCount
        nodes {
          id
          title
          description
          number
          enclosure_url
          fields {
            slug
          }
        }
      }
      allMarkdownRemark {
        edges {
          node {
            html
            frontmatter {
              id
              title
              resources
              guestSummary
              guestName
              guestPhoto {
                childImageSharp {
                  fluid(maxWidth: 200) {
                    ...GatsbyImageSharpFluid_noBase64
                  }
                }
              }
              image {
                childImageSharp {
                  original {
                    src
                  }
                  fluid(maxWidth: 700) {
                    ...GatsbyImageSharpFluid_noBase64
                  }
                }
              }
            }
          }
        }
      }
    }
  `)
  return (
    <Episode
      data={{
        episode: data.allEpisode.nodes[0],
        markdownRemark:
          MarkdownForLatestEpisode[0] && MarkdownForLatestEpisode[0].node,
      }}
    />
  )
}


================================================
FILE: gatsby-theme-simplecast/src/templates/episode.js
================================================
/** @jsx jsx */
import { jsx } from "theme-ui"
import { graphql } from "gatsby"
import { EpisodeConsumer } from "../components/context"
import SEO from "../components/seo"
import Header from "../components/header"
import Aside from "../components/aside"
import { SkipNavContent } from "@reach/skip-nav"

function EpisodeTemplate({ data: { episode, markdownRemark } }) {
  const image = markdownRemark && markdownRemark.frontmatter.image
  const markdown = markdownRemark && markdownRemark

  return (
    <EpisodeConsumer>
      {context => (
        <div>
          <SEO
            title={episode.title && episode.title}
            image={image && image.childImageSharp.original.src}
            description={episode.description && episode.description}
          />
          <div
            sx={{
              display: "flex",
              flexDirection: ["column", "row"],
            }}
          >
            <SkipNavContent sx={{ maxWidth: ["100%", 710] }}>
              <Header context={context} episode={episode} image={image} />
              <article>
                <p>{episode.description && episode.description}</p>
                {markdown && (
                  <div dangerouslySetInnerHTML={{ __html: markdown.html }} />
                )}
              </article>
            </SkipNavContent>
            <Aside markdown={markdown} />
          </div>
        </div>
      )}
    </EpisodeConsumer>
  )
}

export default EpisodeTemplate

export const episodeQuery = graphql`
  query($id: String!) {
    episode(id: { eq: $id }) {
      id
      title
      description
      number
      enclosure_url
      fields {
        slug
      }
    }
    markdownRemark(frontmatter: { id: { eq: $id } }) {
      html
      frontmatter {
        id
        title
        resources
        guestName
        guestSummary
        guestPhoto {
          childImageSharp {
            fluid(maxWidth: 200) {
              ...GatsbyImageSharpFluid
            }
          }
        }
        image {
          childImageSharp {
            original {
              src
            }
            fluid(maxWidth: 700) {
              ...GatsbyImageSharpFluid
            }
          }
        }
      }
    }
  }
`


================================================
FILE: package.json
================================================
{
  "private": true,
  "workspaces": [
    "gatsby-theme-simplecast",
    "demo"
  ]
}
Download .txt
gitextract_3x8h2ftp/

├── .gitignore
├── LICENSE
├── README.md
├── demo/
│   ├── content/
│   │   └── episodes/
│   │       ├── 1/
│   │       │   └── index.md
│   │       ├── 2/
│   │       │   └── index.md
│   │       ├── 3/
│   │       │   └── index.md
│   │       ├── 4/
│   │       │   └── index.md
│   │       ├── 5/
│   │       │   └── index.md
│   │       ├── 6/
│   │       │   └── index.md
│   │       ├── 7/
│   │       │   └── index.md
│   │       └── 8/
│   │           └── index.md
│   ├── gatsby-config.js
│   ├── package.json
│   └── src/
│       ├── @vojtaholik/
│       │   └── gatsby-theme-simplecast/
│       │       └── lib/
│       │           └── config/
│       │               └── index.js
│       ├── gatsby-plugin-theme-ui/
│       │   └── index.js
│       └── pages/
│           ├── 404.js
│           ├── index.js
│           └── page-2.js
├── gatsby-theme-simplecast/
│   ├── .gitignore
│   ├── .prettierrc
│   ├── LICENSE
│   ├── README.md
│   ├── data/
│   │   └── mockupEpisodes.json
│   ├── gatsby-browser.js
│   ├── gatsby-config.js
│   ├── gatsby-node.js
│   ├── gatsby-ssr.js
│   ├── index.js
│   ├── package.json
│   └── src/
│       ├── components/
│       │   ├── aside.js
│       │   ├── bars.js
│       │   ├── context.js
│       │   ├── header.js
│       │   ├── image.js
│       │   ├── layout.css
│       │   ├── layout.js
│       │   ├── link.js
│       │   ├── navigation.js
│       │   ├── player.js
│       │   ├── seo.js
│       │   └── volumeBars.js
│       ├── gatsby-plugin-theme-ui/
│       │   └── index.js
│       ├── lib/
│       │   ├── config/
│       │   │   └── index.js
│       │   └── formatTime.js
│       ├── pages/
│       │   ├── 404.js
│       │   └── index.js
│       └── templates/
│           └── episode.js
└── package.json
Download .txt
SYMBOL INDEX (24 symbols across 15 files)

FILE: demo/src/pages/404.js
  function NotFound (line 3) | function NotFound() {

FILE: demo/src/pages/index.js
  function Index (line 5) | function Index({data: {allEpisode, allMarkdownRemark}}) {

FILE: demo/src/pages/page-2.js
  function Page (line 3) | function Page() {

FILE: gatsby-theme-simplecast/index.js
  function Index (line 6) | function Index({ data: { allEpisode, allMarkdownRemark } }) {

FILE: gatsby-theme-simplecast/src/components/aside.js
  function Aside (line 23) | function Aside({ markdown }) {

FILE: gatsby-theme-simplecast/src/components/context.js
  function EpisodeProvider (line 6) | function EpisodeProvider(props) {
  class EpisodeConsumer (line 40) | class EpisodeConsumer extends Component {
    method render (line 41) | render() {

FILE: gatsby-theme-simplecast/src/components/header.js
  function Header (line 8) | function Header({ context, episode, image }) {

FILE: gatsby-theme-simplecast/src/components/layout.js
  function Layout (line 7) | function Layout({ children }) {

FILE: gatsby-theme-simplecast/src/components/navigation.js
  function Navigation (line 13) | function Navigation() {

FILE: gatsby-theme-simplecast/src/components/player.js
  class Player (line 13) | class Player extends React.Component {
    method constructor (line 18) | constructor(props) {
    method componentWillUpdate (line 52) | componentWillUpdate(nextProps, nextState) {
    method componentDidUpdate (line 56) | componentDidUpdate(prevProps, prevState) {
    method render (line 196) | render() {

FILE: gatsby-theme-simplecast/src/components/seo.js
  function SEO (line 13) | function SEO({ description, lang, meta, title, image }) {

FILE: gatsby-theme-simplecast/src/components/volumeBars.js
  class VolumeBars (line 19) | class VolumeBars extends Component {
    method componentDidMount (line 24) | componentDidMount() {
    method componentDidUpdate (line 32) | componentDidUpdate(prevProps, prevState) {
    method render (line 56) | render() {

FILE: gatsby-theme-simplecast/src/lib/formatTime.js
  function formatTime (line 3) | function formatTime(time) {

FILE: gatsby-theme-simplecast/src/pages/index.js
  function Index (line 6) | function Index({ data: { allEpisode, allMarkdownRemark } }) {

FILE: gatsby-theme-simplecast/src/templates/episode.js
  function EpisodeTemplate (line 10) | function EpisodeTemplate({ data: { episode, markdownRemark } }) {
Condensed preview — 48 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (102K chars).
[
  {
    "path": ".gitignore",
    "chars": 975,
    "preview": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\n\n# Runtime data\npids\n*.pid\n*.seed\n*.pid.lock\n\n# Directo"
  },
  {
    "path": "LICENSE",
    "chars": 1068,
    "preview": "MIT License\n\nCopyright (c) 2019 Vojta Holik\n\nPermission is hereby granted, free of charge, to any person obtaining a cop"
  },
  {
    "path": "README.md",
    "chars": 351,
    "preview": "# Gatsby Theme Simplecast\n\n### [→ Preview](https://gatsby-theme-simplecast.netlify.com)\n\n<img src=\"https://cl.ly/c53006a"
  },
  {
    "path": "demo/content/episodes/1/index.md",
    "chars": 1009,
    "preview": "---\nid: a\nsummary: 'Summary. Lorem ipsum dolor sit amet.'\nresources: ['[Link](/)','[Link](/)','[Link](/)']\nguestName: 'A"
  },
  {
    "path": "demo/content/episodes/2/index.md",
    "chars": 1209,
    "preview": "---\nid: b\nsummary: 'Summary. Lorem ipsum dolor sit amet.'\nimage: './banner.png'\nguestName: 'Charlie Doe'\nguestSummary: '"
  },
  {
    "path": "demo/content/episodes/3/index.md",
    "chars": 1423,
    "preview": "---\nid: c\nsummary: 'Summary. Lorem ipsum dolor sit amet.'\nresources: ['[Badass: Making Users Awesome](https://www.goodre"
  },
  {
    "path": "demo/content/episodes/4/index.md",
    "chars": 1351,
    "preview": "---\nid: d\nsummary: 'Summary. Lorem ipsum dolor sit amet.'\nimage: './banner.png'\nresources: ['[Link](/)', '[Link](/)', '["
  },
  {
    "path": "demo/content/episodes/5/index.md",
    "chars": 1159,
    "preview": "---\nid: e\nsummary: 'Summary. Lorem ipsum dolor sit amet.'\nimage: './banner.png'\n---\n\nPancetta ham hock tongue t-bone por"
  },
  {
    "path": "demo/content/episodes/6/index.md",
    "chars": 778,
    "preview": "---\nid: f\nsummary: 'Summary. Lorem ipsum dolor sit amet.'\nimage: './banner.png'\n---\n\nHam salami biltong pastrami turkey "
  },
  {
    "path": "demo/content/episodes/7/index.md",
    "chars": 1299,
    "preview": "---\nid: g\nsummary: 'Summary. Lorem ipsum dolor sit amet.'\nimage: './banner.png'\n---\n\nLorem ipsum dolor sit amet, consect"
  },
  {
    "path": "demo/content/episodes/8/index.md",
    "chars": 1299,
    "preview": "---\nid: h\nsummary: 'Summary. Lorem ipsum dolor sit amet.'\nimage: './banner.png'\n---\n\nLorem ipsum dolor sit amet, consect"
  },
  {
    "path": "demo/gatsby-config.js",
    "chars": 413,
    "preview": "require('dotenv').config({\n  path: `.env.${process.env.NODE_ENV}`,\n})\n\nmodule.exports = {\n  plugins: [\n    {\n      resol"
  },
  {
    "path": "demo/package.json",
    "chars": 448,
    "preview": "{\n  \"private\": true,\n  \"name\": \"demo\",\n  \"version\": \"1.0.0\",\n  \"license\": \"MIT\",\n  \"scripts\": {\n    \"build\": \"gatsby bui"
  },
  {
    "path": "demo/src/@vojtaholik/gatsby-theme-simplecast/lib/config/index.js",
    "chars": 144,
    "preview": "export default {\n  podcastSeason: '1',\n  headerImageHeight: [300, 400],\n  spotifyUrl: '/',\n  applePodcastsUrl: '/',\n  go"
  },
  {
    "path": "demo/src/gatsby-plugin-theme-ui/index.js",
    "chars": 409,
    "preview": "export default {\n  useCustomProperties: true,\n  initialColorMode: 'dark',\n  colors: {\n    text: 'rgba(255, 255, 255, 0.9"
  },
  {
    "path": "demo/src/pages/404.js",
    "chars": 139,
    "preview": "import React from 'react'\n\nexport default function NotFound() {\n  return (\n    <div>\n      <h1>404 - page not found.</h1"
  },
  {
    "path": "demo/src/pages/index.js",
    "chars": 1193,
    "preview": "import React from 'react'\nimport {graphql} from 'gatsby'\nimport IndexPage from '@vojtaholik/gatsby-theme-simplecast/src/"
  },
  {
    "path": "demo/src/pages/page-2.js",
    "chars": 120,
    "preview": "import React from 'react'\n\nexport default function Page() {\n  return (\n    <div>\n      <h1>hello!</h1>\n    </div>\n  )\n}\n"
  },
  {
    "path": "gatsby-theme-simplecast/.gitignore",
    "chars": 985,
    "preview": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\n\n# Runtime data\npids\n*.pid\n*.seed\n*.pid.lock\n\n# Directo"
  },
  {
    "path": "gatsby-theme-simplecast/.prettierrc",
    "chars": 108,
    "preview": "{\n  \"endOfLine\": \"lf\",\n  \"semi\": false,\n  \"singleQuote\": false,\n  \"tabWidth\": 2,\n  \"trailingComma\": \"es5\"\n}\n"
  },
  {
    "path": "gatsby-theme-simplecast/LICENSE",
    "chars": 1076,
    "preview": "The MIT License (MIT)\n\nCopyright (c) 2015 gatsbyjs\n\nPermission is hereby granted, free of charge, to any person obtainin"
  },
  {
    "path": "gatsby-theme-simplecast/README.md",
    "chars": 1495,
    "preview": "# Gatsby Theme Simplecast\n\nGatsby theme that sources data from Simplecast API which can be combined with Markdown files "
  },
  {
    "path": "gatsby-theme-simplecast/data/mockupEpisodes.json",
    "chars": 13030,
    "preview": "{\n  \"href\": \"https://api.simplecast.com/podcasts/2ac34cab-4949-40aa-bac7-d7e3a70f0a39/episodes?limit=10\",\n  \"pages\": {\n "
  },
  {
    "path": "gatsby-theme-simplecast/gatsby-browser.js",
    "chars": 1007,
    "preview": "import React from \"react\"\nimport Layout from \"./src/components/layout\"\nimport Player from \"./src/components/player\"\nimpo"
  },
  {
    "path": "gatsby-theme-simplecast/gatsby-config.js",
    "chars": 1190,
    "preview": "module.exports = ({ markdownPath = `${__dirname}/content/episodes` }) => ({\n  siteMetadata: {\n    title: `Podcast Name`,"
  },
  {
    "path": "gatsby-theme-simplecast/gatsby-node.js",
    "chars": 2442,
    "preview": "require(\"dotenv\").config({\n  path: `.env.${process.env.NODE_ENV}`,\n})\nconst axios = require(\"axios\")\nconst crypto = requ"
  },
  {
    "path": "gatsby-theme-simplecast/gatsby-ssr.js",
    "chars": 1007,
    "preview": "import React from \"react\"\nimport Layout from \"./src/components/layout\"\nimport Player from \"./src/components/player\"\nimpo"
  },
  {
    "path": "gatsby-theme-simplecast/index.js",
    "chars": 1586,
    "preview": "/** @jsx jsx */\nimport { jsx } from \"theme-ui\"\nimport { graphql } from \"gatsby\"\nimport Episode from \"gatsby-theme-simple"
  },
  {
    "path": "gatsby-theme-simplecast/package.json",
    "chars": 2145,
    "preview": "{\n  \"name\": \"@vojtaholik/gatsby-theme-simplecast\",\n  \"main\": \"index.js\",\n  \"description\": \"Simplecast gatsby theme.\",\n  "
  },
  {
    "path": "gatsby-theme-simplecast/src/components/aside.js",
    "chars": 2691,
    "preview": "/** @jsx jsx */\nimport { jsx } from \"theme-ui\"\nimport Img from \"gatsby-image\"\nimport { FaExternalLinkAlt as ExternalLink"
  },
  {
    "path": "gatsby-theme-simplecast/src/components/bars.js",
    "chars": 538,
    "preview": "import React from \"react\"\n\nconst Bars = () => (\n  <div className=\"bars bars--paused\">\n    <div className=\"bar\" />\n    <d"
  },
  {
    "path": "gatsby-theme-simplecast/src/components/context.js",
    "chars": 862,
    "preview": "import React, { Component } from \"react\"\nimport { graphql, useStaticQuery } from \"gatsby\"\n\nconst EpisodeContext = React."
  },
  {
    "path": "gatsby-theme-simplecast/src/components/header.js",
    "chars": 1460,
    "preview": "/** @jsx jsx */\nimport { jsx, useThemeUI, Header as ThemedHeader, Box, Flex } from \"theme-ui\"\nimport Img from \"gatsby-im"
  },
  {
    "path": "gatsby-theme-simplecast/src/components/image.js",
    "chars": 977,
    "preview": "import React from \"react\"\nimport { useStaticQuery, graphql } from \"gatsby\"\nimport Img from \"gatsby-image\"\n\n/*\n * This co"
  },
  {
    "path": "gatsby-theme-simplecast/src/components/layout.css",
    "chars": 13065,
    "preview": "html {\n  font-family: sans-serif;\n  -ms-text-size-adjust: 100%;\n  -webkit-text-size-adjust: 100%;\n}\nbody {\n  margin: 0;\n"
  },
  {
    "path": "gatsby-theme-simplecast/src/components/layout.js",
    "chars": 633,
    "preview": "/** @jsx jsx */\nimport PropTypes from \"prop-types\"\nimport Navigation from \"./navigation\"\nimport \"./layout.css\"\nimport { "
  },
  {
    "path": "gatsby-theme-simplecast/src/components/link.js",
    "chars": 364,
    "preview": "import React from \"react\"\nimport GatsbyLink from \"gatsby-link\"\n\nconst Link = ({ children, to, ...other }) => {\n  const i"
  },
  {
    "path": "gatsby-theme-simplecast/src/components/navigation.js",
    "chars": 4681,
    "preview": "/** @jsx jsx */\nimport React from \"react\"\nimport { useStaticQuery, graphql } from \"gatsby\"\nimport { jsx, Flex } from \"th"
  },
  {
    "path": "gatsby-theme-simplecast/src/components/player.js",
    "chars": 11761,
    "preview": "// by Wes Bos, syntax.fm\n// https://github.com/wesbos/Syntax/blob/master/components/Player.js\n/** @jsx jsx */\nimport Rea"
  },
  {
    "path": "gatsby-theme-simplecast/src/components/seo.js",
    "chars": 1856,
    "preview": "/**\n * SEO component that queries for data with\n *  Gatsby's useStaticQuery React hook\n *\n * See: https://www.gatsbyjs.o"
  },
  {
    "path": "gatsby-theme-simplecast/src/components/volumeBars.js",
    "chars": 2334,
    "preview": "import React, { Component, Fragment } from \"react\"\n\n// TODO Fix all eslint issues\n\n// data generator -> to create 10 vol"
  },
  {
    "path": "gatsby-theme-simplecast/src/gatsby-plugin-theme-ui/index.js",
    "chars": 6494,
    "preview": "import config from \"../lib/config\"\n\nexport default {\n  useCustomProperties: true,\n  initialColorMode: \"dark\",\n  breakpoi"
  },
  {
    "path": "gatsby-theme-simplecast/src/lib/config/index.js",
    "chars": 144,
    "preview": "export default {\n  podcastSeason: \"1\",\n  headerImageHeight: [300, 400],\n  spotifyUrl: \"/\",\n  applePodcastsUrl: \"/\",\n  go"
  },
  {
    "path": "gatsby-theme-simplecast/src/lib/formatTime.js",
    "chars": 495,
    "preview": "// TODO figure out the ~~ thing\n\nexport default function formatTime(time) {\n  // Hours, minutes and seconds\n  const hrs "
  },
  {
    "path": "gatsby-theme-simplecast/src/pages/404.js",
    "chars": 318,
    "preview": "import React from \"react\"\n\nimport Layout from \"../components/layout\"\nimport SEO from \"../components/seo\"\n\nconst NotFound"
  },
  {
    "path": "gatsby-theme-simplecast/src/pages/index.js",
    "chars": 1577,
    "preview": "/** @jsx jsx */\nimport { jsx } from \"theme-ui\"\nimport { graphql, useStaticQuery } from \"gatsby\"\nimport Episode from \"../"
  },
  {
    "path": "gatsby-theme-simplecast/src/templates/episode.js",
    "chars": 2227,
    "preview": "/** @jsx jsx */\nimport { jsx } from \"theme-ui\"\nimport { graphql } from \"gatsby\"\nimport { EpisodeConsumer } from \"../comp"
  },
  {
    "path": "package.json",
    "chars": 87,
    "preview": "{\n  \"private\": true,\n  \"workspaces\": [\n    \"gatsby-theme-simplecast\",\n    \"demo\"\n  ]\n}\n"
  }
]

About this extraction

This page contains the full source code of the vojtaholik/gatsby-theme-simplecast GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 48 files (91.2 KB), approximately 27.1k tokens, and a symbol index with 24 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!