fix: update Sapper to latest (#775)
* fix: update to latest sapper fixes #416 * fix error and debug pages * requestIdleCallback makes column switching feel way nicer than double rAF * add export feature * add better csp info * workaround for sapper sub-page issue * clarify in readme about exporting * fix now config * switch from rIC to triple raf * style-loader is no longer used * update theming guide
9
.gitignore
vendored
|
@ -1,11 +1,14 @@
|
|||
.DS_Store
|
||||
node_modules
|
||||
.sapper
|
||||
__sapper__
|
||||
yarn.lock
|
||||
templates/.*
|
||||
assets/*.css
|
||||
static/*.css
|
||||
/mastodon
|
||||
mastodon.log
|
||||
assets/robots.txt
|
||||
static/robots.txt
|
||||
/inline-script-checksum.json
|
||||
/assets/inline-script.js.map
|
||||
/static/inline-script.js.map
|
||||
/templates/.*
|
||||
/src/manifest/
|
||||
|
|
10
README.md
|
@ -76,6 +76,16 @@ To keep your version of Pinafore up to date, you can use `git` to check out the
|
|||
|
||||
git checkout $(git tag -l | sort -Vr | head -n 1)
|
||||
|
||||
### Exporting
|
||||
|
||||
You can export Pinafore as a static site. Run:
|
||||
|
||||
npm run export
|
||||
|
||||
Static files will be written to `__sapper__/export`.
|
||||
Be sure to add the [CSP](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP) header printed out in the console to
|
||||
your server config!
|
||||
|
||||
## Developing and testing
|
||||
|
||||
See [CONTRIBUTING.md](https://github.com/nolanlawson/pinafore/blob/master/CONTRIBUTING.md) for
|
||||
|
|
|
@ -8,7 +8,7 @@ import { rollup } from 'rollup'
|
|||
import { terser } from 'rollup-plugin-terser'
|
||||
import replace from 'rollup-plugin-replace'
|
||||
import fromPairs from 'lodash-es/fromPairs'
|
||||
import { themes } from '../routes/_static/themes'
|
||||
import { themes } from '../src/routes/_static/themes'
|
||||
|
||||
const readFile = pify(fs.readFile.bind(fs))
|
||||
const writeFile = pify(fs.writeFile.bind(fs))
|
||||
|
@ -36,22 +36,22 @@ async function main () {
|
|||
sourcemap: true
|
||||
})
|
||||
|
||||
let fullCode = `${code}\n//# sourceMappingURL=inline-script.js.map`
|
||||
let fullCode = `${code}\n//# sourceMappingURL=/inline-script.js.map`
|
||||
|
||||
let checksum = crypto.createHash('sha256').update(fullCode).digest('base64')
|
||||
|
||||
let checksumFilepath = path.join(__dirname, '../inline-script-checksum.json')
|
||||
await writeFile(checksumFilepath, JSON.stringify({ checksum }), 'utf8')
|
||||
|
||||
let html2xxFilepath = path.join(__dirname, '../templates/2xx.html')
|
||||
let html2xxFile = await readFile(html2xxFilepath, 'utf8')
|
||||
html2xxFile = html2xxFile.replace(
|
||||
let htmlTemplateFilepath = path.join(__dirname, '../src/template.html')
|
||||
let htmlTemplateFile = await readFile(htmlTemplateFilepath, 'utf8')
|
||||
htmlTemplateFile = htmlTemplateFile.replace(
|
||||
/<!-- insert inline script here -->[\s\S]+<!-- end insert inline script here -->/,
|
||||
'<!-- insert inline script here --><script>' + fullCode + '</script><!-- end insert inline script here -->'
|
||||
)
|
||||
await writeFile(html2xxFilepath, html2xxFile, 'utf8')
|
||||
await writeFile(htmlTemplateFilepath, htmlTemplateFile, 'utf8')
|
||||
|
||||
await writeFile(path.resolve(__dirname, '../assets/inline-script.js.map'), map.toString(), 'utf8')
|
||||
await writeFile(path.resolve(__dirname, '../static/inline-script.js.map'), map.toString(), 'utf8')
|
||||
}
|
||||
|
||||
main().catch(err => {
|
||||
|
|
|
@ -16,10 +16,10 @@ const globalScss = path.join(__dirname, '../scss/global.scss')
|
|||
const defaultThemeScss = path.join(__dirname, '../scss/themes/_default.scss')
|
||||
const offlineThemeScss = path.join(__dirname, '../scss/themes/_offline.scss')
|
||||
const customScrollbarScss = path.join(__dirname, '../scss/custom-scrollbars.scss')
|
||||
const html2xxFile = path.join(__dirname, '../templates/2xx.html')
|
||||
const htmlTemplateFile = path.join(__dirname, '../src/template.html')
|
||||
const scssDir = path.join(__dirname, '../scss')
|
||||
const themesScssDir = path.join(__dirname, '../scss/themes')
|
||||
const assetsDir = path.join(__dirname, '../assets')
|
||||
const assetsDir = path.join(__dirname, '../static')
|
||||
|
||||
function doWatch () {
|
||||
let start = now()
|
||||
|
@ -44,7 +44,7 @@ async function compileGlobalSass () {
|
|||
let offlineStyle = (await renderCss(offlineThemeScss))
|
||||
let scrollbarStyle = (await renderCss(customScrollbarScss))
|
||||
|
||||
let html = await readFile(html2xxFile, 'utf8')
|
||||
let html = await readFile(htmlTemplateFile, 'utf8')
|
||||
html = html.replace(/<!-- begin inline CSS -->[\s\S]+<!-- end inline CSS -->/,
|
||||
`<!-- begin inline CSS -->\n` +
|
||||
`<style>\n${mainStyle}</style>\n` +
|
||||
|
@ -53,7 +53,7 @@ async function compileGlobalSass () {
|
|||
`<!-- end inline CSS -->`
|
||||
)
|
||||
|
||||
await writeFile(html2xxFile, html, 'utf8')
|
||||
await writeFile(htmlTemplateFile, html, 'utf8')
|
||||
}
|
||||
|
||||
async function compileThemesSass () {
|
||||
|
|
|
@ -28,13 +28,13 @@ async function main () {
|
|||
|
||||
result = `<svg xmlns="http://www.w3.org/2000/svg" style="display:none;">\n${result}\n</svg>`
|
||||
|
||||
let html2xxFilepath = path.join(__dirname, '../templates/2xx.html')
|
||||
let html2xxFile = await readFile(html2xxFilepath, 'utf8')
|
||||
html2xxFile = html2xxFile.replace(
|
||||
let htmlTemplateFilepath = path.join(__dirname, '../src/template.html')
|
||||
let htmlTemplateFile = await readFile(htmlTemplateFilepath, 'utf8')
|
||||
htmlTemplateFile = htmlTemplateFile.replace(
|
||||
/<!-- insert svg here -->[\s\S]+<!-- end insert svg here -->/,
|
||||
'<!-- insert svg here -->' + result + '<!-- end insert svg here -->'
|
||||
)
|
||||
await writeFile(html2xxFilepath, html2xxFile, 'utf8')
|
||||
await writeFile(htmlTemplateFilepath, htmlTemplateFile, 'utf8')
|
||||
}
|
||||
|
||||
main().catch(err => {
|
||||
|
|
|
@ -7,9 +7,9 @@ PATH="$PATH:./node_modules/.bin"
|
|||
|
||||
# set up robots.txt
|
||||
if [[ "$DEPLOY_TYPE" == "dev" ]]; then
|
||||
printf 'User-agent: *\nDisallow: /' > assets/robots.txt
|
||||
printf 'User-agent: *\nDisallow: /' > static/robots.txt
|
||||
else
|
||||
rm -f assets/robots.txt
|
||||
rm -f static/robots.txt
|
||||
fi
|
||||
|
||||
# if in travis, use the $NOW_TOKEN
|
||||
|
|
48
bin/print-export-info.js
Normal file
|
@ -0,0 +1,48 @@
|
|||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
|
||||
const checksum = require('../inline-script-checksum').checksum
|
||||
const html = fs.readFileSync(path.join(__dirname, '../__sapper__/export/index.html'), 'utf8')
|
||||
const nonce = html.match(/<script nonce=([^>]+)>/)[1]
|
||||
|
||||
const csp = `add_header Content-Security-Policy "script-src 'self' 'sha256-${checksum}' 'nonce-${nonce}'; ` +
|
||||
`worker-src 'self'; style-src 'self' 'unsafe-inline'; frame-src 'none'; object-src 'none'; manifest-src 'self';`
|
||||
|
||||
fs.writeFileSync(path.join(__dirname, '../__sapper__/export/.csp.nginx'), csp, 'utf8')
|
||||
|
||||
console.log(`
|
||||
|
||||
,((*
|
||||
,((* (,
|
||||
,((* (((*
|
||||
,((* (((((.
|
||||
* ,((* ((((((*
|
||||
.(/ ,((* (((((((/
|
||||
.((/ ,((* ((((((((/
|
||||
,(((/ ,((* (((((((((*
|
||||
.(((((/ ,((* ((((((((((
|
||||
,((*
|
||||
//////////((((/////////////
|
||||
/((((((((((((((((((((((((((
|
||||
/((((((((((((((((((((((((,
|
||||
*(((((((((((((((((((((/.
|
||||
./((((((((((((((((.
|
||||
|
||||
|
||||
P I N A F O R E
|
||||
|
||||
|
||||
Export successful! Static files are in:
|
||||
|
||||
__sapper__/export/
|
||||
|
||||
Be sure to add the CSP header to your nginx config:
|
||||
|
||||
server {
|
||||
include ${path.resolve(__dirname, '..')}/__sapper__/export/.csp.nginx;
|
||||
}
|
||||
|
||||
This file will be updated whenever you do \`npm run export\`.
|
||||
|
||||
Enjoy Pinafore!
|
||||
`)
|
|
@ -1,12 +1,12 @@
|
|||
import { actions } from './mastodon-data'
|
||||
import { users } from '../tests/users'
|
||||
import { postStatus } from '../routes/_api/statuses'
|
||||
import { followAccount } from '../routes/_api/follow'
|
||||
import { favoriteStatus } from '../routes/_api/favorite'
|
||||
import { reblogStatus } from '../routes/_api/reblog'
|
||||
import { postStatus } from '../src/routes/_api/statuses'
|
||||
import { followAccount } from '../src/routes/_api/follow'
|
||||
import { favoriteStatus } from '../src/routes/_api/favorite'
|
||||
import { reblogStatus } from '../src/routes/_api/reblog'
|
||||
import fetch from 'node-fetch'
|
||||
import FileApi from 'file-api'
|
||||
import { pinStatus } from '../routes/_api/pin'
|
||||
import { pinStatus } from '../src/routes/_api/pin'
|
||||
import { submitMedia } from '../tests/submitMedia'
|
||||
|
||||
global.File = FileApi.File
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
module.exports = [
|
||||
{ id: 'pinafore-logo', src: 'original-assets/sailboat.svg', title: 'Home' },
|
||||
{ id: 'pinafore-logo', src: 'original-static/sailboat.svg', title: 'Home' },
|
||||
{ id: 'fa-bell', src: 'node_modules/font-awesome-svg-png/white/svg/bell.svg', title: 'Notifications' },
|
||||
{ id: 'fa-users', src: 'node_modules/font-awesome-svg-png/white/svg/users.svg', title: 'Local' },
|
||||
{ id: 'fa-globe', src: 'node_modules/font-awesome-svg-png/white/svg/globe.svg', title: 'Federated' },
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
## Theming
|
||||
|
||||
Create a file `scss/themes/foobar.scss`, write some SCSS inside and add the following at the bottom of `scss/themes/foobar.scss`.
|
||||
This document describes how to write your own theme for Pinafore.
|
||||
|
||||
First, create a file `scss/themes/foobar.scss`, write some SCSS inside and add
|
||||
the following at the bottom of `scss/themes/foobar.scss`.
|
||||
```scss
|
||||
@import "_base.scss";
|
||||
|
||||
|
@ -9,9 +12,10 @@ body.theme-foobar {
|
|||
}
|
||||
```
|
||||
|
||||
> Note: You can find all the SCSS variables available in `scss/themes/_default.scss` while the all CSS Custom Properties available are listed in `scss/themes/_base.scss`.
|
||||
> Note: You can find all the SCSS variables available in `scss/themes/_default.scss`
|
||||
> while the all CSS Custom Properties available are listed in `scss/themes/_base.scss`.
|
||||
|
||||
Add your theme to `routes/_static/themes.js`
|
||||
Then, Add your theme to `src/routes/_static/themes.js`
|
||||
```js
|
||||
const themes = [
|
||||
...
|
||||
|
@ -24,4 +28,7 @@ const themes = [
|
|||
]
|
||||
```
|
||||
|
||||
Start the development server (`npm run dev`), go to `http://localhost:4002/settings/instances/your-instance-name` and select your newly created theme. Once you've done that, you can update your theme, and refresh the page to see the change (you don't have to restart the server).
|
||||
Start the development server (`npm run dev`), go to
|
||||
`http://localhost:4002/settings/instances/your-instance-name` and select your
|
||||
newly-created theme. Once you've done that, you can update your theme, and refresh
|
||||
the page to see the change (you don't have to restart the server).
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
// To allow CSP to work correctly, we also calculate a sha256 hash during
|
||||
// the build process and write it to inline-script-checksum.json.
|
||||
|
||||
import { testHasLocalStorageOnce } from './routes/_utils/testStorage'
|
||||
import { switchToTheme } from './routes/_utils/themeEngine'
|
||||
import { testHasLocalStorageOnce } from './src/routes/_utils/testStorage'
|
||||
import { switchToTheme } from './src/routes/_utils/themeEngine'
|
||||
|
||||
window.__themeColors = process.env.THEME_COLORS
|
||||
|
||||
|
|
Before Width: | Height: | Size: 708 B After Width: | Height: | Size: 708 B |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 403 B After Width: | Height: | Size: 403 B |
Before Width: | Height: | Size: 326 B After Width: | Height: | Size: 326 B |
929
package-lock.json
generated
41
package.json
|
@ -3,15 +3,16 @@
|
|||
"description": "Alternative web client for Mastodon",
|
||||
"version": "0.13.0",
|
||||
"scripts": {
|
||||
"lint": "standard && standard --plugin html 'routes/**/*.html'",
|
||||
"lint-fix": "standard --fix && standard --fix --plugin html 'routes/**/*.html'",
|
||||
"lint": "standard && standard --plugin html 'src/routes/**/*.html'",
|
||||
"lint-fix": "standard --fix && standard --fix --plugin html 'src/routes/**/*.html'",
|
||||
"dev": "run-s build-svg build-inline-script serve-dev",
|
||||
"serve-dev": "run-p --race build-sass-watch serve",
|
||||
"serve": "node server.js",
|
||||
"serve-dev": "run-p --race build-sass-watch sapper-dev",
|
||||
"sapper-dev": "cross-env PORT=4002 sapper dev",
|
||||
"sapper-prod": "cross-env PORT=4002 node __sapper__/build",
|
||||
"build": "cross-env NODE_ENV=production npm run build-steps",
|
||||
"build-steps": "run-s globalize-css build-sass build-svg build-inline-script sapper-build deglobalize-css",
|
||||
"sapper-build": "sapper build",
|
||||
"start": "cross-env NODE_ENV=production npm run serve",
|
||||
"start": "cross-env NODE_ENV=production npm run sapper-prod",
|
||||
"build-and-start": "run-s build start",
|
||||
"build-svg": "node ./bin/build-svg.js",
|
||||
"build-inline-script": "node -r esm ./bin/build-inline-script.js",
|
||||
|
@ -36,7 +37,10 @@
|
|||
"deploy-prod": "DEPLOY_TYPE=prod ./bin/deploy.sh",
|
||||
"deploy-dev": "DEPLOY_TYPE=dev ./bin/deploy.sh",
|
||||
"deploy-all-travis": "./bin/deploy-all-travis.sh",
|
||||
"backup-mastodon-data": "./bin/backup-mastodon-data.sh"
|
||||
"backup-mastodon-data": "./bin/backup-mastodon-data.sh",
|
||||
"sapper-export": "sapper export",
|
||||
"print-export-info": "node ./bin/print-export-info.js",
|
||||
"export": "run-s build sapper-export print-export-info"
|
||||
},
|
||||
"dependencies": {
|
||||
"@gamestdio/websocket": "^0.2.8",
|
||||
|
@ -48,6 +52,7 @@
|
|||
"cross-env": "^5.2.0",
|
||||
"css-loader": "^2.0.0",
|
||||
"emoji-regex": "^7.0.1",
|
||||
"encoding": "^0.1.12",
|
||||
"escape-html": "^1.0.3",
|
||||
"esm": "^3.0.84",
|
||||
"events-light": "^1.0.5",
|
||||
|
@ -63,12 +68,10 @@
|
|||
"localstorage-memory": "^1.0.3",
|
||||
"lodash-es": "^4.17.11",
|
||||
"lodash-webpack-plugin": "^0.11.5",
|
||||
"mini-css-extract-plugin": "^0.5.0",
|
||||
"mkdirp": "^0.5.1",
|
||||
"node-fetch": "^2.3.0",
|
||||
"node-sass": "^4.10.0",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"optimize-css-assets-webpack-plugin": "^5.0.1",
|
||||
"p-any": "^1.1.0",
|
||||
"page-lifecycle": "^0.1.1",
|
||||
"performance-now": "^2.1.0",
|
||||
|
@ -78,10 +81,9 @@
|
|||
"rollup": "^0.67.4",
|
||||
"rollup-plugin-replace": "^2.1.0",
|
||||
"rollup-plugin-terser": "^3.0.0",
|
||||
"sapper": "github:nolanlawson/sapper#for-pinafore-9",
|
||||
"sapper": "github:nolanlawson/sapper#nolan/sw-index-html-built",
|
||||
"serve-static": "^1.13.2",
|
||||
"stringz": "^1.0.0",
|
||||
"style-loader": "^0.23.1",
|
||||
"svelte": "^2.15.3",
|
||||
"svelte-extras": "^2.0.2",
|
||||
"svelte-loader": "^2.11.0",
|
||||
|
@ -89,6 +91,7 @@
|
|||
"svgo": "^1.1.1",
|
||||
"terser-webpack-plugin": "^1.1.0",
|
||||
"tiny-queue": "^0.2.1",
|
||||
"uuid": "^3.3.2",
|
||||
"web-animations-js": "^2.3.1",
|
||||
"webpack": "^4.26.1",
|
||||
"webpack-bundle-analyzer": "^3.0.3"
|
||||
|
@ -140,8 +143,8 @@
|
|||
],
|
||||
"ignore": [
|
||||
"dist",
|
||||
"routes/_utils/asyncModules.js",
|
||||
"routes/_components/dialog/asyncDialogs.js"
|
||||
"src/routes/_utils/asyncModules.js",
|
||||
"src/routes/_components/dialog/asyncDialogs.js"
|
||||
]
|
||||
},
|
||||
"esm": {
|
||||
|
@ -154,18 +157,16 @@
|
|||
"NODE_ENV": "production"
|
||||
},
|
||||
"files": [
|
||||
"assets",
|
||||
"bin",
|
||||
"original-assets",
|
||||
"routes",
|
||||
"inline-script.js",
|
||||
"original-static",
|
||||
"scss",
|
||||
"templates",
|
||||
"src",
|
||||
"static",
|
||||
"package.json",
|
||||
"package-lock.json",
|
||||
"server.js",
|
||||
"inline-script.js",
|
||||
"webpack.client.config.js",
|
||||
"webpack.server.config.js"
|
||||
"webpack",
|
||||
"webpack.config.js"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^8.0.0"
|
||||
|
|
|
@ -1,36 +0,0 @@
|
|||
<Nav {page} />
|
||||
|
||||
<div class="main-content">
|
||||
<main class="{infiniteScrollPage ? 'infinite-scroll-page' : ''}">
|
||||
<slot></slot>
|
||||
</main>
|
||||
{#if !$isUserLoggedIn && page === 'home'}
|
||||
<InformationalFooter />
|
||||
{/if}
|
||||
</div>
|
||||
<style>
|
||||
/* this avoids a flash of the background color when switching timelines */
|
||||
.infinite-scroll-page {
|
||||
min-height: 100vh;
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
import Nav from './Nav.html'
|
||||
import { store } from '../_store/store'
|
||||
import InformationalFooter from './InformationalFooter.html'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Nav,
|
||||
InformationalFooter
|
||||
},
|
||||
oncreate () {
|
||||
let { page } = this.get()
|
||||
this.store.set({ currentPage: page })
|
||||
},
|
||||
store: () => store,
|
||||
computed: {
|
||||
infiniteScrollPage: ({ $isUserLoggedIn, page }) => $isUserLoggedIn && page !== 'settings'
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -1,21 +0,0 @@
|
|||
<Title name="Profile" />
|
||||
<Layout page='tags'>
|
||||
<LazyPage {pageComponent} {params} />
|
||||
</Layout>
|
||||
<script>
|
||||
import Layout from '../_components/Layout.html'
|
||||
import Title from '../_components/Title.html'
|
||||
import LazyPage from '../_components/LazyPage.html'
|
||||
import pageComponent from '../_pages/accounts/[accountId].html'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Layout,
|
||||
Title,
|
||||
LazyPage
|
||||
},
|
||||
data: () => ({
|
||||
pageComponent
|
||||
})
|
||||
}
|
||||
</script>
|
14
src/client.js
Normal file
|
@ -0,0 +1,14 @@
|
|||
import * as sapper from '../__sapper__/client.js'
|
||||
import { loadPolyfills } from './routes/_utils/loadPolyfills'
|
||||
import './routes/_utils/serviceWorkerClient'
|
||||
import './routes/_utils/historyEvents'
|
||||
import './routes/_utils/loadingMask'
|
||||
|
||||
loadPolyfills().then(() => {
|
||||
console.log('init()')
|
||||
sapper.start({ target: document.querySelector('#sapper') })
|
||||
})
|
||||
|
||||
if (module.hot) {
|
||||
module.hot.accept()
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
import { getAccessTokenFromAuthCode, registerApplication, generateAuthLink } from '../_api/oauth'
|
||||
import { getInstanceInfo } from '../_api/instance'
|
||||
import { goto } from 'sapper/runtime.js'
|
||||
import { goto } from '../../../__sapper__/client'
|
||||
import { switchToTheme } from '../_utils/themeEngine'
|
||||
import { store } from '../_store/store'
|
||||
import { updateVerifyCredentialsForInstance } from './instances'
|
|
@ -2,7 +2,7 @@ import { getVerifyCredentials } from '../_api/user'
|
|||
import { store } from '../_store/store'
|
||||
import { switchToTheme } from '../_utils/themeEngine'
|
||||
import { toast } from '../_utils/toast'
|
||||
import { goto } from 'sapper/runtime.js'
|
||||
import { goto } from '../../../__sapper__/client'
|
||||
import { cacheFirstUpdateAfter } from '../_utils/sync'
|
||||
import { getInstanceInfo } from '../_api/instance'
|
||||
import { database } from '../_database/database'
|
|
@ -32,8 +32,8 @@
|
|||
</style>
|
||||
<script>
|
||||
import { store } from '../_store/store'
|
||||
import LoadingPage from '../_components/LoadingPage.html'
|
||||
import AccountSearchResult from '../_components/search/AccountSearchResult.html'
|
||||
import LoadingPage from './LoadingPage.html'
|
||||
import AccountSearchResult from './search/AccountSearchResult.html'
|
||||
import { toast } from '../_utils/toast'
|
||||
import { on } from '../_utils/eventBus'
|
||||
|
||||
|
@ -71,4 +71,4 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</script>
|
|
@ -1,4 +1,4 @@
|
|||
<!-- toggled in 2xx.html based on whether the user is logged in or not -->
|
||||
<!-- toggled in template.html based on whether the user is logged in or not -->
|
||||
<div class="hidden-from-ssr">
|
||||
<slot></slot>
|
||||
</div>
|
||||
|
@ -6,4 +6,4 @@
|
|||
.hidden-from-ssr {
|
||||
opacity: 0;
|
||||
}
|
||||
</style>
|
||||
</style>
|
|
@ -2,6 +2,7 @@
|
|||
<svelte:component this={pageComponent} {params} />
|
||||
{/if}
|
||||
<script>
|
||||
import { doubleRAF } from '../_utils/doubleRAF'
|
||||
// On the very first page load, avoid doing a "reveal" because
|
||||
// it leads to a flash between when the SSR is shown, the two frame we hide it,
|
||||
// and then when we show it again.
|
||||
|
@ -13,14 +14,12 @@
|
|||
export default {
|
||||
oncreate () {
|
||||
firstTime = false
|
||||
requestAnimationFrame(() => {
|
||||
requestAnimationFrame(() => {
|
||||
this.set({ revealed: true })
|
||||
})
|
||||
})
|
||||
// Yes, triple raf. This is to ensure the NavItem animation plays before we
|
||||
// start rendering the new page.
|
||||
doubleRAF(() => requestAnimationFrame(() => this.set({ revealed: true })))
|
||||
},
|
||||
data: () => ({
|
||||
revealed: !process.browser || firstTime
|
||||
})
|
||||
}
|
||||
</script>
|
||||
</script>
|
Before Width: | Height: | Size: 443 B After Width: | Height: | Size: 443 B |