├── .assets └── demo.gif ├── .gitignore ├── .vscode ├── extensions.json └── launch.json ├── README.md ├── astro.config.mjs ├── global.d.ts ├── package-lock.json ├── package.json ├── public ├── astro-vim.png └── favicon.svg ├── src ├── components │ ├── Footer.astro │ ├── Header.astro │ ├── KeyBindings.astro │ ├── SearchBar.astro │ ├── Sidebar.astro │ └── StatusBar.astro ├── content │ ├── blog │ │ ├── astro-vim-ai.md │ │ └── md-features.md │ └── config.ts ├── env.d.ts ├── layouts │ └── Layout.astro ├── pages │ ├── about.astro │ ├── api │ │ └── blog-posts.json.ts │ ├── blog.astro │ ├── blog │ │ └── [slug].astro │ ├── contact.astro │ ├── exited.astro │ ├── help.astro │ └── index.astro ├── scripts │ ├── navigation.ts │ └── search.ts └── styles │ └── global.css ├── tailwind.config.cjs └── tsconfig.json /.assets/demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/albertoperdomo2/astro-vim/e4040454798eb116b503933338a766195e1260d0/.assets/demo.gif -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # build output 2 | dist/ 3 | 4 | # generated types 5 | .astro/ 6 | 7 | # dependencies 8 | node_modules/ 9 | 10 | # logs 11 | npm-debug.log* 12 | yarn-debug.log* 13 | yarn-error.log* 14 | pnpm-debug.log* 15 | 16 | # environment variables 17 | .env 18 | .env.production 19 | 20 | # macOS-specific files 21 | .DS_Store 22 | 23 | # jetbrains setting folder 24 | .idea/ 25 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": ["astro-build.astro-vscode"], 3 | "unwantedRecommendations": [] 4 | } 5 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "command": "./node_modules/.bin/astro dev", 6 | "name": "Development server", 7 | "request": "launch", 8 | "type": "node-terminal" 9 | } 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # astro-vim 2 | 3 | > [!NOTE] 4 | > **DISCLAIMER**: Status bar rendering is ~~not~~ working ~~but~~ and commands work! 5 | 6 | Welcome to the astro-vim page template! This project combines the power of Astro with the efficiency of Vim key bindings to create a unique and fast navigation experience. 7 | 8 |

9 | logo 10 |

11 | 12 | ![](.assets/demo.gif) 13 | 14 | ## Table of Contents 15 | 16 | - [Installation](#installation) 17 | - [Running the Project](#running-the-project) 18 | - [Project Structure](#project-structure) 19 | - [Customization](#customization) 20 | - [Using Vim Key Bindings](#using-vim-key-bindings) 21 | - [Generating New Content](#generating-new-content) 22 | - [License](#license) 23 | 24 | ## Installation 25 | 26 | To get started with this project, you need to have Node.js installed. You can then clone this repository and install the necessary dependencies: 27 | 28 | ```sh 29 | git clone https://github.com/albertoperdomo2/astro-vim.git 30 | cd astro-vim 31 | npm install 32 | ``` 33 | 34 | ## Running the Project 35 | 36 | After installing the dependencies, you can start the development server with the following command: 37 | 38 | ```sh 39 | npm start 40 | ``` 41 | 42 | This will start the Astro development server and you can view the site by navigating to `http://localhost:3000` in your web browser. 43 | 44 | ## Project Structure 45 | 46 | Here's an overview of the project's structure: 47 | 48 | ``` 49 | . 50 | ├── README.md 51 | ├── astro.config.mjs 52 | ├── node_modules 53 | ├── package-lock.json 54 | ├── package.json 55 | ├── public 56 | │   └── favicon.svg 57 | ├── src 58 | │ ├── components 59 | │ │  ├── Footer.astro 60 | │ │   ├── Header.astro 61 | │ │   ├── KeyBindings.astro 62 | │ │   ├── SearchBar.astro 63 | │ │   ├── Sidebar.astro 64 | │ │   └── StatusBar.astro 65 | │ ├── content 66 | │ │   ├── blog # blogs go here 67 | │ │   │   ├── astro-vim-ai.md 68 | │ │   │   └── md-features.md 69 | │ │   └── config.ts 70 | │ ├── env.d.ts 71 | │ ├── layouts 72 | │ │   └── Layout.astro 73 | │ ├── pages 74 | │ │   ├── about.astro 75 | │ │   ├── api 76 | │ │   │   └── blog-posts.json.ts 77 | │ │   ├── blog 78 | │ │   │   └── [slug].astro 79 | │ │   ├── blog.astro 80 | │ │   ├── contact.astro 81 | │ │   ├── exited.astro 82 | │ │   ├── help.astro 83 | │ │   └── index.astro 84 | │ ├── scripts 85 | │ │   ├── navigation.ts # key bindings logic 86 | │ │   └── search.ts # search logic 87 | │ └── styles 88 | │ └── global.css 89 | ├── tailwind.config.cjs 90 | └── tsconfig.json 91 | ``` 92 | 93 | - **public/**: Static assets like the favicon. 94 | - **src/components/**: Reusable UI components. 95 | - **src/content/**: Markdown files for blog posts and other content. 96 | - **src/layouts/**: Layout components for pages. 97 | - **src/pages/**: Page components. 98 | - **src/scripts/**: Custom scripts. 99 | - **src/styles/**: Tailwind CSS configuration and custom styles. 100 | 101 | ## Customization 102 | 103 | ### Tailwind CSS 104 | 105 | This project uses Tailwind CSS for styling. You can customize the styles by editing the `tailwind.config.cjs` file and adding your custom styles in the `src/styles` directory. 106 | 107 | ### Content 108 | 109 | Content is managed through Markdown files located in the `src/content` directory. You can create new blog posts or pages by adding new `.md` files in the appropriate subdirectories. 110 | 111 | ## Using Vim Key Bindings 112 | 113 | This template supports Vim-like key bindings for navigation: 114 | 115 | - Press `j` to move down. 116 | - Press `k` to move up. 117 | - Press `u` to navigate up one level. 118 | 119 | These key bindings enhance navigation efficiency, especially for users familiar with Vim. 120 | 121 | ## Generating New Content 122 | 123 | To generate new content for the blog, simply add a new markdown file in the `src/content` directory. For example: 124 | 125 | ```markdown 126 | --- 127 | title: "New Blog Post" 128 | date: 2024-08-02T04:14:54-08:00 129 | author: "Anon" 130 | summary: "A summary of your new blog post." 131 | --- 132 | 133 | # New Blog Post 134 | 135 | Content of your new blog post goes here. 136 | ``` 137 | 138 | ## License 139 | 140 | This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details. 141 | 142 | --- 143 | 144 | Feel free to customize this template to suit your needs and be sure to contribute if you feel like doing so. Happy coding! 145 | -------------------------------------------------------------------------------- /astro.config.mjs: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'astro/config'; 2 | import tailwind from '@astrojs/tailwind'; 3 | import mdx from '@astrojs/mdx'; 4 | import vercel from "@astrojs/vercel/serverless"; 5 | 6 | // https://astro.build/config 7 | export default defineConfig({ 8 | integrations: [tailwind(), mdx()], 9 | output: "server", 10 | adapter: vercel() 11 | }); -------------------------------------------------------------------------------- /global.d.ts: -------------------------------------------------------------------------------- 1 | export {}; 2 | 3 | declare global { 4 | interface Window { 5 | updateStatusBar: (mode?: string, commandLine?: string, fileInfo?: string, position?: string) => void; 6 | performSearch: (query: string) => void; 7 | } 8 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "astro-vim", 3 | "type": "module", 4 | "version": "0.0.3", 5 | "scripts": { 6 | "dev": "astro dev", 7 | "start": "astro dev", 8 | "build": "astro check && astro build", 9 | "preview": "astro preview", 10 | "astro": "astro" 11 | }, 12 | "dependencies": { 13 | "@astrojs/check": "^0.9.4", 14 | "@astrojs/markdown-remark": "^6.3.1", 15 | "@astrojs/mdx": "^4.2.6", 16 | "@astrojs/tailwind": "^6.0.2", 17 | "@astrojs/vercel": "^8.1.4", 18 | "@tailwindcss/typography": "^0.5.13", 19 | "astro": "^5.7.13", 20 | "tailwindcss": "^3.4.4", 21 | "typescript": "^5.5.2" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /public/astro-vim.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/albertoperdomo2/astro-vim/e4040454798eb116b503933338a766195e1260d0/public/astro-vim.png -------------------------------------------------------------------------------- /src/components/Footer.astro: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /src/components/Header.astro: -------------------------------------------------------------------------------- 1 |
2 |
3 | Neovim Landing 4 | 11 |
12 |
13 | -------------------------------------------------------------------------------- /src/components/KeyBindings.astro: -------------------------------------------------------------------------------- 1 |
2 |

Key Bindings

3 | 10 |
11 | -------------------------------------------------------------------------------- /src/components/SearchBar.astro: -------------------------------------------------------------------------------- 1 |
2 | 8 | 9 |
10 | 11 | 32 | -------------------------------------------------------------------------------- /src/components/Sidebar.astro: -------------------------------------------------------------------------------- 1 | --- 2 | // const pathname = Astro.url.pathname; 3 | // const isTildePage = pathname === '/' || pathname.split('/').filter(Boolean).length === 1; 4 | const isTildePage = true; 5 | --- 6 | 7 |
8 | {isTildePage ? ( 9 |
10 | {Array(100).fill('~').map((line) => ( 11 |
{line}
12 | ))} 13 |
14 | ) : ( 15 |
16 | {Array(100).fill(0).map((_, index) => ( 17 |
{index + 1}
18 | ))} 19 |
20 | )} 21 |
22 | -------------------------------------------------------------------------------- /src/components/StatusBar.astro: -------------------------------------------------------------------------------- 1 |
2 |
NORMAL
3 |
4 |
Landing Page
5 |
1:1
6 |
7 | 8 | 36 | -------------------------------------------------------------------------------- /src/content/blog/astro-vim-ai.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Vim, Astro, and AI: A Perfect Combination" 3 | date: 2024-06-26T12:07:54 4 | summary: "Exploring the synergy between Vim, Astro, and AI-generated content." 5 | author: "Alberto Perdomo" 6 | --- 7 | 8 | Welcome to the first post on our Astro Vim-inspired blog! This blog is a testament to the power of combining Vim, a highly efficient text editor, with Astro, a modern web framework. And to make things even more interesting, this blog post is generated by AI. Let's dive into how these tools can elevate your development experience. 9 | 10 | ## Vim: The Ultimate Text Editor 11 | 12 | Vim is more than just a text editor; it's a way of life for developers who value efficiency and control. With its modal editing, extensive customization options, and powerful plugins, Vim allows you to code at the speed of thought. Here are some reasons why Vim is loved by developers: 13 | 14 | - **Efficiency**: Perform complex text manipulations with just a few keystrokes. 15 | - **Customizability**: Tailor Vim to fit your workflow perfectly with a plethora of plugins and scripts. 16 | - **Ubiquity**: Vim is available on almost every operating system, making it a versatile tool for any developer. 17 | 18 | ## Astro: Modern Web Development 19 | 20 | Astro is a modern web framework that prioritizes performance and simplicity. It's designed to make building fast, content-focused websites easier. With Astro, you can bring your favorite front-end frameworks together and generate static sites that load in a flash. Some of the key features of Astro include: 21 | 22 | - **Islands Architecture**: Load only the JavaScript you need, when you need it. 23 | - **Component Framework Agnostic**: Use React, Vue, Svelte, and more all in one project. 24 | - **Built for Speed**: Generate static sites that are incredibly fast. 25 | 26 | ## Navigating the Blog 27 | 28 | Our blog page is designed to be as intuitive and efficient as Vim itself. You can navigate through the blog posts using Vim-inspired key bindings: 29 | 30 | - **`j`**: Move down to the next blog post. 31 | - **`k`**: Move up to the previous blog post. 32 | - **`u`**: Navigate up one level to the main blog page. 33 | 34 | These key bindings allow you to traverse the blog with the same efficiency you enjoy in Vim. 35 | 36 | ## AI-Generated Content 37 | 38 | This blog post is generated by AI, showcasing the power of modern technology in content creation. AI can help generate high-quality content quickly, allowing developers to focus on what they do best: coding and creating. While AI-generated content can be a great starting point, remember that the final touch and personalization should come from you. 39 | 40 | Thank you for visiting our blog! Stay tuned for more posts on Vim, Astro, and the intersection of technology and development. Happy coding! -------------------------------------------------------------------------------- /src/content/blog/md-features.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Exploring Markdown Features" 3 | date: 2024-06-26T12:34:54 4 | summary: "A showcase of different markdown elements supported in our Astro blog." 5 | author: "Alberto Perdomo" 6 | --- 7 | 8 | Welcome to another post on our Astro Vim-inspired blog! In this article, we'll explore various markdown features that you can use to enhance your blog posts. Markdown is a lightweight markup language that allows you to format text easily. Let's dive into some of the elements supported in our blog. 9 | 10 | ## Headings 11 | 12 | Markdown supports six levels of headings. Use `#` for the largest heading and `######` for the smallest. 13 | 14 | ```markdown 15 | # Heading 1 16 | ## Heading 2 17 | ### Heading 3 18 | #### Heading 4 19 | ##### Heading 5 20 | ###### Heading 6 21 | ``` 22 | 23 | ## Emphasis 24 | 25 | You can add emphasis to your text with **bold**, *italic*, or ***both***. 26 | 27 | ```markdown 28 | **bold** 29 | *italic* 30 | ***bold and italic*** 31 | ``` 32 | 33 | ## Lists 34 | 35 | ### Unordered Lists 36 | 37 | Create unordered lists using `*`, `-`, or `+`. 38 | 39 | ```markdown 40 | * Item 1 41 | * Item 2 42 | * Subitem 2.1 43 | * Subitem 2.2 44 | ``` 45 | 46 | ### Ordered Lists 47 | 48 | Create ordered lists using numbers followed by a period. 49 | 50 | ```markdown 51 | 1. First item 52 | 2. Second item 53 | 1. Subitem 2.1 54 | 2. Subitem 2.2 55 | ``` 56 | 57 | ## Links 58 | 59 | Add links using the `[text](URL)` syntax. 60 | 61 | ```markdown 62 | [Astro](https://astro.build) 63 | ``` 64 | 65 | ## Images 66 | 67 | Embed images using the `![alt text](URL)` syntax. 68 | 69 | ```markdown 70 | ![Astro Logo](https://astro.build/assets/logo.svg) 71 | ``` 72 | 73 | ## Blockquotes 74 | 75 | Create blockquotes using the `>` symbol. 76 | 77 | ```markdown 78 | > This is a blockquote. 79 | ``` 80 | 81 | ## Code 82 | 83 | ### Inline Code 84 | 85 | Include inline code using backticks. 86 | 87 | ```markdown 88 | Here is some `inline code`. 89 | ``` 90 | 91 | ### Code Blocks 92 | 93 | Create code blocks using triple backticks. 94 | 95 |
 96 | ```javascript
 97 | function helloWorld() {
 98 |   console.log("Hello, world!");
 99 | }
100 | ```
101 | 
102 | 103 | ## Tables 104 | 105 | Create tables using pipes (`|`) and hyphens (`-`). 106 | 107 | ```markdown 108 | | Syntax | Description | 109 | |--------|-------------| 110 | | Header | Title | 111 | | Paragraph | Text | 112 | ``` 113 | 114 | ## Horizontal Rules 115 | 116 | Add horizontal rules using three or more hyphens (`---`), asterisks (`***`), or underscores (`___`). 117 | 118 | ```markdown 119 | --- 120 | ``` 121 | 122 | ## Task Lists 123 | 124 | Create task lists using `- [ ]` for incomplete tasks and `- [x]` for completed tasks. 125 | 126 | ```markdown 127 | - [x] Completed task 128 | - [ ] Incomplete task 129 | ``` 130 | 131 | ## Combining Elements 132 | 133 | You can combine different markdown elements to create more complex structures. For example, here is a blockquote with a heading and a list: 134 | 135 | ```markdown 136 | > ## Blockquote with Heading 137 | > 138 | > - Item 1 139 | > - Item 2 140 | ``` 141 | 142 | ## Conclusion 143 | 144 | Markdown is a versatile and easy-to-use language that allows you to format text with minimal effort. Whether you're writing a simple blog post or complex documentation, markdown can help you create well-structured and readable content. Experiment with these elements to see how they can enhance your writing! 145 | 146 | Thank you for reading! Stay tuned for more tips and tutorials on using markdown effectively in your blog posts. -------------------------------------------------------------------------------- /src/content/config.ts: -------------------------------------------------------------------------------- 1 | import { defineCollection, z } from 'astro:content'; 2 | 3 | const blogCollection = defineCollection({ 4 | schema: z.object({ 5 | title: z.string(), 6 | date: z.date(), 7 | author: z.string().optional(), 8 | summary: z.string().optional(), 9 | }), 10 | }); 11 | 12 | export const collections = { 13 | 'blog': blogCollection, 14 | }; -------------------------------------------------------------------------------- /src/env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | -------------------------------------------------------------------------------- /src/layouts/Layout.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import Header from '../components/Header.astro'; 3 | import Sidebar from '../components/Sidebar.astro'; 4 | import StatusBar from '../components/StatusBar.astro'; 5 | import '../styles/global.css'; 6 | 7 | export interface Props { 8 | title: string; 9 | } 10 | 11 | const { title } = Astro.props; 12 | --- 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | {title} 22 | 23 | 24 |
25 | 26 |
27 | 28 |
29 |
30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /src/pages/about.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import Layout from '../layouts/Layout.astro'; 3 | --- 4 | 5 | 6 |
7 |

About

8 |
9 |

version 0.0.3

10 |
11 |

12 | Welcome to our Astro template landing page, inspired by the legendary Vim console. 13 | This template is designed for developers who understand that Vim is not just a text editor, but a way of life. 14 |

15 |

16 | Our landing page harnesses the power of Vim’s efficient and minimalist design, bringing the speed and functionality 17 | of the console to the web. With support for key bindings, you can navigate and interact with the page just as you 18 | would in your favorite editor. 19 |

20 |

21 | Why Vim? 22 | Vim is renowned for its unparalleled efficiency and control, allowing developers to perform complex text manipulations 23 | with just a few keystrokes. It's a tool that, once mastered, transforms the coding experience, making it faster and 24 | more enjoyable. 25 |

26 |

27 | Experience the future of landing pages with our Vim-inspired design, where every interaction is swift, intuitive, and 28 | satisfying. Whether you're a seasoned Vim user or new to this powerful editor, our template will enhance your web development 29 | process, making every moment on your site as productive as possible. 30 |

31 |

32 | Join us on this journey to redefine web navigation and interaction with the timeless efficiency of Vim. Embrace the best text 33 | editor’s philosophy and see how it revolutionizes the way you build and use web pages. Welcome to a new era of web development 34 | with the Astro.build Vim-inspired landing page. 35 |

36 |
37 |
38 |
39 |
40 | -------------------------------------------------------------------------------- /src/pages/api/blog-posts.json.ts: -------------------------------------------------------------------------------- 1 | import { getCollection } from 'astro:content'; 2 | 3 | export async function get() { 4 | const blogEntries = await getCollection('blog'); 5 | const posts = blogEntries.map(entry => ({ 6 | title: entry.data.title, 7 | content: entry.body, 8 | path: `/blog/${entry.slug}`, 9 | })); 10 | 11 | return new Response(JSON.stringify(posts), { 12 | status: 200, 13 | headers: { 14 | 'Content-Type': 'application/json', 15 | }, 16 | }); 17 | } 18 | -------------------------------------------------------------------------------- /src/pages/blog.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import Layout from '../layouts/Layout.astro'; 3 | import { getCollection } from 'astro:content'; 4 | 5 | const blogPosts = await getCollection('blog'); 6 | blogPosts.sort((a, b) => b.data.date.valueOf() - a.data.date.valueOf()); 7 | --- 8 | 9 | 10 |
    11 | {blogPosts.map((post, index) => ( 12 |
  • 13 | 14 | {post.data.title} 15 | 16 |

    17 | {post.data.summary} 18 |

    19 | 22 |
  • 23 | ))} 24 |
25 |
26 | 27 | 42 | 43 | -------------------------------------------------------------------------------- /src/pages/blog/[slug].astro: -------------------------------------------------------------------------------- 1 | --- 2 | import Layout from '../../layouts/Layout.astro'; 3 | import { getCollection } from 'astro:content'; 4 | 5 | const { slug } = Astro.params; 6 | const blogEntries = await getCollection('blog'); 7 | const entry = blogEntries.find((post) => post.slug === slug); 8 | 9 | if (!entry) { 10 | return Astro.redirect('/404'); 11 | } 12 | 13 | const { Content } = await entry.render(); 14 | --- 15 | 16 | 17 |
18 |
19 |

{entry.data.title}

20 | 21 |
22 |
23 |
24 | -------------------------------------------------------------------------------- /src/pages/contact.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import Layout from '../layouts/Layout.astro'; 3 | --- 4 | 5 | 6 |
7 |

Get in touch!

8 |
9 |

version 0.0.3

10 |

If you have any questions or would like to get in touch, please use the contact information below:

11 |

hi [at] albertoperdomo [dot] me

12 |
13 |
14 |
15 | -------------------------------------------------------------------------------- /src/pages/exited.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import Layout from '../layouts/Layout.astro'; 3 | --- 4 | 5 | 6 |
7 |
8 |
9 |

Web Session Closed!

10 |

Your web session has been successfully closed.

11 |

You can now safely close this tab.

12 |
13 |
14 |
15 |
-------------------------------------------------------------------------------- /src/pages/help.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import Layout from '../layouts/Layout.astro'; 3 | --- 4 | 5 | 6 |
7 |

Help page

8 |
9 |

version 0.0.3

10 |
11 |

12 | This page contains basic instructions to use this page. Some Vim key bindings are enabled and can be used within the 13 | page itself, but some of them don't work in all the pages since I did not considered it necessary. 14 |

15 | 16 |

Instructions

17 |

u: Undo the last change, in this case, it navigates up one level.

18 |

j: Move the cursor down one line.

19 |

k: Move the cursor up one line.

20 |

gg: Move the cursor to the first line of the file, in this case, page.

21 |

G: Move the cursor to the last line of the file, in this case, page.

22 |

:<command>: Executes a command.

23 |
24 |
25 |
26 |
27 | -------------------------------------------------------------------------------- /src/pages/index.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import Layout from '../layouts/Layout.astro'; 3 | --- 4 | 5 | 6 |
7 |

astro-vim-landing page

8 |
9 |

version 0.0.3

10 |

astro-vim-landing is a free and opensource blog template for tech enthusiasts

11 | github.com/albertoperdomo2/astro-vim-landing 12 |
13 |

type :about<Enter>            to view about information

14 |

type :blog<Enter>             to view blog posts

15 |

type :contact<Enter>          to view contact details

16 |
17 |

type /<Search term>           to search posts

18 |
19 |

type :h<Enter>                to view the help page

20 |

type :q<Enter>                to exit

21 |
22 |
23 |
24 |
25 | -------------------------------------------------------------------------------- /src/scripts/navigation.ts: -------------------------------------------------------------------------------- 1 | interface StatusBar { 2 | mode: HTMLElement; 3 | commandLine: HTMLElement; 4 | fileInfo: HTMLElement; 5 | position: HTMLElement; 6 | } 7 | 8 | let currentMode = 'NORMAL'; 9 | let commandBuffer = ''; 10 | 11 | document.addEventListener('keydown', (e: KeyboardEvent) => { 12 | if (currentMode === 'INSERT' && e.key !== 'Escape') { 13 | return; 14 | } 15 | 16 | switch (e.key) { 17 | case ':': 18 | e.preventDefault(); 19 | currentMode = 'COMMAND'; 20 | commandBuffer = ':'; 21 | window.updateStatusBar(currentMode, commandBuffer); 22 | break; 23 | case '/': 24 | e.preventDefault(); 25 | currentMode = 'SEARCH'; 26 | commandBuffer = '/'; 27 | window.updateStatusBar(currentMode, commandBuffer); 28 | break; 29 | case 'Escape': 30 | handleEscape(); 31 | break; 32 | case 'Enter': 33 | if (currentMode === 'COMMAND' || currentMode === 'SEARCH') { 34 | handleCommand(commandBuffer); 35 | handleEscape(); 36 | } 37 | break; 38 | case 'Backspace': 39 | if (currentMode === 'COMMAND' || currentMode === 'SEARCH') { 40 | e.preventDefault(); 41 | commandBuffer = commandBuffer.slice(0, -1); 42 | if (commandBuffer.length === 0) { 43 | handleEscape(); 44 | } else { 45 | window.updateStatusBar(undefined, commandBuffer); 46 | } 47 | } 48 | break; 49 | default: 50 | if (currentMode === 'COMMAND' || currentMode === 'SEARCH') { 51 | e.preventDefault(); 52 | commandBuffer += e.key; 53 | window.updateStatusBar(undefined, commandBuffer); 54 | } else { 55 | handleNormalModeKey(e.key); 56 | } 57 | } 58 | }); 59 | 60 | function handleEscape() { 61 | currentMode = 'NORMAL'; 62 | commandBuffer = ''; 63 | window.updateStatusBar(currentMode, ''); 64 | const searchResults = document.getElementById('search-results'); 65 | if (searchResults) { 66 | searchResults.innerHTML = ''; 67 | searchResults.classList.add('hidden'); 68 | } 69 | } 70 | 71 | function handleCommand(command: string) { 72 | if (command.startsWith(':')) { 73 | const cmd = command.slice(1).toLowerCase(); 74 | switch (cmd) { 75 | case 'blog': 76 | window.location.href = '/blog'; 77 | break; 78 | case 'about': 79 | window.location.href = '/about'; 80 | break; 81 | case 'contact': 82 | window.location.href = '/contact'; 83 | break; 84 | case 'q': 85 | window.close(); 86 | window.location.href = '/exited'; 87 | break; 88 | case 'h': 89 | window.location.href = '/help'; 90 | break; 91 | default: 92 | console.log('Unknown command:', cmd); 93 | } 94 | } else if (command.startsWith('/')) { 95 | const searchTerm = command.slice(1); 96 | window.performSearch(searchTerm); 97 | } 98 | } 99 | 100 | function handleNormalModeKey(key: string) { 101 | switch (key) { 102 | case 'u': 103 | navigateUp(); 104 | break; 105 | case 'j': 106 | window.scrollBy(0, 30); 107 | updatePosition(); 108 | break; 109 | case 'k': 110 | window.scrollBy(0, -30); 111 | updatePosition(); 112 | break; 113 | case 'g': 114 | window.scrollTo(0, 0); 115 | updatePosition(); 116 | break; 117 | case 'G': 118 | window.scrollTo(0, document.body.scrollHeight); 119 | updatePosition(); 120 | break; 121 | case 'i': 122 | currentMode = 'INSERT'; 123 | window.updateStatusBar(currentMode); 124 | break; 125 | case 'v': 126 | currentMode = 'VISUAL'; 127 | window.updateStatusBar(currentMode); 128 | break; 129 | } 130 | } 131 | 132 | function updatePosition() { 133 | const scrollPercentage = Math.round((window.scrollY / (document.documentElement.scrollHeight - window.innerHeight)) * 100); 134 | window.updateStatusBar(undefined, undefined, undefined, `${scrollPercentage}%`); 135 | } 136 | 137 | updatePosition(); 138 | window.addEventListener('scroll', updatePosition); 139 | 140 | const currentPath = window.location.pathname; 141 | window.updateStatusBar(undefined, undefined, currentPath === '/' ? 'Landing Page' : currentPath.slice(1)); 142 | 143 | function navigateUp() { 144 | const currentPath = window.location.pathname; 145 | if (currentPath === '/') return; // Already at root 146 | 147 | const pathParts = currentPath.split('/').filter(Boolean); 148 | if (pathParts.length === 1) { 149 | // If only one level deep, go to root 150 | window.location.href = '/'; 151 | } else { 152 | // Remove the last part of the path 153 | const newPath = '/' + pathParts.slice(0, -1).join('/'); 154 | window.location.href = newPath; 155 | } 156 | } -------------------------------------------------------------------------------- /src/scripts/search.ts: -------------------------------------------------------------------------------- 1 | interface BlogPost { 2 | title: string; 3 | content: string; 4 | path: string; 5 | } 6 | 7 | let blogPosts: BlogPost[] = []; 8 | 9 | async function fetchBlogPosts() { 10 | const response = await fetch('/api/blog-posts'); 11 | blogPosts = await response.json(); 12 | } 13 | 14 | function performSearch(query: string) { 15 | const results = blogPosts.filter(post => 16 | post.title.toLowerCase().includes(query.toLowerCase()) || 17 | post.content.toLowerCase().includes(query.toLowerCase()) 18 | ); 19 | 20 | const searchResults = document.getElementById('search-results'); 21 | searchResults!.innerHTML = results.map(post => ` 22 | 23 |

${post.title}

24 |

${post.content.substring(0, 100)}...

25 |
26 | `).join(''); 27 | 28 | searchResults!.classList.remove('hidden'); 29 | } 30 | 31 | document.addEventListener('DOMContentLoaded', () => { 32 | fetchBlogPosts(); 33 | }); 34 | 35 | window.performSearch = performSearch; 36 | -------------------------------------------------------------------------------- /src/styles/global.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | body { 6 | @apply bg-nvim-bg text-nvim-fg font-mono; 7 | } 8 | 9 | ::-webkit-scrollbar { 10 | width: 8px; 11 | } 12 | 13 | ::-webkit-scrollbar-track { 14 | @apply bg-nvim-bg; 15 | } 16 | 17 | ::-webkit-scrollbar-thumb { 18 | @apply bg-nvim-gray rounded; 19 | } 20 | -------------------------------------------------------------------------------- /tailwind.config.cjs: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | content: ['./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}'], 4 | theme: { 5 | extend: { 6 | colors: { 7 | 'nvim-bg': '#1c1c1c', 8 | 'nvim-fg': '#d0d0d0', 9 | 'nvim-gray': '#4e4e4e', 10 | 'nvim-blue': '#5fafd7', 11 | 'nvim-green': '#87d787', 12 | 'nvim-statusline': '#303030', 13 | }, 14 | }, 15 | }, 16 | plugins: [ 17 | require('@tailwindcss/typography'), 18 | ], 19 | }; 20 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "astro/tsconfigs/strict", 3 | "include": ["global.d.ts", "src"] 4 | } 5 | --------------------------------------------------------------------------------