### GitHub Actions Workflow for Notion Site Generation and Deployment Source: https://github.com/merkulovdaniil/notion4ever/blob/main/README.md This workflow automates the process of downloading content from a Notion page, generating a static site using notion4ever, and deploying it to GitHub Pages. It includes steps for setting up the environment, checking out repositories, installing dependencies, running the generation script, and deploying the output. The workflow is triggered on a schedule and can also be run manually via workflow_dispatch. ```yaml on: workflow_dispatch: schedule: - cron: "0 */12 * * *" jobs: download_old-generate-push: runs-on: ubuntu-latest steps: - name: Submodule Update run: | wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb sudo apt install ./google-chrome-stable_current_amd64.deb sudo apt-get update - name: Set up Python uses: actions/setup-python@v2 with: python-version: 3.10.0 - name: Download notion4ever uses: actions/checkout@v2 with: repository: 'Merkulov Daniil/notion4ever' - name: Install packages run: pip install -r requirements.txt - name: Download current version of the site uses: actions/checkout@v2 with: repository: 'Merkulov Daniil/merkulovdaniil.github.io' ref: main path: _site - name: Run notion4ever run: python -m notion4ever env: SITE_URL: "https://merkulov.top" NOTION_TOKEN: ${{secrets.NOTION_TOKEN}} NOTION_PAGE_ID: ${{secrets.NOTION_PAGE_ID}} - name: Deploy to Pages uses: JamesIves/github-pages-deploy-action@3.7.1 with: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} BRANCH: main FOLDER: _site COMMIT_MESSAGE: 🤖 Deployed via notion4ever. ``` -------------------------------- ### Automated Deployment Workflow with GitHub Actions Source: https://context7.com/merkulovdaniil/notion4ever/llms.txt Automates the deployment of a Notion-generated static site to GitHub Pages. This workflow runs on a schedule or manually, checks out the Notion4ever repository, installs dependencies, downloads the existing site for incremental updates, runs Notion4ever to generate the site, and then deploys the output to GitHub Pages. ```yaml # .github/workflows/publish.yml name: Deploy from Notion to Pages on: schedule: - cron: "0 */12 * * *" # Run every 12 hours workflow_dispatch: # Manual trigger jobs: deploy: runs-on: ubuntu-latest steps: - name: Set up Python uses: actions/setup-python@v2 with: python-version: "3.10" - name: Download notion4ever uses: actions/checkout@v2 with: repository: 'MerkulovDaniil/notion4ever' - name: Install dependencies run: pip install -r requirements.txt - name: Download existing site (for incremental updates) uses: actions/checkout@v2 with: repository: '${{ github.repository_owner }}/my-site' ref: main path: _site - name: Run notion4ever run: python -m notion4ever env: SITE_URL: "https://mysite.github.io" NOTION_TOKEN: ${{ secrets.NOTION_TOKEN }} NOTION_PAGE_ID: ${{ secrets.NOTION_PAGE_ID }} INCLUDE_FOOTER: "true" INCLUDE_SEARCH: "true" - name: Deploy to GitHub Pages uses: JamesIves/github-pages-deploy-action@3.7.1 with: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} BRANCH: main FOLDER: _site COMMIT_MESSAGE: "🤖 Deployed via notion4ever" ``` -------------------------------- ### Parse Notion Page with notion2json.notion_page_parser Source: https://context7.com/merkulovdaniil/notion4ever/llms.txt Recursively downloads all Notion pages, databases, and nested content starting from a root page ID using the Notion API. Saves raw Notion API responses incrementally to a JSON file for caching and offline processing. ```python from notion_client import Client from notion4ever import notion2json # Initialize Notion client with API token notion = Client(auth="secret_your_notion_token") # Storage for raw Notion data raw_notion = {} filename = "./notion_content.json" # Parse the root page and all nested content # This recursively fetches all child pages, databases, and blocks notion2json.notion_page_parser( page_id="12e3d1659a444678b4e2b6a989a3c625", notion=notion, filename=filename, notion_json=raw_notion ) # Result: raw_notion dict with page IDs as keys # { # "12e3d165-9a44-4678-b4e2-b6a989a3c625": { # "object": "page", # "id": "12e3d165-9a44-4678-b4e2-b6a989a3c625", # "properties": {...}, # "blocks": [ # {"type": "paragraph", "paragraph": {"text": [...]}}, # {"type": "child_page", "id": "89ae66ca-..."}, # ... # ] # }, # "89ae66ca-44a5-4819-9797-5bf321572676": {...} # } ``` -------------------------------- ### Generate Full Static Site with Python Source: https://context7.com/merkulovdaniil/notion4ever/llms.txt Orchestrates the entire static site build process. It verifies templates, compiles SASS, generates HTML pages from Notion data, creates archive and 404 pages, and copies fonts. Requires structured Notion data and a configuration dictionary. ```python from pathlib import Path from notion4ever import site_generation structured_notion = { "root_page_id": "page123", "base_url": "https://example.com", "include_footer": True, "include_search": True, "search_index": [], "sorted_id_by_year": {}, "pages": { "page123": { "type": "page", "title": "Home", "url": "https://example.com", "md_content": "# Welcome\n\nHello world", "cover": None, "icon": None, "emoji": "🏠", "parent": None, "children": [], "family_line": [], "last_edited_time": "2024-01-25T22:35:00.000Z" } } } config = { "output_dir": Path("./_site"), "templates_dir": "./_templates", "sass_dir": "./_sass", "site_url": "https://example.com", "build_locally": False } # Generate the complete static site site_generation.generate_site(structured_notion, config) # Output structure: # _site/ # index.html # Home page # 404.html # Error page # Archive/index.html # Archive page # search_index.json # Search index (if enabled) # css/ # main.css # Compiled SASS # fonts/ # Copied fonts # Home.md # Markdown source ``` -------------------------------- ### Run Notion4ever CLI Source: https://context7.com/merkulovdaniil/notion4ever/llms.txt Execute the Notion4ever command-line interface to export Notion content. Requires a Notion API token and page ID. Supports various configuration options for output directory, templates, site URL, and build behavior. ```bash # Basic usage with required parameters python -m notion4ever -n "secret_your_notion_token" -p "12e3d1659a444678b4e2b6a989a3c625" # Full usage with all configuration options python -m notion4ever \ --notion_token "secret_your_notion_token" \ --notion_page_id "12e3d1659a444678b4e2b6a989a3c625" \ --output_dir "./_site" \ --templates_dir "./_templates" \ --sass_dir "./_sass" \ --site_url "https://example.com" \ --build_locally False \ --download_files True \ --remove_before False \ --include_footer True \ --include_search True \ --logging_level INFO # Build for local preview (uses file:// paths) python -m notion4ever -n $NOTION_TOKEN -p $NOTION_PAGE_ID -bl True # Using environment variables (recommended for CI/CD) export NOTION_TOKEN="secret_your_notion_token" export NOTION_PAGE_ID="12e3d1659a444678b4e2b6a989a3c625" export SITE_URL="https://example.com" python -m notion4ever ``` -------------------------------- ### HTML Templating and Content Rendering in Notion4ever Source: https://github.com/merkulovdaniil/notion4ever/blob/main/_templates/page.html This snippet demonstrates the use of HTML includes and block templating within the Notion4ever project. It defines the structure for page headers, content, and footers, dynamically rendering elements based on page properties and types. It utilizes Jinja-like syntax for conditional logic and includes. ```html {% include '\_head.html' %} {% include '\_header.html' %} {% block page_header %} {% block page_title %} {{ page.title }} {% endblock page_title %} {% block page_date %} {% if page.date_end %} Dates: {{ page['date'].strftime("%d %b, %Y") }} - {{ page['date_end'].strftime("%d %b, %Y") }} {% elif page.date %} Date: {{ page['date'].strftime("%d %b, %Y") }} {% else %} Last edited: {{ page['last_edited_time'].strftime("%d %b, %Y") }} {% endif %} {% endblock page_date %} {% if page.type == 'db_entry' %} {% if page.properties_md|length > 1 %} {% block page_properties %} {% include '\_properties_table.html' %} {% endblock page_properties %} {% endif %} {% endif %} {% endblock page_header %} {% block page_content %} {% if 'db_list' in page.keys() %} {% include '\_list.html' %} {% elif page.type == 'database' %} {% include '\_gallery.html' %} {% else %} {{ content }} {% endif %} {% endblock page_content %} {% block footer %} {% if site["include_footer"] %} {% include '\_footer.html' %} {% endif %} {% endblock footer %} ``` -------------------------------- ### Jinja2 Template for Page Output Source: https://context7.com/merkulovdaniil/notion4ever/llms.txt This Jinja2 template defines the structure for individual content pages. It includes the head, header, footer, breadcrumb navigation, page cover, title, markdown content, and a section for child pages displayed as a list or gallery. It relies on partial templates like _head.html, _header.html, _footer.html, _list.html, and _gallery.html. ```html {% include "_head.html" %} {% include "_header.html" %}
{% if page.cover %}
{{ page.title }}
{% endif %}

{% if page.emoji %}{{ page.emoji }}{% endif %} {{ page.title }}

{{ content | safe }} {% if page.children %} {% if page.db_list %} {% include "_list.html" %} {% else %} {% include "_gallery.html" %} {% endif %} {% endif %}
{% include "_footer.html" %} ``` -------------------------------- ### Generate Single HTML Page with Python Source: https://context7.com/merkulovdaniil/notion4ever/llms.txt Creates a single HTML page from structured Notion data, generating both a markdown source file and the rendered HTML using Jinja2 templates. It requires the page ID, structured Notion data, and a configuration dictionary. ```python from pathlib import Path from notion4ever import site_generation structured_notion = { "base_url": "https://example.com", "include_footer": True, "pages": { "page123": { "type": "page", "title": "About", "url": "https://example.com/About", "md_content": "# About Me\n\nI am a developer.", "cover": "https://example.com/About/cover.jpg", "icon": None, "emoji": "👤", "parent": "root123", "children": [], "family_line": ["root123"] } } } config = { "output_dir": Path("./_site"), "templates_dir": "./_templates", "site_url": "https://example.com", "build_locally": False } # Generate single page site_generation.generate_page("page123", structured_notion, config) # Creates: # _site/About/index.html (HTML page) # _site/About/About.md (Markdown source with YAML frontmatter) ``` -------------------------------- ### JavaScript: Implement Search Functionality with Fuse.js Source: https://github.com/merkulovdaniil/notion4ever/blob/main/_templates/page.html This JavaScript code implements a search feature for the website using the Fuse.js library. It handles loading the search index (either embedded or fetched), initializing Fuse.js with specified options, and displaying search results dynamically as the user types. It also includes functionality to toggle the search input and close search results. ```javascript document.addEventListener('DOMContentLoaded', function() { // Search functionality const searchIcon = document.querySelector('.search-icon'); const searchInput = document.getElementById('search-input'); const searchResults = document.getElementById('search-results'); // Toggle search input searchIcon.addEventListener('click', function() { const isHidden = searchInput.style.display === 'none'; searchInput.style.display = isHidden ? 'block' : 'none'; searchResults.style.display = 'none'; if (isHidden) { searchInput.focus(); // Load search index if not already loaded if (!window.searchData) { loadSearchIndex(); } } }); // Close search when clicking outside document.addEventListener('click', function(e) { if (!e.target.closest('.search-container')) { searchInput.style.display = 'none'; searchResults.style.display = 'none'; } }); async function loadSearchIndex() { {% if site.build_locally %} // For local builds, use embedded data window.searchData = {{ site.search_index|tojson }}; initializeSearch(); {% else %} // For server builds, fetch the index file try { const response = await fetch('{{ site.base_url }}/{{ site.search_index }}'); window.searchData = await response.json(); initializeSearch(); } catch (error) { console.error('Failed to load search index:', error); } {% endif %} } function initializeSearch() { const fuseOptions = { keys: ['title', 'content'], threshold: 0.2, minMatchCharLength: 2, ignoreLocation: true, useExtendedSearch: true, distance: 500 }; window.fuse = new Fuse(window.searchData, fuseOptions); // Add input event listener searchInput.addEventListener('input', function(e) { const query = e.target.value; if (query.length < 2) { searchResults.style.display = 'none'; return; } const results = window.fuse.search(query); if (results.length > 0) { searchResults.innerHTML = results.slice(0, 5).map(result => `
${result.item.title}
${result.item.content.substring(0, 100)}...
` ).join(''); searchResults.style.display = 'block'; } else { searchResults.innerHTML = '
No results found
'; searchResults.style.display = 'block'; } }); } }); {% endif %} ``` -------------------------------- ### Iterate Notion Page Children and Display Entries (Templating) Source: https://github.com/merkulovdaniil/notion4ever/blob/main/_templates/_gallery.html This snippet iterates through the children of a Notion page, retrieves corresponding database entries, and formats them as links. It handles displaying cover images, emojis, or icons alongside the page title. Dependencies include access to `site.pages` and page properties like `cover`, `emoji`, `icon`, `title`, and `url`. ```jinja {% for child_id in page.children %} {% set db_entry = site.pages[child_id] %} [{% if db_entry.cover %} ![]({{db_entry.cover}}) {% endif %} {% if db_entry.emoji %} {{db_entry.emoji}} {{db_entry.title}} {% elif db_entry.icon %} ![]({{db_entry.icon}}){{db_entry.title}} {% else %} {{db_entry.title}} {% endif %} ]({{db_entry.url}}) {% endfor %} ``` -------------------------------- ### Configure MathJax for Inline Math in Notion4Ever Head Source: https://github.com/merkulovdaniil/notion4ever/blob/main/_templates/_head.html This snippet configures MathJax to render inline mathematical expressions within the Notion4Ever project. It specifies the delimiters for inline math as '$' and '\\(', '\\)'. ```html {% block head %} {% if 'md_content' in page.keys() %}{% endif %} MathJax = { tex: { inlineMath: [['$', '$'], ['\\(', '\\)']] }, svg: { fontCache: 'global' } }; {% block tab_favicon %} {% if page.emoji %} {% elif page.icon %} {% else %} {% endif %} {% endblock tab_favicon %} {% block tab_title %} {{page.title}} {% endblock tab_title %} {% endblock head%} ``` -------------------------------- ### JavaScript: Add Copy Buttons to Code Blocks Source: https://github.com/merkulovdaniil/notion4ever/blob/main/_templates/page.html This JavaScript code snippet enhances code blocks by adding a 'Copy' button next to each one. When clicked, it copies the code content to the clipboard and provides visual feedback. It targets `pre > code` elements and uses the `navigator.clipboard` API. ```javascript document.addEventListener('DOMContentLoaded', function() { // Add copy buttons to all code blocks document.querySelectorAll('pre > code').forEach(function(codeBlock) { // Create button var button = document.createElement('button'); button.className = 'copy-button'; button.textContent = 'Copy'; // Add button to pre element (parent of code block) codeBlock.parentNode.appendChild(button); // Add click handler button.addEventListener('click', function() { // Copy code var code = codeBlock.textContent; navigator.clipboard.writeText(code).then(function() { // Success feedback button.textContent = 'Copied!'; button.classList.add('success'); // Reset after 2 seconds setTimeout(function() { button.textContent = 'Copy'; button.classList.remove('success'); }, 2000); }).catch(function(err) { console.error('Failed to copy:', err); button.textContent = 'Error'; // Reset after 2 seconds setTimeout(function() { button.textContent = 'Copy'; }, 2000); }); }); }); }); ``` -------------------------------- ### Structure Notion Content with structuring.structurize_notion_content Source: https://context7.com/merkulovdaniil/notion4ever/llms.txt Transforms raw Notion JSON data into a structured format with parsed markdown, generated URLs, family lines (breadcrumbs), and organized page relationships. This is the main data processing function that prepares content for site generation. ```python import json from notion4ever import structuring # Load previously downloaded raw Notion content with open("./notion_content.json", "r") as f: raw_notion = json.load(f) # Configuration options config = { "build_locally": False, "site_url": "https://example.com", "output_dir": "./_site", "download_files": True, "include_footer": True, "include_search": True } # Process raw Notion data into structured format structured_notion = structuring.structurize_notion_content(raw_notion, config) # Result structure: # { ``` -------------------------------- ### Convert Notion Blocks to Markdown Source: https://context7.com/merkulovdaniil/notion4ever/llms.txt Transforms Notion blocks into Markdown format, populating the 'md_content' field in the structured data. It supports various block types including headings, paragraphs, lists, and code blocks. ```python from notion4ever import markdown_parser raw_notion = { "page123": { "blocks": [ {"type": "heading_1", "heading_1": {"text": [{"plain_text": "Welcome"}]}, "has_children": False}, {"type": "paragraph", "paragraph": {"text": [{"plain_text": "Hello world"}]}, "has_children": False}, {"type": "bulleted_list_item", "bulleted_list_item": {"text": [{"plain_text": "Item 1"}]}, "has_children": False}, {"type": "code", "code": {"language": "python", "text": [{"plain_text": "print('hello')"}]}, "has_children": False} ] } } structured_notion = { "pages": { "page123": { "title": "My Page", "url": "https://example.com/my-page", "emoji": None, "icon": None, "files": [] } } } markdown_parser.parse_markdown(raw_notion, structured_notion) # The 'md_content' field will be populated in structured_notion["pages"]["page123"] # Expected markdown content: # """# Welcome # # Hello world # # * Item 1 # # ```python # print('hello') # ``` # """ ``` -------------------------------- ### Parse Notion Page Headers Source: https://context7.com/merkulovdaniil/notion4ever/llms.txt Extracts metadata from raw Notion page objects, including page type, title, last edited time, parent/child relationships, cover images, and icons. It returns a dictionary mapping page IDs to their structured header information. ```python from notion4ever import structuring raw_notion = { "12e3d165-9a44-4678-b4e2-b6a989a3c625": { "object": "page", "properties": {"title": {"title": [{"plain_text": "My Page"}]}}, "last_edited_time": "2024-01-25T22:35:00.000Z", "parent": {"type": "workspace"}, "cover": {"file": {"url": "https://notion.so/image.jpg"}}, "icon": {"emoji": "🏠"}, "blocks": [] } } headers = structuring.parse_headers(raw_notion) # Expected result structure: # { # "12e3d165-9a44-4678-b4e2-b6a989a3c625": { # "type": "page", # "title": "My Page", # "last_edited_time": "2024-01-25T22:35:00.000Z", # "parent": null, # "children": [], # "cover": "https://notion.so/image.jpg", # "icon": null, # "emoji": "🏠", # "files": ["https://notion.so/image.jpg"] # } # } ``` -------------------------------- ### Save Structured Notion Data to JSON Source: https://context7.com/merkulovdaniil/notion4ever/llms.txt Saves the structured Notion data to a JSON file with UTF-8 encoding and indentation for readability. This is useful for persisting processed Notion content. ```python import json # Assuming structured_notion is a dictionary containing the processed Notion data # Example structure: # structured_notion = { # "root_page_id": "12e3d165-9a44-4678-b4e2-b6a989a3c625", # "urls": ["https://example.com", "https://example.com/Papers", ...], # "pages": { ... } # } # Save structured data with open("./notion_structured.json", "w", encoding="utf-8") as f: json.dump(structured_notion, f, ensure_ascii=False, indent=4) ``` -------------------------------- ### Convert Notion Rich Text to Markdown Source: https://context7.com/merkulovdaniil/notion4ever/llms.txt Converts Notion's rich text array format into a Markdown string. It handles various text annotations like bold, italic, strikethrough, code, colors, links, mentions, and equations. An option to strip LaTeX for title parsing is also available. ```python from notion4ever import markdown_parser # Example Notion rich text array with formatting richtext_list = [ { "type": "text", "plain_text": "Hello ", "annotations": {"bold": False, "italic": False, "strikethrough": False, "underline": False, "code": False, "color": "default"}, "href": None, "text": {"content": "Hello ", "link": None} }, { "type": "text", "plain_text": "world", "annotations": {"bold": True, "italic": False, "strikethrough": False, "underline": False, "code": False, "color": "default"}, "href": None, "text": {"content": "world", "link": None} }, { "type": "text", "plain_text": " with link", "annotations": {"bold": False, "italic": False, "strikethrough": False, "underline": False, "code": False, "color": "default"}, "href": "https://example.com", "text": {"content": " with link", "link": {"url": "https://example.com"}} } ] markdown_text = markdown_parser.richtext_convertor(richtext_list) # Result: "Hello **world**[ with link](https://example.com)" # For title parsing (strips latex but keeps text) title_text = markdown_parser.richtext_convertor(richtext_list, title_mode=True) # Result: "Hello world with link" ``` -------------------------------- ### Displaying Archived Pages by Year (Jinja2) Source: https://github.com/merkulovdaniil/notion4ever/blob/main/_templates/archive.html This Jinja2 template code iterates through archived pages, organized by year. It displays each page's title, icon (if available), and publication date, along with its parent category. The code handles different icon types (emoji or image URL) and formats the date for readability. ```jinja2 {% for year in site['sorted_id_by_year'] %} {{year}} {% for page_id in site['sorted_id_by_year'][year] %} {% set page = site.pages[page_id] %} {% if page.emoji %} [{{page.emoji}} {{page.title}}]({{page.url}}) {% elif page.icon %} [![]({{page.icon}}){{page.title}}]({{page.url}}) {% else %} [{{page.title}}]({{page.url}}) {% endif %} {{ page['date'].strftime("%d, %b") }} in [{{ site["pages"][page["parent"]]["title"] }}]({{site[) {% endfor %} {% endfor %} ``` -------------------------------- ### Parse Notion Block with notion2json.block_parser Source: https://context7.com/merkulovdaniil/notion4ever/llms.txt Parses a single Notion block and recursively fetches all nested child blocks. Returns the block with an added 'children' key containing nested content. This function is useful for processing individual blocks within a Notion page. ```python from notion_client import Client from notion4ever import notion2json notion = Client(auth="secret_your_notion_token") # Example block from Notion API block = { "id": "abc123", "type": "toggle", "has_children": True, "toggle": {"text": [{"plain_text": "Click to expand"}]} } # Parse block and fetch all nested children parsed_block = notion2json.block_parser( block=block, notion=notion, filename="./notion_content.json", notion_json={} ) # Result: block with children populated # { # "id": "abc123", # "type": "toggle", # "has_children": True, # "toggle": {"text": [...]}, # "children": [ # {"type": "paragraph", "paragraph": {...}}, # {"type": "bulleted_list_item", ...} # ] # } ``` -------------------------------- ### Archive Page Header Path (Jinja2) Source: https://github.com/merkulovdaniil/notion4ever/blob/main/_templates/archive.html This Jinja2 code snippet defines the header path for the Archive page. It displays the current page's title and icon, followed by a breadcrumb link to the Archive itself. It supports both emoji and image-based icons for the current page. ```jinja2 {% set page = site['pages'][site['root_page_id']] %} {% if page.emoji %}* [{{page.emoji}} {{page.title}}]({{page.url}}) {% elif page.icon %}* [![]({{page.icon}}){{page.title}}]({{page.url}}) {% else %}* [{{page.title}}]({{page.url}}) {% endif %}* 🏛 Archive ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.