diff --git a/.env.example b/.env.example
new file mode 100644
index 0000000..18354f2
--- /dev/null
+++ b/.env.example
@@ -0,0 +1,7 @@
+# Connection to Notion API
+# see: https://developers.notion.com/docs/create-a-notion-integration
+NOTION_API_KEY=""
+
+# Edge Config is a key-value data store associated with your Vercel account
+# see: https://vercel.com/docs/concepts/edge-network/edge-config/get-started
+EDGE_CONFIG="https://edge-config.vercel.com/your_edge_config_id_here?token=your_edge_config_read_access_token_here"
diff --git a/package.json b/package.json
index c3457d5..acc1a95 100644
--- a/package.json
+++ b/package.json
@@ -10,12 +10,14 @@
},
"dependencies": {
"@next/font": "13.1.6",
+ "@notionhq/client": "^2.2.3",
"@radix-ui/react-collapsible": "^1.0.1",
"@radix-ui/react-dropdown-menu": "^2.0.2",
"@radix-ui/react-scroll-area": "^1.0.2",
"@types/node": "18.13.0",
"@types/react": "18.0.27",
"@types/react-dom": "18.0.10",
+ "@vercel/edge-config": "^0.1.1",
"eslint": "8.33.0",
"eslint-config-next": "13.1.6",
"lucide-react": "^0.112.0",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index ad7526c..d8edf28 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -2,12 +2,14 @@ lockfileVersion: 5.4
specifiers:
'@next/font': 13.1.6
+ '@notionhq/client': ^2.2.3
'@radix-ui/react-collapsible': ^1.0.1
'@radix-ui/react-dropdown-menu': ^2.0.2
'@radix-ui/react-scroll-area': ^1.0.2
'@types/node': 18.13.0
'@types/react': 18.0.27
'@types/react-dom': 18.0.10
+ '@vercel/edge-config': ^0.1.1
autoprefixer: ^10.4.13
eslint: 8.33.0
eslint-config-next: 13.1.6
@@ -25,12 +27,14 @@ specifiers:
dependencies:
'@next/font': 13.1.6
+ '@notionhq/client': 2.2.3
'@radix-ui/react-collapsible': 1.0.1_biqbaboplfbrettd7655fr4n2y
'@radix-ui/react-dropdown-menu': 2.0.2_5ndqzdd6t4rivxsukjv3i3ak2q
'@radix-ui/react-scroll-area': 1.0.2_biqbaboplfbrettd7655fr4n2y
'@types/node': 18.13.0
'@types/react': 18.0.27
'@types/react-dom': 18.0.10
+ '@vercel/edge-config': 0.1.1
eslint: 8.33.0
eslint-config-next: 13.1.6_4vsywjlpuriuw3tl5oq6zy5a64
lucide-react: 0.112.0_react@18.2.0
@@ -267,6 +271,16 @@ packages:
'@nodelib/fs.scandir': 2.1.5
fastq: 1.15.0
+ /@notionhq/client/2.2.3:
+ resolution: {integrity: sha512-ZqUzY0iRg/LIrwS+wzz/6osSB2nxIpmqdAtdUwzpcimc9Jlu1j85FeYdaU26Shr193CFrl2TLFeKqpk/APRQ4g==}
+ engines: {node: '>=12'}
+ dependencies:
+ '@types/node-fetch': 2.6.2
+ node-fetch: 2.6.9
+ transitivePeerDependencies:
+ - encoding
+ dev: false
+
/@pkgr/utils/2.3.1:
resolution: {integrity: sha512-wfzX8kc1PMyUILA+1Z/EqoE4UCXGy0iRGMhPwdfae1+f0OXlLqCk+By+aMzgJBzR9AzS4CDizioG6Ss1gvAFJw==}
engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0}
@@ -652,6 +666,13 @@ packages:
resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==}
dev: false
+ /@types/node-fetch/2.6.2:
+ resolution: {integrity: sha512-DHqhlq5jeESLy19TYhLakJ07kNumXWjcDdxXsLUMJZ6ue8VZJj4kLPQVE/2mdHh3xZziNF1xppu5lwmS53HR+A==}
+ dependencies:
+ '@types/node': 18.13.0
+ form-data: 3.0.1
+ dev: false
+
/@types/node/18.13.0:
resolution: {integrity: sha512-gC3TazRzGoOnoKAhUx+Q0t8S9Tzs74z7m0ipwGpSqQrleP14hKxP4/JUeEQcD3W1/aIpnWl8pHowI7WokuZpXg==}
dev: false
@@ -740,6 +761,11 @@ packages:
eslint-visitor-keys: 3.3.0
dev: false
+ /@vercel/edge-config/0.1.1:
+ resolution: {integrity: sha512-HDl+12tzW2RHIu8Y804kXg2VpZ02KVb7Nqsa+e1AFoACXXMb+7TfjI1wR19tXoDIu5enQz1dbat40YMEgaH13Q==}
+ engines: {node: '>=14.6'}
+ dev: false
+
/acorn-jsx/5.3.2_acorn@8.8.2:
resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==}
peerDependencies:
@@ -880,6 +906,10 @@ packages:
resolution: {integrity: sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag==}
dev: false
+ /asynckit/0.4.0:
+ resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
+ dev: false
+
/autoprefixer/10.4.13_postcss@8.4.21:
resolution: {integrity: sha512-49vKpMqcZYsJjwotvt4+h/BCjJVnhGwcLpDt5xkcaOG3eLrG/HUYLagrihYsQ+qrIBgIzX1Rw7a6L8I/ZA1Atg==}
engines: {node: ^10 || ^12 || >=14}
@@ -999,6 +1029,13 @@ packages:
/color-name/1.1.4:
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
+ /combined-stream/1.0.8:
+ resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
+ engines: {node: '>= 0.8'}
+ dependencies:
+ delayed-stream: 1.0.0
+ dev: false
+
/concat-map/0.0.1:
resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
dev: false
@@ -1090,6 +1127,11 @@ packages:
/defined/1.0.1:
resolution: {integrity: sha512-hsBd2qSVCRE+5PmNdHt1uzyrFu5d3RwmFDKzyNZMFq/EwDNJF7Ee5+D5oEKF0hU6LhtoUF1macFvOe4AskQC1Q==}
+ /delayed-stream/1.0.0:
+ resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
+ engines: {node: '>=0.4.0'}
+ dev: false
+
/detect-node-es/1.1.0:
resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==}
dev: false
@@ -1584,6 +1626,15 @@ packages:
is-callable: 1.2.7
dev: false
+ /form-data/3.0.1:
+ resolution: {integrity: sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==}
+ engines: {node: '>= 6'}
+ dependencies:
+ asynckit: 0.4.0
+ combined-stream: 1.0.8
+ mime-types: 2.1.35
+ dev: false
+
/fraction.js/4.2.0:
resolution: {integrity: sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==}
dev: true
@@ -2093,6 +2144,18 @@ packages:
braces: 3.0.2
picomatch: 2.3.1
+ /mime-db/1.52.0:
+ resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==}
+ engines: {node: '>= 0.6'}
+ dev: false
+
+ /mime-types/2.1.35:
+ resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==}
+ engines: {node: '>= 0.6'}
+ dependencies:
+ mime-db: 1.52.0
+ dev: false
+
/minimatch/3.1.2:
resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
dependencies:
@@ -2163,6 +2226,18 @@ packages:
- babel-plugin-macros
dev: false
+ /node-fetch/2.6.9:
+ resolution: {integrity: sha512-DJm/CJkZkRjKKj4Zi4BsKVZh3ValV5IR5s7LVZnW+6YMh0W1BfNA8XSs6DLMGYlId5F3KnA70uu2qepcR08Qqg==}
+ engines: {node: 4.x || >=6.0.0}
+ peerDependencies:
+ encoding: ^0.1.0
+ peerDependenciesMeta:
+ encoding:
+ optional: true
+ dependencies:
+ whatwg-url: 5.0.0
+ dev: false
+
/node-releases/2.0.10:
resolution: {integrity: sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==}
dev: true
@@ -2788,6 +2863,10 @@ packages:
dependencies:
is-number: 7.0.0
+ /tr46/0.0.3:
+ resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==}
+ dev: false
+
/tsconfig-paths/3.14.1:
resolution: {integrity: sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==}
dependencies:
@@ -2922,6 +3001,17 @@ packages:
resolution: {integrity: sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg==}
dev: false
+ /webidl-conversions/3.0.1:
+ resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==}
+ dev: false
+
+ /whatwg-url/5.0.0:
+ resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==}
+ dependencies:
+ tr46: 0.0.3
+ webidl-conversions: 3.0.1
+ dev: false
+
/which-boxed-primitive/1.0.2:
resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==}
dependencies:
diff --git a/src/app/others/dev-setup/page.tsx b/src/app/others/dev-setup/page.tsx
index fd52753..814d549 100644
--- a/src/app/others/dev-setup/page.tsx
+++ b/src/app/others/dev-setup/page.tsx
@@ -1,25 +1,21 @@
import { CodePreview } from '@/components/CodePreview'
+import { getCodeBlockFromNotion } from '@/lib/notion-client'
+import { getNotionPagesId } from '@/lib/vercel-edge-config'
import shiki from 'shiki'
export const metadata = {
title: 'Dev Setup',
}
-const markdown = `
-# Dev Setup
-
-- MacBook M1 Max (64gb Memory)
-- LG 25" UltraWide Display
-
-That's it, nothing more.
-`.trim()
-
export default async function DevSetup() {
+ const { setup_dev } = await getNotionPagesId()
+ const { content } = await getCodeBlockFromNotion(setup_dev)
+
const highlighter = await shiki.getHighlighter({
theme: 'rose-pine-moon',
})
- const code = highlighter.codeToHtml(markdown, { lang: 'md' })
+ const code = highlighter.codeToHtml(content, { lang: 'md' })
return
-}
\ No newline at end of file
+}
diff --git a/src/app/others/gaming-setup/page.tsx b/src/app/others/gaming-setup/page.tsx
index 09c0ab3..d200940 100644
--- a/src/app/others/gaming-setup/page.tsx
+++ b/src/app/others/gaming-setup/page.tsx
@@ -1,36 +1,21 @@
import { CodePreview } from '@/components/CodePreview'
+import { getCodeBlockFromNotion } from '@/lib/notion-client'
+import { getNotionPagesId } from '@/lib/vercel-edge-config'
import shiki from 'shiki'
export const metadata = {
title: 'Gaming Setup',
}
-const markdown = `
-# Gaming Setup
-
-- Intel Core i5-9600KF 3.7Ghz
-- 2x HyperX Fury 16GB 3000Mhz
-- Gigabyte Z390M Gaming
-- Cooler Master ATX 500W 80 Plus
-- 2x Corsair SSD MP510 480GB NVMe
-- Gigabyte NVIDIA GeForce RTX 2060 6G
-
-
-## Peripherals
-
-- Logitech G PRO Wireless Mouse
-- Keychron K2 Keyboard (Brown Switch)
-- Samsung 23.5" Curved 144hz 1ms Display
-
-That's it, nothing more.
-`.trim()
-
export default async function GamingSetup() {
+ const { setup_gaming } = await getNotionPagesId()
+ const { content } = await getCodeBlockFromNotion(setup_gaming)
+
const highlighter = await shiki.getHighlighter({
theme: 'rose-pine-moon',
})
- const code = highlighter.codeToHtml(markdown, { lang: 'md' })
+ const code = highlighter.codeToHtml(content, { lang: 'md' })
return
}
diff --git a/src/app/terminal/fish/page.tsx b/src/app/terminal/fish/page.tsx
index 38840da..04c4263 100644
--- a/src/app/terminal/fish/page.tsx
+++ b/src/app/terminal/fish/page.tsx
@@ -1,27 +1,21 @@
import { CodePreview } from '@/components/CodePreview'
+import { getCodeBlockFromNotion } from '@/lib/notion-client'
+import { getNotionPagesId } from '@/lib/vercel-edge-config'
import shiki from 'shiki'
export const metadata = {
title: 'Fish',
}
-const fishConfig = `if status is-interactive
-# Commands to run in interactive sessions can go here
-end
-
-set SPACEFISH_PROMPT_ADD_NEWLINE false
-
-starship init fish | source
-
-# Aliases
-# alias cat="bat --theme=\$(defaults read -globalDomain AppleInterfaceStyle &> /dev/null && echo default || echo GitHub)"`
-
export default async function FishConfig() {
+ const { terminal_fish } = await getNotionPagesId()
+ const { content } = await getCodeBlockFromNotion(terminal_fish)
+
const highlighter = await shiki.getHighlighter({
theme: 'rose-pine-moon',
})
- const code = highlighter.codeToHtml(fishConfig, { lang: 'fish' })
+ const code = highlighter.codeToHtml(content, { lang: 'fish' })
- return
+ return
}
diff --git a/src/app/terminal/general/page.tsx b/src/app/terminal/general/page.tsx
index 289e34f..4415735 100644
--- a/src/app/terminal/general/page.tsx
+++ b/src/app/terminal/general/page.tsx
@@ -1,37 +1,21 @@
import { CodePreview } from '@/components/CodePreview'
+import { getCodeBlockFromNotion } from '@/lib/notion-client'
+import { getNotionPagesId } from '@/lib/vercel-edge-config'
import shiki from 'shiki'
export const metadata = {
title: 'Terminal',
}
-const markdown = `
-# General
-
-Currently I'm using the combo Fish + Starship in my terminal.
-
-Fish Shell: https://fishshell.com/
-Starship: https://starship.rs/
-
----
-
-I'm also using Warp as my terminal emulator.
-
-Warp: https://www.warp.dev/
-
----
-
-For the theme, I chose Rosé Pine Moon variant:
-
-Theme: https://github.com/austintraver/warp-theme/blob/main/base16_rose_pine_moon.yaml
-`.trim()
-
export default async function General() {
+ const { terminal_general } = await getNotionPagesId()
+ const { content } = await getCodeBlockFromNotion(terminal_general)
+
const highlighter = await shiki.getHighlighter({
theme: 'rose-pine-moon',
})
- const code = highlighter.codeToHtml(markdown, { lang: 'md' })
+ const code = highlighter.codeToHtml(content, { lang: 'md' })
return
}
diff --git a/src/lib/notion-client.ts b/src/lib/notion-client.ts
new file mode 100644
index 0000000..b90129a
--- /dev/null
+++ b/src/lib/notion-client.ts
@@ -0,0 +1,20 @@
+import { Client, isFullBlock } from '@notionhq/client'
+import { CodeBlockObjectResponse } from '@notionhq/client/build/src/api-endpoints'
+
+export const notionClient = new Client({ auth: process.env.NOTION_API_KEY })
+
+export async function getCodeBlockFromNotion(pageId: string) {
+ const { results } = await notionClient.blocks.children.list({ block_id: pageId })
+
+ const codeBlock = results.find(
+ block => isFullBlock(block) && block.type === 'code'
+ ) as CodeBlockObjectResponse | undefined
+
+ if (!codeBlock) {
+ throw new Error(`Failed to fetch Notion content of ID: ${pageId}`)
+ }
+
+ const { plain_text } = codeBlock.code.rich_text[0]
+
+ return { content: plain_text }
+}
diff --git a/src/lib/vercel-edge-config.ts b/src/lib/vercel-edge-config.ts
new file mode 100644
index 0000000..dd866a1
--- /dev/null
+++ b/src/lib/vercel-edge-config.ts
@@ -0,0 +1,15 @@
+import { get } from '@vercel/edge-config'
+import { z } from 'zod'
+
+const notionPagesIdStore = z.object({
+ terminal_general: z.string(),
+ terminal_fish: z.string(),
+ setup_dev: z.string(),
+ setup_gaming: z.string(),
+})
+
+export async function getNotionPagesId() {
+ const pagesId = await get('notion')
+
+ return notionPagesIdStore.parse(pagesId)
+}