├── static ├── .nojekyll ├── favicon.png ├── robots.txt └── 404.html ├── .npmrc ├── src ├── routes │ ├── [pageId] │ │ ├── +page.ts │ │ ├── +page.server.ts │ │ └── +page.svelte │ ├── +layout.ts │ ├── components │ │ ├── BlockItem │ │ │ ├── Divider.svelte │ │ │ ├── ColumnList.svelte │ │ │ ├── Waiting.svelte │ │ │ ├── Audio.svelte │ │ │ ├── SyncPointerBlock.svelte │ │ │ ├── Quote.svelte │ │ │ ├── Callout.svelte │ │ │ ├── Text.svelte │ │ │ ├── Column.svelte │ │ │ ├── File.svelte │ │ │ ├── Bookmark.svelte │ │ │ ├── List.svelte │ │ │ ├── EOI.svelte │ │ │ ├── Header.svelte │ │ │ ├── Code.svelte │ │ │ └── ViewPage.svelte │ │ ├── BlockRender.svelte │ │ ├── Pdf.svelte │ │ ├── Checkbox.svelte │ │ ├── GracefulImage.svelte │ │ ├── Equation.svelte │ │ ├── Collection │ │ │ ├── CollectionColumnTitle.svelte │ │ │ ├── CollectionPropertyCheckbox.svelte │ │ │ ├── ColletionPropertySelect.svelte │ │ │ ├── CollectionPropertyFile.svelte │ │ │ ├── CollectionPropteryFormula.svelte │ │ │ ├── CollectionPropertyTime.svelte │ │ │ ├── CollectionViewTabs.svelte │ │ │ ├── CollectionViewGallery.svelte │ │ │ ├── CollectionPropertyNumber.svelte │ │ │ ├── CollectionRow.svelte │ │ │ ├── Collection.svelte │ │ │ ├── CollectionProperty.svelte │ │ │ ├── CollectionCard.svelte │ │ │ └── eval-formula.ts │ │ ├── Render.svelte │ │ ├── TextPage.svelte │ │ ├── RecursiveBlock.svelte │ │ ├── TextLink.svelte │ │ ├── TextExternalLink.svelte │ │ ├── PageTitle.svelte │ │ ├── TextPlain.svelte │ │ ├── Text.svelte │ │ ├── PageIcon.svelte │ │ ├── LiteYoutubeEmbed.svelte │ │ ├── AssetWrapper.svelte │ │ ├── Block.svelte │ │ ├── Header.svelte │ │ └── Asset.svelte │ ├── +page.ts │ ├── +layout.svelte │ ├── +page.server.ts │ ├── +page.svelte │ ├── store.ts │ ├── Header.svelte │ └── prism-theme.css ├── lib │ ├── images │ │ ├── svelte-welcome.png │ │ ├── svelte-welcome.webp │ │ ├── default-page-icon.svg │ │ ├── github.svg │ │ └── svelte-logo.svg │ ├── icons │ │ ├── check.svelte │ │ ├── type-relation.svelte │ │ ├── chevron-down-icon.svelte │ │ ├── type-select.svelte │ │ ├── default-page-icon.svelte │ │ ├── collection-view-board.svelte │ │ ├── clear-icon.svelte │ │ ├── collection-view-list.svelte │ │ ├── file-icon.svelte │ │ ├── type-multi-select.svelte │ │ ├── type-relation.svg │ │ ├── collection-view-gallery.svelte │ │ ├── check.svg │ │ ├── type-date.svelte │ │ ├── collection-view-table.svelte │ │ ├── type-text.svelte │ │ ├── type-person-2.svelte │ │ ├── type-number.svelte │ │ ├── type-checkbox.svelte │ │ ├── type-timestamp.svelte │ │ ├── type-formula.svelte │ │ ├── link-icon.svelte │ │ ├── type-url.svelte │ │ ├── type-select.svg │ │ ├── type-file.svelte │ │ ├── type-phone-number.svelte │ │ ├── collection-view-board.svg │ │ ├── collection-view-list.svg │ │ ├── copy.svg │ │ ├── copy.svelte │ │ ├── collection-view-gallery.svg │ │ ├── empty-icon.svelte │ │ ├── type-person-2.svg │ │ ├── collection-view-icon.svelte │ │ ├── type-date.svg │ │ ├── type-title.svelte │ │ ├── type-person.svelte │ │ ├── search-icon.tsx │ │ ├── type-checkbox.svg │ │ ├── type-multi-select.svg │ │ ├── type-text.svg │ │ ├── collection-view-table.svg │ │ ├── type-phone-number.svg │ │ ├── type-formula.svg │ │ ├── type-file.svg │ │ ├── type-email.svelte │ │ ├── type-number.svg │ │ ├── loading-icon.svelte │ │ ├── type-url.svg │ │ ├── type-timestamp.svg │ │ ├── property-icon.svelte │ │ ├── type-person.svg │ │ ├── collection-view-calendar.svelte │ │ ├── type-email.svg │ │ ├── type-title.svg │ │ ├── type-github.svelte │ │ └── collection-view-calendar.svg │ ├── notion-api.ts │ ├── style-object.ts │ ├── get-canonical-page-id.ts │ ├── site-config.ts │ ├── map-image-url.ts │ ├── get-config-value.ts │ ├── types.ts │ ├── get-site-map.ts │ ├── notion.ts │ └── config.ts ├── app.d.ts ├── app.html ├── hooks.server.ts └── site.config.ts ├── 2023-04-13-20-09-36.png ├── .eslintignore ├── .gitignore ├── .prettierignore ├── vite.config.ts ├── .prettierrc ├── .eslintrc.cjs ├── tsconfig.json ├── svelte.config.js ├── .github └── workflows │ └── build.yml ├── README.md ├── package.json └── scripts └── sitemap.js /static/.nojekyll: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | engine-strict=true 2 | -------------------------------------------------------------------------------- /src/routes/[pageId]/+page.ts: -------------------------------------------------------------------------------- 1 | export const prerender = 'auto'; -------------------------------------------------------------------------------- /src/routes/+layout.ts: -------------------------------------------------------------------------------- 1 | export const prerender = true; 2 | export const csr = false; 3 | -------------------------------------------------------------------------------- /static/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tiodot/svelte-notion/HEAD/static/favicon.png -------------------------------------------------------------------------------- /static/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /2023-04-13-20-09-36.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tiodot/svelte-notion/HEAD/2023-04-13-20-09-36.png -------------------------------------------------------------------------------- /src/lib/images/svelte-welcome.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tiodot/svelte-notion/HEAD/src/lib/images/svelte-welcome.png -------------------------------------------------------------------------------- /src/lib/images/svelte-welcome.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tiodot/svelte-notion/HEAD/src/lib/images/svelte-welcome.webp -------------------------------------------------------------------------------- /src/lib/icons/check.svelte: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/lib/icons/type-relation.svelte: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/routes/components/BlockItem/Divider.svelte: -------------------------------------------------------------------------------- 1 | 4 | 5 |
-------------------------------------------------------------------------------- /src/lib/icons/chevron-down-icon.svelte: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/lib/notion-api.ts: -------------------------------------------------------------------------------- 1 | import { NotionAPI } from 'notion-client' 2 | 3 | export const notion = new NotionAPI({ 4 | apiBaseUrl: process.env.NOTION_API_BASE_URL 5 | }) 6 | -------------------------------------------------------------------------------- /src/routes/components/BlockRender.svelte: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/routes/+page.ts: -------------------------------------------------------------------------------- 1 | // since there's no dynamic data here, we can prerender 2 | // it so that it gets served as a static asset in production 3 | 4 | export const prerender = true; 5 | -------------------------------------------------------------------------------- /src/routes/components/BlockItem/ColumnList.svelte: -------------------------------------------------------------------------------- 1 | 4 | 5 |
6 | 7 |
8 | -------------------------------------------------------------------------------- /src/routes/components/BlockItem/Waiting.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 |
empty {block.type}
7 | -------------------------------------------------------------------------------- /src/lib/icons/type-select.svelte: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /build 4 | /.svelte-kit 5 | /package 6 | .env 7 | .env.* 8 | !.env.example 9 | 10 | # Ignore files for PNPM, NPM and YARN 11 | pnpm-lock.yaml 12 | package-lock.json 13 | yarn.lock 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /build 4 | /.svelte-kit 5 | /package 6 | .env 7 | .env.* 8 | !.env.example 9 | .vercel 10 | .output 11 | vite.config.js.timestamp-* 12 | vite.config.ts.timestamp-* 13 | yarn.lock 14 | .idea -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /build 4 | /.svelte-kit 5 | /package 6 | .env 7 | .env.* 8 | !.env.example 9 | 10 | # Ignore files for PNPM, NPM and YARN 11 | pnpm-lock.yaml 12 | package-lock.json 13 | yarn.lock 14 | -------------------------------------------------------------------------------- /src/lib/icons/default-page-icon.svelte: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /vite.config.ts: -------------------------------------------------------------------------------- 1 | import { sveltekit } from '@sveltejs/kit/vite'; 2 | import { defineConfig } from 'vite'; 3 | 4 | export default defineConfig({ 5 | plugins: [sveltekit()], 6 | define: { 7 | "process.env": { 8 | DEBUG: true, 9 | } 10 | } 11 | }); 12 | -------------------------------------------------------------------------------- /src/lib/images/default-page-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "useTabs": true, 3 | "singleQuote": true, 4 | "trailingComma": "none", 5 | "printWidth": 100, 6 | "plugins": ["prettier-plugin-svelte"], 7 | "pluginSearchDirs": ["."], 8 | "overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }] 9 | } 10 | -------------------------------------------------------------------------------- /src/lib/icons/collection-view-board.svelte: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/lib/icons/clear-icon.svelte: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/lib/icons/collection-view-list.svelte: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/app.d.ts: -------------------------------------------------------------------------------- 1 | // See https://kit.svelte.dev/docs/types#app 2 | // for information about these interfaces 3 | declare global { 4 | namespace App { 5 | // interface Error {} 6 | // interface Locals {} 7 | // interface PageData {} 8 | // interface Platform {} 9 | } 10 | } 11 | 12 | export {}; 13 | -------------------------------------------------------------------------------- /src/lib/icons/file-icon.svelte: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/lib/icons/type-multi-select.svelte: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/lib/icons/type-relation.svg: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | -------------------------------------------------------------------------------- /src/lib/icons/collection-view-gallery.svelte: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/routes/components/Pdf.svelte: -------------------------------------------------------------------------------- 1 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /static/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Document 9 | 10 | 11 | 12 | 404 Not Found 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/lib/icons/check.svg: -------------------------------------------------------------------------------- 1 | 2 | 8 | 11 | -------------------------------------------------------------------------------- /src/lib/icons/type-date.svelte: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/lib/icons/collection-view-table.svelte: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/lib/style-object.ts: -------------------------------------------------------------------------------- 1 | export const camelCaseToDash = (obj: Record) => { 2 | return Object.keys(obj).reduce((pre, cur) => { 3 | if (obj[cur] === undefined) { 4 | return pre; 5 | } 6 | const dashKey = cur.replaceAll(/[A-W]/g, ($0) => `-${$0.toLocaleLowerCase()}`); 7 | return pre + `;${dashKey}: ${obj[cur]}`; 8 | }, ''); 9 | }; 10 | -------------------------------------------------------------------------------- /src/app.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | %sveltekit.head% 8 | 9 | 10 |
%sveltekit.body%
11 | 12 | 13 | -------------------------------------------------------------------------------- /src/routes/+layout.svelte: -------------------------------------------------------------------------------- 1 | 8 | 9 |
10 | 11 |
12 | 13 | 20 | -------------------------------------------------------------------------------- /src/lib/icons/type-text.svelte: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/routes/components/BlockItem/Audio.svelte: -------------------------------------------------------------------------------- 1 | 9 | 10 |
11 | 12 |
-------------------------------------------------------------------------------- /src/lib/icons/type-person-2.svelte: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/routes/components/BlockItem/SyncPointerBlock.svelte: -------------------------------------------------------------------------------- 1 | 12 | 13 | 14 | {#if referenceBlockId} 15 | 16 | {/if} 17 | -------------------------------------------------------------------------------- /src/lib/icons/type-number.svelte: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/routes/components/BlockItem/Quote.svelte: -------------------------------------------------------------------------------- 1 | 7 | 8 | {#if block.properties} 9 | {@const blockColor = block.format?.block_color} 10 |
11 | {/if} 12 | -------------------------------------------------------------------------------- /src/lib/icons/type-checkbox.svelte: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/routes/components/Checkbox.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | {#if isChecked} 8 |
9 | 10 | 11 | 12 |
13 | {:else} 14 |
15 | {/if} 16 | 17 | -------------------------------------------------------------------------------- /src/lib/icons/type-timestamp.svelte: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/routes/components/GracefulImage.svelte: -------------------------------------------------------------------------------- 1 | 14 | 15 | {#if userInfo} 16 | {userInfo.given_name} 17 | {/if} 18 | -------------------------------------------------------------------------------- /src/lib/icons/type-formula.svelte: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/routes/components/Equation.svelte: -------------------------------------------------------------------------------- 1 | 15 | {#if value} 16 | 17 | 18 | {value} 19 | 20 | 21 | {/if} 22 | -------------------------------------------------------------------------------- /src/lib/icons/link-icon.svelte: -------------------------------------------------------------------------------- 1 | 2 | 8 | 12 | 13 | -------------------------------------------------------------------------------- /src/routes/components/Collection/CollectionColumnTitle.svelte: -------------------------------------------------------------------------------- 1 | 8 | 9 |
10 | 14 | 15 |
{schema.name}
16 |
-------------------------------------------------------------------------------- /src/routes/components/Render.svelte: -------------------------------------------------------------------------------- 1 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/routes/components/Collection/CollectionPropertyCheckbox.svelte: -------------------------------------------------------------------------------- 1 | 11 | 12 |
13 | 14 | {schema.name} 15 |
-------------------------------------------------------------------------------- /src/lib/icons/type-url.svelte: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/lib/icons/type-select.svg: -------------------------------------------------------------------------------- 1 | 2 | 8 | 11 | -------------------------------------------------------------------------------- /.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | parser: '@typescript-eslint/parser', 4 | extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended', 'prettier'], 5 | plugins: ['svelte3', '@typescript-eslint'], 6 | ignorePatterns: ['*.cjs'], 7 | overrides: [{ files: ['*.svelte'], processor: 'svelte3/svelte3' }], 8 | settings: { 9 | 'svelte3/typescript': () => require('typescript') 10 | }, 11 | parserOptions: { 12 | sourceType: 'module', 13 | ecmaVersion: 2020 14 | }, 15 | env: { 16 | browser: true, 17 | es2017: true, 18 | node: true 19 | } 20 | }; 21 | -------------------------------------------------------------------------------- /src/lib/icons/type-file.svelte: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/lib/icons/type-phone-number.svelte: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/routes/components/TextPage.svelte: -------------------------------------------------------------------------------- 1 | 14 | 15 | {#if linkedBlock} 16 | 17 | 18 | 19 | {/if} 20 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./.svelte-kit/tsconfig.json", 3 | "compilerOptions": { 4 | "allowJs": true, 5 | "checkJs": true, 6 | "esModuleInterop": true, 7 | "forceConsistentCasingInFileNames": true, 8 | "resolveJsonModule": true, 9 | "skipLibCheck": true, 10 | "sourceMap": true, 11 | "strict": true 12 | } 13 | // Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias 14 | // 15 | // If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes 16 | // from the referenced tsconfig.json - TypeScript does not merge them in 17 | } 18 | -------------------------------------------------------------------------------- /src/routes/[pageId]/+page.server.ts: -------------------------------------------------------------------------------- 1 | import { domain, site } from '$lib/config'; 2 | import { resolveNotionPage } from '$lib/notion'; 3 | import { getBlockTitle } from 'notion-utils'; 4 | 5 | export async function load({ params }: {params: {pageId: string}}) { 6 | console.log('request pageId:', params) 7 | const recordMap = await resolveNotionPage(domain, params.pageId); 8 | // console.log('props:', recordMap); 9 | const keys = Object.keys(recordMap.block) || {}; 10 | const block = recordMap.block[keys[0]].value; 11 | const title = getBlockTitle(block, recordMap); 12 | return { title, recordMap, site }; 13 | } 14 | -------------------------------------------------------------------------------- /src/lib/icons/collection-view-board.svg: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | -------------------------------------------------------------------------------- /src/lib/icons/collection-view-list.svg: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | -------------------------------------------------------------------------------- /src/routes/components/BlockItem/Callout.svelte: -------------------------------------------------------------------------------- 1 | 16 | 17 |
18 | 19 | 20 |
21 |
22 | -------------------------------------------------------------------------------- /src/routes/components/Collection/ColletionPropertySelect.svelte: -------------------------------------------------------------------------------- 1 | 14 | 15 | {#each values as value, index (index)} 16 |
17 | {value} 18 |
19 | {/each} 20 | -------------------------------------------------------------------------------- /src/lib/icons/copy.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/routes/components/BlockItem/Text.svelte: -------------------------------------------------------------------------------- 1 | 8 | 9 | {#if !block.properties && !block.content?.length} 10 |
 
11 | {:else } 12 |
14 | {#if block.properties?.title} 15 | 16 | {/if} 17 | 18 | 19 |
20 | {/if} -------------------------------------------------------------------------------- /src/lib/icons/copy.svelte: -------------------------------------------------------------------------------- 1 | 2 | 8 | 12 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/routes/components/BlockItem/Column.svelte: -------------------------------------------------------------------------------- 1 | 15 | 16 |
17 |
18 | -------------------------------------------------------------------------------- /src/lib/icons/collection-view-gallery.svg: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | -------------------------------------------------------------------------------- /src/lib/icons/empty-icon.svelte: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/lib/get-canonical-page-id.ts: -------------------------------------------------------------------------------- 1 | import type { ExtendedRecordMap } from 'notion-types' 2 | import { 3 | getCanonicalPageId as getCanonicalPageIdImpl, 4 | parsePageId 5 | } from 'notion-utils' 6 | 7 | import { inversePageUrlOverrides } from './config' 8 | 9 | export function getCanonicalPageId( 10 | pageId: string, 11 | recordMap: ExtendedRecordMap, 12 | { uuid = true }: { uuid?: boolean } = {} 13 | ): string | null { 14 | const cleanPageId = parsePageId(pageId, { uuid: false }) 15 | if (!cleanPageId) { 16 | return null 17 | } 18 | 19 | const override = inversePageUrlOverrides[cleanPageId] 20 | if (override) { 21 | return override 22 | } else { 23 | return getCanonicalPageIdImpl(pageId, recordMap, { 24 | uuid 25 | }) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/lib/icons/type-person-2.svg: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | -------------------------------------------------------------------------------- /src/hooks.server.ts: -------------------------------------------------------------------------------- 1 | import type { Handle } from '@sveltejs/kit'; 2 | 3 | import { minify } from 'html-minifier'; 4 | import { building } from '$app/environment'; 5 | 6 | const minification_options = { 7 | collapseBooleanAttributes: true, 8 | collapseWhitespace: true, 9 | html5: true, 10 | ignoreCustomComments: [/^#/], 11 | minifyCSS: true, 12 | minifyJS: false, 13 | removeComments: true, // some hydration code needs comments, so leave them in 14 | }; 15 | 16 | export const handle = (async ({ event, resolve }) => { 17 | let page = ''; 18 | 19 | return resolve(event, { 20 | transformPageChunk: ({ html, done }) => { 21 | page += html; 22 | if (done) { 23 | return building ? minify(page, minification_options) : page; 24 | } 25 | } 26 | }); 27 | }) satisfies Handle; 28 | -------------------------------------------------------------------------------- /src/routes/components/Collection/CollectionPropertyFile.svelte: -------------------------------------------------------------------------------- 1 | 13 | 14 | {#each files as file, index (index)} 15 | 21 | 22 | 23 | {/each} 24 | -------------------------------------------------------------------------------- /src/lib/icons/collection-view-icon.svelte: -------------------------------------------------------------------------------- 1 | 22 | -------------------------------------------------------------------------------- /src/routes/components/RecursiveBlock.svelte: -------------------------------------------------------------------------------- 1 | 16 | 17 | {#if block} 18 | 19 | {#each block.content || [] as contentBlockId (contentBlockId)} 20 | {@const contentBlock = $recordMapStore.block[contentBlockId]?.value} 21 | 22 | {/each} 23 | 24 | {/if} 25 | -------------------------------------------------------------------------------- /src/lib/icons/type-date.svg: -------------------------------------------------------------------------------- 1 | 2 | 8 | 11 | -------------------------------------------------------------------------------- /src/lib/site-config.ts: -------------------------------------------------------------------------------- 1 | import type * as types from './types'; 2 | 3 | export interface SiteConfig { 4 | rootNotionPageId: string; 5 | rootNotionSpaceId?: string; 6 | 7 | name: string; 8 | domain: string; 9 | author: string; 10 | description?: string; 11 | 12 | defaultPageIcon?: string | null; 13 | defaultPageCover?: string | null; 14 | defaultPageCoverPosition?: number | null; 15 | 16 | isSearchEnabled?: boolean; 17 | 18 | includeNotionIdInUrls?: boolean; 19 | pageUrlOverrides?: types.PageUrlOverridesMap; 20 | pageUrlAdditions?: types.PageUrlOverridesMap; 21 | 22 | navigationStyle?: types.NavigationStyle; 23 | } 24 | 25 | export interface NavigationLink { 26 | title: string; 27 | pageId?: string; 28 | url?: string; 29 | } 30 | 31 | export const siteConfig = (config: SiteConfig): SiteConfig => { 32 | return config; 33 | }; 34 | -------------------------------------------------------------------------------- /src/routes/+page.server.ts: -------------------------------------------------------------------------------- 1 | import { site } from '$lib/config'; 2 | import { resolveNotionPage } from '$lib/notion'; 3 | import { error } from '@sveltejs/kit'; 4 | import { getBlockTitle } from 'notion-utils'; 5 | 6 | export async function load() { 7 | const recordMap = await resolveNotionPage(); 8 | // console.log('props:', recordMap); 9 | 10 | const keys = Object.keys(recordMap.block) || {}; 11 | const block = recordMap.block[keys[0]].value; 12 | 13 | if (block.type.startsWith('collection_view') && block.collection_id) { 14 | const collection = recordMap.collection[block.collection_id].value; 15 | block.format = { 16 | ...block.format, 17 | page_cover: collection.cover || undefined, 18 | page_icon: collection.icon 19 | }; 20 | } 21 | const title = getBlockTitle(block, recordMap); 22 | return { title, recordMap, site }; 23 | } 24 | -------------------------------------------------------------------------------- /src/routes/components/Collection/CollectionPropteryFormula.svelte: -------------------------------------------------------------------------------- 1 | 28 | 29 | {content} 30 | -------------------------------------------------------------------------------- /src/lib/icons/type-title.svelte: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/lib/icons/type-person.svelte: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/lib/icons/search-icon.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | 3 | import { cs } from '../utils' 4 | 5 | export const SearchIcon = (props) => { 6 | const { className, ...rest } = props 7 | return ( 8 | 9 | 10 | 11 | ) 12 | } 13 | -------------------------------------------------------------------------------- /svelte.config.js: -------------------------------------------------------------------------------- 1 | import adapter from '@sveltejs/adapter-static'; 2 | import { vitePreprocess } from '@sveltejs/kit/vite'; 3 | const dev = process.argv.includes('dev'); 4 | /** @type {import('@sveltejs/kit').Config} */ 5 | const config = { 6 | compilerOptions: { 7 | css: "external", 8 | }, 9 | // Consult https://kit.svelte.dev/docs/integrations#preprocessors 10 | // for more information about preprocessors 11 | preprocess: vitePreprocess(), 12 | 13 | kit: { 14 | // adapter-auto only supports some environments, see https://kit.svelte.dev/docs/adapter-auto for a list. 15 | // If your environment is not supported or you settled on a specific environment, switch out the adapter. 16 | // See https://kit.svelte.dev/docs/adapters for more information about adapters. 17 | adapter: adapter({ 18 | fallback: '404.html', 19 | }), 20 | paths: { 21 | base: dev ? '' : '/svelte-notion', 22 | relative: false, 23 | } 24 | } 25 | }; 26 | 27 | export default config; 28 | -------------------------------------------------------------------------------- /src/routes/components/BlockItem/File.svelte: -------------------------------------------------------------------------------- 1 | 11 | 12 | 29 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Github Pages 2 | 3 | # Controls when the workflow will run 4 | on: 5 | # Allows you to run this workflow manually from the Actions tab 6 | workflow_dispatch: 7 | 8 | 9 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel 10 | jobs: 11 | # This workflow contains a single job called "build" 12 | build: 13 | # The type of runner that the job will run on 14 | runs-on: ubuntu-latest 15 | 16 | # Steps represent a sequence of tasks that will be executed as part of the job 17 | steps: 18 | - name: Checkout 19 | uses: actions/checkout@v3 20 | - name: Install Dependencies 21 | run: yarn 22 | - name: Build 23 | run: yarn build 24 | - name: Deploy 25 | uses: JamesIves/github-pages-deploy-action@v4 26 | with: 27 | token: ${{ secrets.ACCESS_TOKEN }} 28 | repository-name: tiodot/svelte-notion 29 | branch: doc 30 | folder: build 31 | -------------------------------------------------------------------------------- /src/lib/icons/type-checkbox.svg: -------------------------------------------------------------------------------- 1 | 2 | 8 | 11 | -------------------------------------------------------------------------------- /src/routes/components/TextLink.svelte: -------------------------------------------------------------------------------- 1 | 30 | 31 | {text} 32 | 33 | -------------------------------------------------------------------------------- /src/routes/components/Collection/CollectionPropertyTime.svelte: -------------------------------------------------------------------------------- 1 | 29 | 30 | {value} -------------------------------------------------------------------------------- /src/lib/icons/type-multi-select.svg: -------------------------------------------------------------------------------- 1 | 2 | 8 | 11 | -------------------------------------------------------------------------------- /src/lib/icons/type-text.svg: -------------------------------------------------------------------------------- 1 | 2 | 8 | 11 | -------------------------------------------------------------------------------- /src/routes/components/TextExternalLink.svelte: -------------------------------------------------------------------------------- 1 | 19 | 20 | {#if value} 21 | {#if linkType === 'u'} 22 | 23 | {userInfo?.given_name} 24 | {/if} 25 | {#if linkedBlock} 26 | 27 | 28 | 29 | {/if} 30 | {/if} -------------------------------------------------------------------------------- /src/lib/icons/collection-view-table.svg: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | -------------------------------------------------------------------------------- /src/lib/icons/type-phone-number.svg: -------------------------------------------------------------------------------- 1 | 2 | 8 | 11 | -------------------------------------------------------------------------------- /src/lib/icons/type-formula.svg: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | -------------------------------------------------------------------------------- /src/routes/components/PageTitle.svelte: -------------------------------------------------------------------------------- 1 | 16 | 17 | {#if block} 18 | {#if isCollectionBlock && title} 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | {/if} 27 | {#if !isCollectionBlock && title} 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | {/if} 36 | {/if} 37 | -------------------------------------------------------------------------------- /src/lib/icons/type-file.svg: -------------------------------------------------------------------------------- 1 | 2 | 8 | 11 | -------------------------------------------------------------------------------- /src/routes/components/Collection/CollectionViewTabs.svelte: -------------------------------------------------------------------------------- 1 | 10 | 11 |
12 | {#each viewIds as viewId (viewId)} 13 | {@const collectionView = $recordMapStore.collection_view[viewId]?.value} 14 | 27 | {/each} 28 |
29 | -------------------------------------------------------------------------------- /src/lib/icons/type-email.svelte: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/routes/+page.svelte: -------------------------------------------------------------------------------- 1 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | {title} 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /src/lib/map-image-url.ts: -------------------------------------------------------------------------------- 1 | import type { Block } from 'notion-types'; 2 | 3 | import { defaultPageCover, defaultPageIcon } from './config'; 4 | 5 | const defaultMapImageUrl = (url: string, block: Block) => { 6 | if (!url) { 7 | return null; 8 | } 9 | if (url.startsWith('data:')) { 10 | return url; 11 | } 12 | if (url.startsWith('https://images.unsplash.com')) { 13 | return url; 14 | } 15 | 16 | if (url.startsWith('/images')) { 17 | url = `https://www.notion.so${url}`; 18 | } 19 | url = `https://www.notion.so${ 20 | url.startsWith('/image') ? url : `/image/${encodeURIComponent(url)}` 21 | }`; 22 | 23 | const notionImageUrlV2 = new URL(url); 24 | let table = block.parent_table === 'space' ? 'block' : block.parent_table; 25 | if (table === 'collection' || table === 'team') { 26 | table = 'block'; 27 | } 28 | notionImageUrlV2.searchParams.set('table', table); 29 | notionImageUrlV2.searchParams.set('id', block.id); 30 | notionImageUrlV2.searchParams.set('cache', 'v2'); 31 | url = notionImageUrlV2.toString(); 32 | return url; 33 | }; 34 | 35 | export const mapImageUrl = (url: string, block: Block) => { 36 | if (url === defaultPageCover || url === defaultPageIcon) { 37 | return url; 38 | } 39 | 40 | return defaultMapImageUrl(url, block); 41 | }; 42 | -------------------------------------------------------------------------------- /src/routes/[pageId]/+page.svelte: -------------------------------------------------------------------------------- 1 | 2 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | {title} 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /src/routes/components/TextPlain.svelte: -------------------------------------------------------------------------------- 1 | 12 | 13 | {#if type} 14 | {#if type === 'h'} 15 | {text} 16 | {:else if type === 'c'} 17 | {text} 18 | {:else if type === 'b'} 19 | {text} 20 | {:else if type === 'i'} 21 | {text} 22 | {:else if type === 's'} 23 | {text} 24 | {:else if type === '_'} 25 | {text} 26 | {:else if type === 'm'} 27 | {text} 28 | {:else if type === 'd'} 29 | {#if value?.type === 'date'} 30 | {formatDate(value?.start_date)} 31 | {:else if value?.type === 'daterange'} 32 | {`${formatDate(value.start_date)} → ${formatDate(value.end_date)}`} 33 | {:else } 34 | {text} 35 | {/if} 36 | {:else } 37 | {text} 38 | {/if} 39 | 40 | {:else } 41 | {#if text === ','} 42 | 43 | {:else} 44 | {@html text} 45 | {/if} 46 | {/if} 47 | 48 | -------------------------------------------------------------------------------- /src/lib/icons/type-number.svg: -------------------------------------------------------------------------------- 1 | 2 | 8 | 11 | -------------------------------------------------------------------------------- /src/lib/icons/loading-icon.svelte: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 31 | 37 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /src/routes/store.ts: -------------------------------------------------------------------------------- 1 | import type { ExtendedRecordMap } from 'notion-types'; 2 | import { writable, get } from 'svelte/store'; 3 | import { parsePageId, uuidToId, getCanonicalPageId } from 'notion-utils'; 4 | import type { Site } from '$lib/types'; 5 | import { site } from '$lib/config'; 6 | import { inversePageUrlOverrides } from '$lib/config'; 7 | import { base } from '$app/paths'; 8 | // export const site = writable(); 9 | 10 | function createRecordMap() { 11 | const { subscribe, set, update } = writable(); 12 | 13 | return { 14 | subscribe, 15 | update, 16 | init: (obj: ExtendedRecordMap) => set(obj) 17 | // 生成跳转的链接 18 | }; 19 | } 20 | 21 | export const recordMapStore = createRecordMap(); 22 | 23 | const uuid = true; 24 | 25 | export const actions = { 26 | mapPageUrl: (pageId = '') => { 27 | const pageUuid = parsePageId(pageId, { uuid: true }); 28 | 29 | if (uuidToId(pageUuid) === site.rootNotionPageId) { 30 | return '/'; 31 | } else { 32 | const cleanPageId = parsePageId(pageId, { uuid: false }); 33 | if (!cleanPageId) { 34 | return '/'; 35 | } 36 | const override = inversePageUrlOverrides[cleanPageId]; 37 | if (override) { 38 | return `/${override}`; 39 | } else { 40 | return `${base ? base : ''}/${getCanonicalPageId(pageUuid, get(recordMapStore), { uuid })}`; 41 | } 42 | } 43 | } 44 | }; 45 | -------------------------------------------------------------------------------- /src/lib/icons/type-url.svg: -------------------------------------------------------------------------------- 1 | 2 | 8 | 11 | -------------------------------------------------------------------------------- /src/lib/get-config-value.ts: -------------------------------------------------------------------------------- 1 | import rawSiteConfig from '../site.config'; 2 | import type { SiteConfig } from './site-config'; 3 | 4 | if (!rawSiteConfig) { 5 | throw new Error(`Config error: invalid site.config.ts`); 6 | } 7 | 8 | // allow environment variables to override site.config.ts 9 | let siteConfigOverrides: SiteConfig; 10 | 11 | try { 12 | if (process.env.NEXT_PUBLIC_SITE_CONFIG) { 13 | siteConfigOverrides = JSON.parse(process.env.NEXT_PUBLIC_SITE_CONFIG); 14 | } 15 | } catch (err) { 16 | console.error('Invalid config "NEXT_PUBLIC_SITE_CONFIG" failed to parse'); 17 | throw err; 18 | } 19 | 20 | const siteConfig: SiteConfig = { 21 | ...rawSiteConfig, 22 | ...(siteConfigOverrides ?? {}) 23 | }; 24 | 25 | export function getSiteConfig(key: string, defaultValue?: T): T { 26 | const value = siteConfig[key as keyof SiteConfig]; 27 | 28 | if (value !== undefined) { 29 | return value as T; 30 | } 31 | 32 | if (defaultValue !== undefined) { 33 | return defaultValue; 34 | } 35 | 36 | throw new Error(`Config error: missing required site config value "${key}"`); 37 | } 38 | 39 | export function getEnv(key: string, defaultValue?: string, env = process.env): string { 40 | const value = env[key]; 41 | 42 | if (value !== undefined) { 43 | return value; 44 | } 45 | 46 | if (defaultValue !== undefined) { 47 | return defaultValue; 48 | } 49 | 50 | throw new Error(`Config error: missing required env variable "${key}"`); 51 | } 52 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Svelte Notion Kit 2 | 3 | This repo is using svelte to generate static blog site. Inspired by [react-notion-x](https://github.com/NotionX/react-notion-x) 4 | 5 | It uses Notion as a CMS, can be deployed to GitHub pages or Vercel; 6 | 7 | ## Features 8 | - Setup only takes a few minutes(single config file) 9 | - Build using Svelte without CSR that means only html files and css files 10 | - Excellent page speeds, no hydrate, no Javascript. 11 | - Responsive for different devices 12 | 13 | ## Demos 14 | https://xchb.work/svelte-notion/ 15 | 16 | ![](2023-04-13-20-09-36.png) 17 | ## Setup 18 | All config is defined in [site.config.ts](https://github.com/tiodot/svelte-notion/blob/main/src/site.config.ts). 19 | 20 | 1. Fork / clone this repo 21 | 2. Change a few values in [site.config.ts](https://github.com/tiodot/svelte-notion/blob/main/src/site.config.ts) 22 | 3. npm install 23 | 4. npm run dev to test locally 24 | 5. npm run deploy to deploy to vercel 💪 25 | 26 | I tried to make configuration as easy as possible — All you really need to do to get started is edit **rootNotionPageId**. 27 | 28 | We recommend duplicating the [default page](https://tiodot.notion.site/5e19b09eec9e43c5b4c23031d41fea81) as a starting point, but you can use any public notion page you want. 29 | 30 | Make sure your root Notion page is public and then copy the link to your clipboard. Extract the last part of the URL that looks like 7875426197cf461698809def95960ebf, which is your page's Notion ID. 31 | -------------------------------------------------------------------------------- /src/lib/icons/type-timestamp.svg: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | -------------------------------------------------------------------------------- /src/site.config.ts: -------------------------------------------------------------------------------- 1 | import { siteConfig } from './lib/site-config'; 2 | 3 | export default siteConfig({ 4 | rootNotionPageId: '5e19b09eec9e43c5b4c23031d41fea81', 5 | 6 | // if you want to restrict pages to a single notion workspace (optional) 7 | // (this should be a Notion ID; see the docs for how to extract this) 8 | rootNotionSpaceId: undefined, 9 | 10 | // basic site info (required) 11 | name: 'Svelte Notion Blog', 12 | domain: 'svelte-noiton.test.com', 13 | author: 'svelte-noiton', 14 | 15 | // open graph metadata (optional) 16 | description: 'svelte notion render', 17 | 18 | // default notion icon and cover images for site-wide consistency (optional) 19 | // page-specific values will override these site-wide defaults 20 | defaultPageIcon: null, 21 | defaultPageCover: null, 22 | defaultPageCoverPosition: 0.5, 23 | 24 | isSearchEnabled: undefined, 25 | // pageUrlOverrides: { 26 | // '/foo': '067dd719a912471ea9a3ac10710e7fdf', 27 | // '/bar': '0be6efce9daf42688f65c76b89f8eb27' 28 | // } 29 | pageUrlOverrides: undefined, 30 | 31 | // whether to use the default notion navigation style or a custom one with links to 32 | // important pages 33 | navigationStyle: 'custom', 34 | // navigationStyle: 'custom', 35 | // navigationLinks: [ 36 | // { 37 | // title: 'About', 38 | // pageId: 'f1199d37579b41cbabfc0b5174f4256a' 39 | // }, 40 | // { 41 | // title: 'Contact', 42 | // pageId: '6a29ebcb935a4f0689fe661ab5f3b8d1' 43 | // } 44 | // ] 45 | 46 | }); 47 | -------------------------------------------------------------------------------- /src/lib/icons/property-icon.svelte: -------------------------------------------------------------------------------- 1 | 46 | 47 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "svelte-notion", 3 | "version": "0.0.1", 4 | "scripts": { 5 | "dev": "vite dev", 6 | "build": "vite build", 7 | "preview": "vite preview", 8 | "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", 9 | "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", 10 | "lint": "prettier --plugin-search-dir . --check . && eslint .", 11 | "format": "prettier --plugin-search-dir . --write .", 12 | "sitemap": "HOST=https://example.com node scripts/sitemap.js" 13 | }, 14 | "devDependencies": { 15 | "@fontsource/fira-mono": "^4.5.10", 16 | "@neoconfetti/svelte": "^1.0.0", 17 | "@sveltejs/adapter-auto": "^2.0.0", 18 | "@sveltejs/adapter-static": "^2.0.1", 19 | "@sveltejs/kit": "^1.5.0", 20 | "@types/cookie": "^0.5.1", 21 | "@typescript-eslint/eslint-plugin": "^5.45.0", 22 | "@typescript-eslint/parser": "^5.45.0", 23 | "classnames": "^2.3.2", 24 | "date-fns": "^2.29.3", 25 | "eslint": "^8.28.0", 26 | "eslint-config-prettier": "^8.5.0", 27 | "eslint-plugin-svelte3": "^4.0.0", 28 | "format-number": "^3.0.0", 29 | "html-minifier": "^4.0.0", 30 | "notion-client": "^6.15.8", 31 | "notion-types": "^6.15.6", 32 | "notion-utils": "^6.15.8", 33 | "p-memoize": "^7.1.1", 34 | "prettier": "^2.8.0", 35 | "prettier-plugin-svelte": "^2.8.1", 36 | "prismjs": "^1.29.0", 37 | "querystring": "^0.2.1", 38 | "svelte": "^3.54.0", 39 | "svelte-check": "^3.0.1", 40 | "svelte-katex": "^0.1.2", 41 | "svelte-pdf": "^1.0.17", 42 | "tslib": "^2.4.1", 43 | "typescript": "^4.9.3", 44 | "vite": "^4.0.0" 45 | }, 46 | "type": "module" 47 | } 48 | -------------------------------------------------------------------------------- /src/routes/components/Text.svelte: -------------------------------------------------------------------------------- 1 | 39 | 40 | {#each value as [text, decorations], index} 41 | {#if !decorations} 42 | 43 | {:else} 44 | {#each decorations as decorator} 45 | 56 | {/each} 57 | {/if} 58 | {/each} 59 | -------------------------------------------------------------------------------- /src/lib/icons/type-person.svg: -------------------------------------------------------------------------------- 1 | 2 | 8 | 11 | -------------------------------------------------------------------------------- /src/routes/components/Collection/CollectionViewGallery.svelte: -------------------------------------------------------------------------------- 1 | 27 | 28 | -------------------------------------------------------------------------------- /src/lib/icons/collection-view-calendar.svelte: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/lib/images/github.svg: -------------------------------------------------------------------------------- 1 | 2 | 9 | 16 | -------------------------------------------------------------------------------- /src/lib/types.ts: -------------------------------------------------------------------------------- 1 | import type { ExtendedRecordMap, PageMap } from 'notion-types'; 2 | import type { ParsedUrlQuery } from 'querystring'; 3 | 4 | export * from 'notion-types'; 5 | 6 | export type NavigationStyle = 'default' | 'custom'; 7 | 8 | export interface PageError { 9 | message?: string; 10 | statusCode: number; 11 | } 12 | 13 | export interface PageProps { 14 | site?: Site; 15 | recordMap?: ExtendedRecordMap; 16 | pageId?: string; 17 | error?: PageError; 18 | } 19 | 20 | export interface Params extends ParsedUrlQuery { 21 | pageId: string; 22 | } 23 | 24 | export interface Site { 25 | name: string; 26 | domain: string; 27 | 28 | rootNotionPageId: string; 29 | rootNotionSpaceId: string; 30 | 31 | // settings 32 | html?: string; 33 | fontFamily?: string; 34 | darkMode?: boolean; 35 | previewImages?: boolean; 36 | 37 | // opengraph metadata 38 | description?: string; 39 | image?: string; 40 | } 41 | 42 | export interface SiteMap { 43 | site: Site; 44 | pageMap: PageMap; 45 | canonicalPageMap: CanonicalPageMap; 46 | } 47 | 48 | export interface CanonicalPageMap { 49 | [canonicalPageId: string]: string; 50 | } 51 | 52 | export interface PageUrlOverridesMap { 53 | // maps from a URL path to the notion page id the page should be resolved to 54 | // (this overrides the built-in URL path generation for these pages) 55 | [pagePath: string]: string; 56 | } 57 | 58 | export interface PageUrlOverridesInverseMap { 59 | // maps from a notion page id to the URL path the page should be resolved to 60 | // (this overrides the built-in URL path generation for these pages) 61 | [pageId: string]: string; 62 | } 63 | 64 | export interface NotionPageInfo { 65 | pageId: string; 66 | title: string; 67 | image: string; 68 | imageObjectPosition: string; 69 | author: string; 70 | authorImage: string; 71 | detail: string; 72 | } 73 | -------------------------------------------------------------------------------- /src/lib/images/svelte-logo.svg: -------------------------------------------------------------------------------- 1 | svelte-logo -------------------------------------------------------------------------------- /src/routes/components/PageIcon.svelte: -------------------------------------------------------------------------------- 1 | 44 | {#if isBlockIcon && (iconUrl || icon)} 45 |
47 | {#if iconUrl} 48 | {title || 'page icon'} 49 | {:else} 50 | 55 | {icon} 56 | 57 | {/if} 58 |
59 | 60 | {/if} 61 | 62 | 63 | -------------------------------------------------------------------------------- /src/routes/components/Collection/CollectionPropertyNumber.svelte: -------------------------------------------------------------------------------- 1 | 58 | 59 | {#if output} 60 | 61 | {:else } 62 | 63 | {/if} -------------------------------------------------------------------------------- /src/routes/components/LiteYoutubeEmbed.svelte: -------------------------------------------------------------------------------- 1 | 26 | 27 | 28 | 29 |
{ 31 | iframeInitialized = true; 32 | }} 33 | class="notion-yt-lite {iframeInitialized ? 'notion-yt-initialized' : ''}" 34 | style={$$props.style} 35 | > 36 | 37 | 38 |
39 | 40 | {#if iframeInitialized} 41 |