├── .editorconfig ├── .env.sample ├── .envrc ├── .eslintrc.json ├── .github ├── ISSUE_TEMPLATE │ ├── 1-bug-report.yml │ └── config.yml └── workflows │ ├── test.yml │ └── warn-autogenerated-files.yaml ├── .gitignore ├── .prettierrc ├── CONTRIBUTING.md ├── Dockerfile ├── LICENSE ├── Makefile ├── README.md ├── bin └── video-to-terminal │ ├── README.md │ ├── video-to-terminal.sh │ └── video.mp4 ├── docker-compose.yaml ├── docs ├── about.mdx ├── config │ ├── index.mdx │ ├── keybind │ │ ├── index.mdx │ │ ├── reference.mdx │ │ └── sequence.mdx │ └── reference.mdx ├── features │ ├── index.mdx │ ├── shell-integration.mdx │ └── theme.mdx ├── help │ ├── gtk-single-instance.mdx │ ├── index.mdx │ ├── macos-login-shells.mdx │ ├── macos-tiling-wms.mdx │ └── terminfo.mdx ├── index.mdx ├── install │ ├── binary.mdx │ ├── build.mdx │ ├── package.mdx │ ├── pre.mdx │ └── release-notes │ │ ├── 1-0-1.mdx │ │ ├── 1-1-0.mdx │ │ ├── 1-1-1.mdx │ │ ├── 1-1-2.mdx │ │ ├── 1-1-3.mdx │ │ └── index.mdx ├── nav.json └── vt │ ├── concepts │ ├── cursor.mdx │ ├── screen.mdx │ └── sequences.mdx │ ├── control │ ├── bel.mdx │ ├── bs.mdx │ ├── cr.mdx │ ├── lf.mdx │ └── tab.mdx │ ├── csi │ ├── cbt.mdx │ ├── cht.mdx │ ├── cnl.mdx │ ├── cpl.mdx │ ├── cub.mdx │ ├── cud.mdx │ ├── cuf.mdx │ ├── cup.mdx │ ├── cuu.mdx │ ├── dch.mdx │ ├── decscusr.mdx │ ├── decslrm.mdx │ ├── decstbm.mdx │ ├── dl.mdx │ ├── dsr.mdx │ ├── ech.mdx │ ├── ed.mdx │ ├── el.mdx │ ├── hpa.mdx │ ├── hpr.mdx │ ├── ich.mdx │ ├── il.mdx │ ├── rep.mdx │ ├── sd.mdx │ ├── su.mdx │ ├── tbc.mdx │ ├── vpa.mdx │ ├── vpr.mdx │ └── xtshiftescape.mdx │ ├── esc │ ├── decaln.mdx │ ├── deckpam.mdx │ ├── deckpnm.mdx │ ├── decrc.mdx │ ├── decsc.mdx │ ├── ind.mdx │ ├── ri.mdx │ └── ris.mdx │ ├── index.mdx │ └── reference.mdx ├── flake.lock ├── flake.nix ├── next.config.mjs ├── nix └── devShell.nix ├── package-lock.json ├── package.json ├── postcss.config.json ├── public ├── favicon-128.png ├── favicon-16.png ├── favicon-256.png ├── favicon-32.png ├── favicon.ico ├── ghostty-404.png ├── ghostty-logo.svg ├── images │ └── 1-1-0 │ │ ├── csd.png │ │ ├── linear-corrected_blending.png │ │ ├── linear_blending.png │ │ ├── p3_blending.png │ │ ├── srgb_blending.png │ │ └── ssd.png ├── next.svg ├── social-share-card.jpg └── vercel.svg ├── src ├── components │ ├── animated-terminal │ │ └── index.tsx │ ├── blockquote │ │ ├── Blockquote.module.css │ │ └── index.tsx │ ├── breadcrumbs │ │ ├── Breadcrumbs.module.css │ │ └── index.tsx │ ├── button-links │ │ ├── ButtonLinks.module.css │ │ └── index.tsx │ ├── button │ │ ├── Button.module.css │ │ └── index.tsx │ ├── callout │ │ ├── Callout.module.css │ │ └── index.tsx │ ├── card-links │ │ ├── CardLinks.module.css │ │ └── index.tsx │ ├── codeblock │ │ ├── CodeBlock.module.css │ │ └── index.tsx │ ├── custom-mdx │ │ ├── CustomMDX.module.css │ │ └── index.tsx │ ├── footer │ │ ├── Footer.module.css │ │ └── index.tsx │ ├── generic-404 │ │ ├── Generic404.module.css │ │ └── index.tsx │ ├── generic-card │ │ ├── GenericCard.module.css │ │ └── index.tsx │ ├── grid-container │ │ ├── GridContainer.module.css │ │ └── index.tsx │ ├── info-cards-section │ │ ├── InfoCards.module.css │ │ └── index.tsx │ ├── jumplink-header │ │ ├── JumplinkHeader.module.css │ │ └── index.tsx │ ├── link │ │ ├── Link.module.css │ │ └── index.tsx │ ├── nav-tree │ │ ├── NavTree.module.css │ │ └── index.tsx │ ├── navbar │ │ ├── Navbar.module.css │ │ ├── ghostty-wordmark.svg │ │ └── index.tsx │ ├── scroll-to-top │ │ ├── ScrollToTop.module.css │ │ └── index.tsx │ ├── section-wrapper │ │ ├── SectionWrapper.module.css │ │ └── index.tsx │ ├── sidecar │ │ ├── Sidecar.module.css │ │ └── index.tsx │ ├── tabbed-terminals-section │ │ ├── TabbedTerminalsSection.module.css │ │ └── index.tsx │ ├── tag-list │ │ ├── TagList.module.css │ │ └── index.tsx │ ├── terminal-cards-section │ │ ├── TerminalCardsSection.module.css │ │ └── index.tsx │ ├── terminal │ │ ├── Terminal.module.css │ │ └── index.tsx │ ├── text │ │ ├── Text.module.css │ │ ├── font │ │ │ └── pretendard-std-variable.woff2 │ │ └── index.tsx │ └── vt-sequence │ │ ├── VTSequence.module.css │ │ └── index.tsx ├── layouts │ ├── nav-footer-layout │ │ └── index.tsx │ └── root-layout │ │ ├── RootLayout.module.css │ │ └── index.tsx ├── lib │ ├── fetch-docs.ts │ ├── fetch-latest-ghostty-version.ts │ ├── fetch-nav.ts │ ├── fetch-terminal-content.ts │ ├── nav-tree-to-breadcrumbs.ts │ └── use-store.ts ├── pages │ ├── 404.tsx │ ├── 404Page.module.css │ ├── Home.module.css │ ├── _app.tsx │ ├── _document.tsx │ ├── docs │ │ ├── [...path] │ │ │ ├── DocsPage.module.css │ │ │ └── index.tsx │ │ └── index.tsx │ ├── download │ │ ├── DownloadPage.module.css │ │ └── index.tsx │ └── index.tsx ├── styles │ └── globals.css └── types │ └── style.ts ├── terminals └── home │ └── animation_frames │ ├── frame_001.txt │ ├── frame_002.txt │ ├── frame_003.txt │ ├── frame_004.txt │ ├── frame_005.txt │ ├── frame_006.txt │ ├── frame_007.txt │ ├── frame_008.txt │ ├── frame_009.txt │ ├── frame_010.txt │ ├── frame_011.txt │ ├── frame_012.txt │ ├── frame_013.txt │ ├── frame_014.txt │ ├── frame_015.txt │ ├── frame_016.txt │ ├── frame_017.txt │ ├── frame_018.txt │ ├── frame_019.txt │ ├── frame_020.txt │ ├── frame_021.txt │ ├── frame_022.txt │ ├── frame_023.txt │ ├── frame_024.txt │ ├── frame_025.txt │ ├── frame_026.txt │ ├── frame_027.txt │ ├── frame_028.txt │ ├── frame_029.txt │ ├── frame_030.txt │ ├── frame_031.txt │ ├── frame_032.txt │ ├── frame_033.txt │ ├── frame_034.txt │ ├── frame_035.txt │ ├── frame_036.txt │ ├── frame_037.txt │ ├── frame_038.txt │ ├── frame_039.txt │ ├── frame_040.txt │ ├── frame_041.txt │ ├── frame_042.txt │ ├── frame_043.txt │ ├── frame_044.txt │ ├── frame_045.txt │ ├── frame_046.txt │ ├── frame_047.txt │ ├── frame_048.txt │ ├── frame_049.txt │ ├── frame_050.txt │ ├── frame_051.txt │ ├── frame_052.txt │ ├── frame_053.txt │ ├── frame_054.txt │ ├── frame_055.txt │ ├── frame_056.txt │ ├── frame_057.txt │ ├── frame_058.txt │ ├── frame_059.txt │ ├── frame_060.txt │ ├── frame_061.txt │ ├── frame_062.txt │ ├── frame_063.txt │ ├── frame_064.txt │ ├── frame_065.txt │ ├── frame_066.txt │ ├── frame_067.txt │ ├── frame_068.txt │ ├── frame_069.txt │ ├── frame_070.txt │ ├── frame_071.txt │ ├── frame_072.txt │ ├── frame_073.txt │ ├── frame_074.txt │ ├── frame_075.txt │ ├── frame_076.txt │ ├── frame_077.txt │ ├── frame_078.txt │ ├── frame_079.txt │ ├── frame_080.txt │ ├── frame_081.txt │ ├── frame_082.txt │ ├── frame_083.txt │ ├── frame_084.txt │ ├── frame_085.txt │ ├── frame_086.txt │ ├── frame_087.txt │ ├── frame_088.txt │ ├── frame_089.txt │ ├── frame_090.txt │ ├── frame_091.txt │ ├── frame_092.txt │ ├── frame_093.txt │ ├── frame_094.txt │ ├── frame_095.txt │ ├── frame_096.txt │ ├── frame_097.txt │ ├── frame_098.txt │ ├── frame_099.txt │ ├── frame_100.txt │ ├── frame_101.txt │ ├── frame_102.txt │ ├── frame_103.txt │ ├── frame_104.txt │ ├── frame_105.txt │ ├── frame_106.txt │ ├── frame_107.txt │ ├── frame_108.txt │ ├── frame_109.txt │ ├── frame_110.txt │ ├── frame_111.txt │ ├── frame_112.txt │ ├── frame_113.txt │ ├── frame_114.txt │ ├── frame_115.txt │ ├── frame_116.txt │ ├── frame_117.txt │ ├── frame_118.txt │ ├── frame_119.txt │ ├── frame_120.txt │ ├── frame_121.txt │ ├── frame_122.txt │ ├── frame_123.txt │ ├── frame_124.txt │ ├── frame_125.txt │ ├── frame_126.txt │ ├── frame_127.txt │ ├── frame_128.txt │ ├── frame_129.txt │ ├── frame_130.txt │ ├── frame_131.txt │ ├── frame_132.txt │ ├── frame_133.txt │ ├── frame_134.txt │ ├── frame_135.txt │ ├── frame_136.txt │ ├── frame_137.txt │ ├── frame_138.txt │ ├── frame_139.txt │ ├── frame_140.txt │ ├── frame_141.txt │ ├── frame_142.txt │ ├── frame_143.txt │ ├── frame_144.txt │ ├── frame_145.txt │ ├── frame_146.txt │ ├── frame_147.txt │ ├── frame_148.txt │ ├── frame_149.txt │ ├── frame_150.txt │ ├── frame_151.txt │ ├── frame_152.txt │ ├── frame_153.txt │ ├── frame_154.txt │ ├── frame_155.txt │ ├── frame_156.txt │ ├── frame_157.txt │ ├── frame_158.txt │ ├── frame_159.txt │ ├── frame_160.txt │ ├── frame_161.txt │ ├── frame_162.txt │ ├── frame_163.txt │ ├── frame_164.txt │ ├── frame_165.txt │ ├── frame_166.txt │ ├── frame_167.txt │ ├── frame_168.txt │ ├── frame_169.txt │ ├── frame_170.txt │ ├── frame_171.txt │ ├── frame_172.txt │ ├── frame_173.txt │ ├── frame_174.txt │ ├── frame_175.txt │ ├── frame_176.txt │ ├── frame_177.txt │ ├── frame_178.txt │ ├── frame_179.txt │ ├── frame_180.txt │ ├── frame_181.txt │ ├── frame_182.txt │ ├── frame_183.txt │ ├── frame_184.txt │ ├── frame_185.txt │ ├── frame_186.txt │ ├── frame_187.txt │ ├── frame_188.txt │ ├── frame_189.txt │ ├── frame_190.txt │ ├── frame_191.txt │ ├── frame_192.txt │ ├── frame_193.txt │ ├── frame_194.txt │ ├── frame_195.txt │ ├── frame_196.txt │ ├── frame_197.txt │ ├── frame_198.txt │ ├── frame_199.txt │ ├── frame_200.txt │ ├── frame_201.txt │ ├── frame_202.txt │ ├── frame_203.txt │ ├── frame_204.txt │ ├── frame_205.txt │ ├── frame_206.txt │ ├── frame_207.txt │ ├── frame_208.txt │ ├── frame_209.txt │ ├── frame_210.txt │ ├── frame_211.txt │ ├── frame_212.txt │ ├── frame_213.txt │ ├── frame_214.txt │ ├── frame_215.txt │ ├── frame_216.txt │ ├── frame_217.txt │ ├── frame_218.txt │ ├── frame_219.txt │ ├── frame_220.txt │ ├── frame_221.txt │ ├── frame_222.txt │ ├── frame_223.txt │ ├── frame_224.txt │ ├── frame_225.txt │ ├── frame_226.txt │ ├── frame_227.txt │ ├── frame_228.txt │ ├── frame_229.txt │ ├── frame_230.txt │ ├── frame_231.txt │ ├── frame_232.txt │ ├── frame_233.txt │ ├── frame_234.txt │ └── frame_235.txt └── tsconfig.json /.editorconfig: -------------------------------------------------------------------------------- 1 | [*] 2 | indent_style = space 3 | indent_size = 2 4 | 5 | [Makefile] 6 | indent_style = tab 7 | -------------------------------------------------------------------------------- /.env.sample: -------------------------------------------------------------------------------- 1 | # PORT needs to be exported for NextJS 2 | export PORT=3000 3 | -------------------------------------------------------------------------------- /.envrc: -------------------------------------------------------------------------------- 1 | # If we are a computer with nix-shell available, then use that to setup 2 | # the build environment with exactly what we need. 3 | if has nix; then 4 | watch_file nix/{devShell}.nix 5 | use flake 6 | fi 7 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/1-bug-report.yml: -------------------------------------------------------------------------------- 1 | name: Bug Report 2 | title: "[ BUG ] " 3 | description: Bug Report for ghostty.org 4 | 5 | labels: ["bug"] 6 | 7 | body: 8 | - type: markdown 9 | attributes: 10 | value: >- 11 | > [!IMPORTANT] 12 | 13 | > **This is not the place to report bugs for Ghostty, the terminal emulator**. This form should be used to report a bug in [ghostty.org](https://ghostty.org). To create a bug report for the terminal, [follow the contribution guidelines](https://github.com/ghostty-org/ghostty/blob/main/CONTRIBUTING.md). 14 | - type: dropdown 15 | id: dropdown-0 16 | attributes: 17 | label: Browser on which this bug occurs 18 | options: 19 | - Google Chrome 20 | - Microsoft Edge 21 | - Safari 22 | - Mozilla Firefox 23 | - Opera 24 | - Brave 25 | - Other (Specify Below) 26 | default: 0 27 | - type: textarea 28 | id: textarea-1 29 | attributes: 30 | value: Put the behavior you expected from the site here. 31 | label: Expected Behavior 32 | - type: textarea 33 | id: textarea-2 34 | attributes: 35 | value: Put the behavior you actually got from the site here. 36 | label: Actual Behavior 37 | - type: textarea 38 | id: textarea-3 39 | attributes: 40 | value: Put any extra information you can include here. 41 | label: Extra Details 42 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: true 2 | contact_links: 3 | - name: Terminal Issues 4 | url: https://github.com/ghostty-org/ghostty/blob/main/CONTRIBUTING.md 5 | about: "If you're looking to file an issue about the terminal, click here!" 6 | - name: Ghostty Website 7 | url: https://ghostty.org 8 | about: "Ghostty is a fast, feature-rich, and cross-platform terminal emulator that uses platform-native UI and GPU acceleration." 9 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: {} 3 | pull_request: {} 4 | workflow_dispatch: {} 5 | 6 | name: Test 7 | 8 | jobs: 9 | alejandra: 10 | runs-on: namespace-profile-ghostty-sm 11 | timeout-minutes: 60 12 | steps: 13 | - uses: actions/checkout@v4 # Check out repo so we can lint it 14 | - name: Setup Cache 15 | uses: namespacelabs/nscloud-cache-action@v1.2.0 16 | with: 17 | path: | 18 | /nix 19 | - uses: cachix/install-nix-action@v30 20 | with: 21 | nix_path: nixpkgs=channel:nixos-unstable 22 | - name: alejandra check 23 | run: nix develop -c alejandra --check . 24 | 25 | prettier: 26 | runs-on: namespace-profile-ghostty-sm 27 | timeout-minutes: 60 28 | steps: 29 | - uses: actions/checkout@v4 # Check out repo so we can lint it 30 | - name: Setup Cache 31 | uses: namespacelabs/nscloud-cache-action@v1.2.0 32 | with: 33 | path: | 34 | /nix 35 | - uses: cachix/install-nix-action@v30 36 | with: 37 | nix_path: nixpkgs=channel:nixos-unstable 38 | - name: npm install 39 | run: nix develop -c npm install 40 | - name: prettier check 41 | run: nix develop -c npm exec -- prettier --check 'src/**/*.ts' 42 | -------------------------------------------------------------------------------- /.github/workflows/warn-autogenerated-files.yaml: -------------------------------------------------------------------------------- 1 | on: 2 | pull_request_target: 3 | types: 4 | - opened 5 | branches: 6 | - main 7 | paths: 8 | - docs/config/reference.mdx 9 | - docs/config/keybind/reference.mdx 10 | 11 | name: Autogenerated files check 12 | 13 | jobs: 14 | warn-autogenerated: 15 | runs-on: namespace-profile-ghostty-sm 16 | timeout-minutes: 5 17 | permissions: 18 | pull-requests: write 19 | steps: 20 | - name: Comment on PR 21 | run: | 22 | gh pr comment "$PR_URL" -b \ 23 | "Note: This PR contains modifications to auto-generated sections of the documentation.\ 24 | These are generated from comments in ghostty's source code. Please submit changes to \ 25 | the upstream repository at https://github.com/ghostty-org/ghostty by modifying the appropriate\ 26 | source files" 27 | env: 28 | PR_URL: ${{ github.event.pull_request.html_url }} 29 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 30 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | .yarn/install-state.gz 8 | 9 | # testing 10 | /coverage 11 | 12 | # next.js 13 | /.next/ 14 | /out/ 15 | 16 | # production 17 | /build 18 | 19 | # misc 20 | .DS_Store 21 | *.pem 22 | 23 | # debug 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | 28 | # .env files 29 | .env* 30 | !.envrc 31 | !.env.sample 32 | .direnv 33 | 34 | # vercel 35 | .vercel 36 | 37 | # typescript 38 | *.tsbuildinfo 39 | next-env.d.ts 40 | 41 | # bun files 42 | bun.lockb 43 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to Ghostty.org 2 | 3 | Ghostty.org is the official Ghostty website. 4 | 5 | **Note, Ghostty.org has different contributing guidelines than 6 | the Ghostty project.** They are simpler. If you are familiar with 7 | the Ghostty project contributing guidelines, you probably still 8 | want to read the Quick Guide below. 9 | 10 | ## Quick Guide 11 | 12 | **There is a typo or grammatical error in the website.** 13 | 14 | Open a pull request with the correction. No need to make an 15 | issue or discussion. 16 | 17 | **There is a behavioral bug in the website.** 18 | 19 | Open an issue with clear steps to reproduce the bug. 20 | 21 | **There is a feature request for the website.** 22 | 23 | [Open a discussion](https://github.com/ghostty-org/ghostty/discussions) 24 | in the _Ghostty project_ repository. All feature requests even 25 | for the website flow through the Ghostty discussions as a central 26 | point. 27 | 28 | New pages or new content constitutes a feature request. 29 | 30 | Once the feature request is accepted, it will be converted to 31 | an issue in this repository. 32 | 33 | **Drive-by pull requests for features are discouraged.** If you 34 | open a PR without previous discussion with a maintainer, you do 35 | so at your own risk. 36 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # =================================== 2 | # =========== BUILD IMAGE =========== 3 | # =================================== 4 | FROM node:22-alpine AS build_image 5 | 6 | # Set the Current Working Directory inside the container 7 | WORKDIR /app 8 | 9 | # Copy package.json files 10 | COPY package.json package-lock.json ./ 11 | 12 | # Download all dependencies. Dependencies will be cached 13 | # if the package.json files are not changed 14 | RUN npm i 15 | 16 | # Copy the source from the current directory to 17 | # the Working Directory inside the container 18 | COPY . . 19 | 20 | # Compile the website (production build) 21 | RUN npm run build 22 | 23 | # Remove development dependencies 24 | RUN npm prune --production 25 | 26 | # =================================== 27 | # ========== RUNTIME IMAGE ========== 28 | # =================================== 29 | FROM node:22-alpine 30 | 31 | # Set the Current Working Directory inside the container 32 | WORKDIR /app 33 | 34 | # Adds Bash for env variable access (alpine does not ship w/ Bash) 35 | RUN apk update && apk add bash 36 | 37 | COPY --from=build_image /app/.next ./.next 38 | COPY --from=build_image /app/public ./public 39 | COPY --from=build_image /app/node_modules ./node_modules 40 | COPY --from=build_image /app/package.json ./package.json 41 | 42 | # Run it 43 | CMD ["npm", "run", "start"] 44 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Ghostty 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | SHELL := /usr/bin/env bash 2 | 3 | .PHONY: dev 4 | dev: node_modules/.installed .env.local 5 | source .env.local && npm run dev 6 | 7 | node_modules/.installed: package.json 8 | npm i && touch node_modules/.installed 9 | 10 | .env.local: 11 | cp .env.sample .env.local 12 | 13 | .PHONY: sync-webdata 14 | sync-webdata: 15 | test -d "$(GHOSTTY_BUILD_DIR)" # if this fails, set GHOSTTY_BUILD_DIR to the ghostty output directory 16 | cp $(GHOSTTY_BUILD_DIR)/share/ghostty/webdata/config.mdx ./docs/config/reference.mdx 17 | cp $(GHOSTTY_BUILD_DIR)/share/ghostty/webdata/actions.mdx ./docs/config/keybind/reference.mdx 18 | 19 | # ==================================== 20 | # ======= Docker Configuration ======= 21 | # ==================================== 22 | 23 | .PHONY: docker 24 | docker: .env.local docker-build 25 | source .env.local && docker compose up 26 | 27 | .PHONY: docker-build 28 | docker-build: 29 | docker compose build 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 |

3 | Logo 4 |
Ghostty Website 5 |

6 |

7 |

8 | This repository contains the entire source for ghostty.org. 9 |

10 | 11 | ## Local Development 12 | 13 | The official development environment is defined by Nix. You do not need 14 | to use Nix to develop the website, but the Nix environment is the 15 | only supported development environment. 16 | 17 | To spin up a local server, you can simply run `make`. 18 | 19 | ```bash 20 | make 21 | ``` 22 | 23 | Open [http://localhost:3000](http://localhost:3000) with your browser 24 | to view the website. 25 | 26 | ## Contributing 27 | 28 | If you would like to contribute to the website, please read the 29 | [contributing guidelines](CONTRIBUTING.md). 30 | -------------------------------------------------------------------------------- /bin/video-to-terminal/README.md: -------------------------------------------------------------------------------- 1 | # `video-to-terminal.sh` 2 | 3 | ```sh 4 | ./video-to-terminal.sh ./video-file.mp4 5 | ``` 6 | -------------------------------------------------------------------------------- /bin/video-to-terminal/video.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ghostty-org/website/7093f052a2f35e0247fcb828ede644ed74a749a0/bin/video-to-terminal/video.mp4 -------------------------------------------------------------------------------- /docker-compose.yaml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | services: 3 | website: 4 | container_name: ghostty-website 5 | build: . 6 | image: ghostty-org/website 7 | environment: 8 | - PORT 9 | ports: 10 | - "${PORT}:${PORT}" 11 | -------------------------------------------------------------------------------- /docs/config/keybind/sequence.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Trigger Sequences 3 | description: |- 4 | A trigger that requires a sequence of multiple keypress events. 5 | --- 6 | 7 | Ghostty supports keybindings that require a sequence of triggers 8 | to activate the action. For example, you could require `ctrl+a` 9 | followed by `n` to open a new window. In other software, this is sometimes 10 | referred to as a "leader key", a "key chord", a "key table", 11 | etc. 12 | 13 | To specify a sequence of triggers, use the 14 | [normal trigger syntax](/docs/config/keybind#trigger) and 15 | separate the triggers in the sequence with a `>` character. 16 | An example is shown below: 17 | 18 | ```ini 19 | keybind = ctrl+a>n=new_window 20 | ``` 21 | 22 | 23 | If you define a sequence as a CLI argument to `ghostty`, 24 | you probably have to quote the keybind since `>` is a special character 25 | in most shells. Example: ghostty --keybind='ctrl+a>n=new_window' 26 | 27 | 28 | A trigger sequence has some special handling: 29 | 30 | * Ghostty will wait an indefinite amount of time for the next key in 31 | the sequence. There is no way to specify a timeout. The only way to 32 | force the output of a prefix key is to assign another keybind to 33 | specifically output that key (i.e. `ctrl+a>ctrl+a=text:foo`) or 34 | press an unbound key which will send both keys to the program. 35 | 36 | * If a prefix in a sequence is previously bound, the sequence will 37 | override the previous binding. For example, if `ctrl+a` is bound to 38 | `new_window` and `ctrl+a>n` is bound to `new_tab`, pressing `ctrl+a` 39 | will do nothing. 40 | 41 | * Adding to the above, if a previously bound sequence prefix is 42 | used in a new, non-sequence binding, the entire previously bound 43 | sequence will be unbound. For example, if you bind `ctrl+a>n` and 44 | `ctrl+a>t`, and then bind `ctrl+a` directly, both `ctrl+a>n` and 45 | `ctrl+a>t` will become unbound. 46 | 47 | * Trigger sequences are not allowed for `global:` or `all:`-prefixed 48 | triggers. This is a limitation we could remove in the future. 49 | 50 | 51 | -------------------------------------------------------------------------------- /docs/help/gtk-single-instance.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: GTK Single Instance Mode 3 | description: |- 4 | GTK single instance mode is a feature that ensures only one instance of the 5 | application is running at a time. 6 | --- 7 | 8 | Ghostty uses GTK single instance mode by default when it detects 9 | it is launched from a desktop environment (i.e. not from the CLI). 10 | 11 | GTK single instance mode is recommended by the GTK project and is 12 | the idiomatic way official Gnome applications are expected to behave. 13 | When a second instance of Ghostty is launched, the original 14 | instance will create a new window, gain focus, and terminate the 15 | second instance. 16 | 17 | 18 | If you're using Ghostty on Linux and are experiencing slow startup times 19 | or high memory usage, GTK single instance detection may not be 20 | working for your environment and you may need to force it on. 21 | Continue reading this page to learn more. 22 | 23 | 24 | ## Desktop Environment Detection 25 | 26 | The default behavior of Ghostty is to use GTK single instance mode 27 | **when launched from a desktop environment.** 28 | 29 | Ghostty has this behavior because CLI terminal usage is common 30 | (i.e. `ghostty -e "some command"`) and in those cases the expected 31 | behavior is to launch a new process and block until it exits. 32 | Single instance mode doesn't allow us to easily do this so we only 33 | want to enable single instance mode when launched from a desktop 34 | environment. 35 | 36 | There isn't a standard API to detect if an application is launched 37 | from a desktop environment, so Ghostty uses the following heuristic: 38 | 39 | 1. The `GIO_LAUNCHED_DESKTOP_FILE_PID` environment variable is set. 40 | Gnome applications set this variable when launched from a `.desktop` 41 | file. 42 | 43 | 2. The PID in `GIO_LAUNCHED_DESKTOP_FILE_PID` matches the pid of 44 | the Ghostty process. This prevents Ghostty being launched from 45 | another desktop application from being detected as a desktop 46 | application. 47 | 48 | 49 | I expect that this heuristic isn't perfect. If you can improve 50 | this, please [start a GitHub discussion](https://github.com/ghostty-org/ghostty/discussions). 51 | During the private beta period of Ghostty, we had around 5,000 52 | beta testers and this heuristic overall worked very well. 53 | 54 | 55 | ## Forcing GTK Single Instance Mode On or Off 56 | 57 | You can force GTK single instance mode on or off by setting the 58 | [`gtk-single-instance`](/docs/config/reference#gtk-single-instance) option: 59 | 60 | ```ini 61 | gtk-single-instance = true 62 | ``` 63 | 64 | A value of `true` forces Ghostty to run in single instance mode. 65 | In this mode, each new `ghostty` process will result in a new window 66 | in an existing instance of Ghostty if one is running. 67 | 68 | ## Relationship to Startup Performance and Memory 69 | 70 | Ghostty is a GTK application. The GTK framework has unavoidable 71 | overhead when starting up, both in terms of how long it takes and 72 | how much memory it uses. Ghostty can't do anything about this. 73 | 74 | GTK single instance mode avoids this overhead for subsequent 75 | instances of Ghostty. Launching a second instance of Ghostty 76 | will be extremely fast and use very little memory since it is 77 | just creating a new window in the existing instance. 78 | -------------------------------------------------------------------------------- /docs/help/index.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Help 3 | description: |- 4 | How to seek help, a list of common issues and solutions, 5 | and how to report a bug or request a feature. 6 | --- 7 | 8 | Ghostty is a personal side project with no commercial backing. 9 | As such, there is no support team or dedicated resources to help you. 10 | The community is very helpful and may provide assistance, and 11 | we try to document as many common issues and solutions as possible. 12 | 13 | ## Common Issues and Solutions 14 | 15 | | Issue | Solution | 16 | |-------|----------| 17 | | Error "missing or unsuitable terminal" when running commands or using SSH. | [Terminfo](/docs/help/terminfo) | 18 | | macOS window managers such as Yabai or Aerospace act strange | [macOS Tiling Window Managers](/docs/help/macos-tiling-wms) | 19 | | Ghostty has slow startup or high memory usage on Linux. | [GTK Single Instance](/docs/help/gtk-single-instance) | 20 | | macOS defaults to login shells | [macOS Login Shells](/docs/help/macos-login-shells) 21 | 22 | ## Seeking Help 23 | 24 | If the common issues and solutions above did not help and you 25 | can't find an answer in the documentation, the following resources 26 | are available: 27 | 28 | 42 | 43 | ## Reporting Bugs or Requesting Features 44 | 45 | Please, please, please read the [contributing guide on GitHub](https://github.com/ghostty-org/ghostty/blob/main/CONTRIBUTING.md). 46 | 47 | 48 | **GitHub Issues is probably not the right place to go.** This project 49 | starts all bugs and feature requests as discussions on GitHub Discussions 50 | until more specific details are known. From there, we convert them into 51 | GitHub Issues as needed. See the contributing guide for more details. 52 | 53 | -------------------------------------------------------------------------------- /docs/help/macos-login-shells.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: macOS Login Shells 3 | description: |- 4 | In other Unix systems terminal emulators are generally started 5 | using non-login shells. Due to various reasons, macOS defaults 6 | all terminals as login shells. 7 | --- 8 | 9 | Ghostty respects the macOS traditions and will start each shell as a login 10 | shell. This matches the behavior of other terminal emulators on macOS, most 11 | notably the default Terminal.app. However, this behavior is different from 12 | other Unix systems, and may cause confusion for users who are used to the 13 | behavior of those systems. 14 | 15 | ## Why Login Shells 16 | 17 | Traditionally, unix shells have two main files that expect to be run before the user 18 | is able to interact with the shell: a profile file and the rc file. The 19 | profile file is usually run once at login and is used to set up the environment 20 | for the user. The rc file is run every time a new shell is created and is used 21 | to set up the shell itself. 22 | 23 | Using zsh as an example (the default shell on macOS since 10.15): 24 | zsh uses the `.zprofile` and `.zshrc` files. On most Linux systems `.zprofile` 25 | (or similar) is executed when the user initially logs in via something like 26 | a desktop manager. This allows subsequent shells to inherit the environment 27 | and any setup done by `.zprofile`. The idea here is that any expensive 28 | initialization only needs done once at login, and subsequent shells can reuse 29 | everything from the setup process. 30 | 31 | 32 | Zsh is used as an example but almost any shell can be used in its place and 33 | have the same behavior, with the exception of bash. Bash will **only** read 34 | `.bashrc` if the shell is both interactive and non-login. Since macOS moved from 35 | tcsh to bash in OSX 10.2 Jaguar, this difference in bash may have been 36 | overlooked, resulting in developers placing their shell setup entirely in 37 | `.profile` as `.bashrc` would rarely be run, specifically not by starting a new 38 | terminal. Now that zsh is the new default, `.zprofile` and `.zshrc` are both 39 | called starting an interactive non-login shell. 40 | 41 | 42 | macOS differs in that the GUI used to login to the system does not run 43 | `.zprofile` as it has its own method of loading in system level global 44 | settings. This means that any terminal emulator must run shells as login or 45 | else new shells would be potentially broken since they would be missing any 46 | setup process in `.zprofile`. This is the unfortunate reality, but makes sense 47 | in that there is no `.xsession` or similar, since `.zprofile` is never run, 48 | that can give a users terminals access to initial settings or set things like 49 | global environment variables [^1]. 50 | 51 | This however does *not* mean that everything should just go in `.zprofile`, or 52 | that the two files are always called together. Since zsh is started in login 53 | mode and interactive mode, both `.zprofile` and `.zshrc` will run every time 54 | a new terminal is created. However, if you run a shell inside of a terminal 55 | emulator that will only be an interactive shell and it will lose access to the 56 | parent shells settings that were initialized in `.zprofile`. You can test this 57 | by placing an alias in your `.zprofile` and starting a terminal instance and 58 | running that alias, then run `zsh` and you will find that the alias is no longer 59 | available. 60 | 61 | Therefore, most shell setup should still go in the `.zshrc` file so that any shells, 62 | terminal or otherwise, are initialized correctly. This is also somewhat in line 63 | with how dotfiles on other Linux systems would be written. 64 | 65 | [^1]: [Why are interactive shells on OSX login shells by default?](https://unix.stackexchange.com/questions/119627/why-are-interactive-shells-on-osx-login-shells-by-default) 66 | -------------------------------------------------------------------------------- /docs/help/macos-tiling-wms.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: macOS Tiling Window Managers 3 | description: |- 4 | Ghostty tabs may render as separate windows in macOS tiling 5 | window managers such as Yabai or Aerospace. 6 | --- 7 | 8 | macOS tiling window managers such as Yabai or Aerospace may 9 | render Ghostty tabs as separate windows. This is a known issue 10 | that is well documented in both the Yabai and Aerospace 11 | issue trackers. 12 | 13 | Ghostty uses macOS native tabs. macOS native tabs are represented 14 | as separate windows in the macOS window manager API. This is 15 | the API that Yabai, Aerospace, and other tiling window managers 16 | use to manage windows on macOS. 17 | 18 | As such, this is a limitation of macOS APIs and there isn't 19 | anything Ghostty can do to fix this issue. 20 | 21 | 22 | Longer term, we'd like to implement a custom tabbing solution 23 | that doesn't rely on macOS native tabs. This would allow us 24 | to better integrate with macOS tiling window managers. 25 | 26 | 27 | ## Workarounds 28 | 29 | The Ghostty community has identified potential workarounds 30 | depending on the tiling window manager you are using. 31 | 32 | ### Aerospace 33 | 34 | ```toml 35 | [[on-window-detected]] 36 | if.app-id="com.mitchellh.ghostty" 37 | run= [ 38 | "layout tiling", 39 | ] 40 | ``` 41 | 42 | If Ghostty tabs are still being rendered as separate windows, try replacing `"layout tiling"` with `"layout floating"`. Note that this will cause the Ghostty window to be floating on initial startup. You can manually unfloat the window (`alt+shift+;` followed by `f` by default). 43 | 44 | ### Yabai 45 | 46 | ``` 47 | yabai -m signal --add app='^Ghostty$' event=window_created action='yabai -m space --layout bsp' 48 | yabai -m signal --add app='^Ghostty$' event=window_destroyed action='yabai -m space --layout bsp' 49 | ``` 50 | -------------------------------------------------------------------------------- /docs/index.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Ghostty Docs 3 | description: |- 4 | Ghostty is a fast, feature-rich, and cross-platform terminal emulator 5 | that uses platform-native UI and GPU acceleration. 6 | hideSidecar: true 7 | --- 8 | 9 | ## Get Started 10 | 11 | Install Ghostty and run! 12 | [Zero configuration required](/docs/config#zero-configuration-philosophy) to get up and running. 13 | 14 | ### Installation Instructions 15 | 16 | Ready-to-run binaries for macOS. Packages or build from source for Linux. 17 | 18 | 33 | 34 |
35 | 36 | ## Featured Documentation 37 | 38 | 62 | -------------------------------------------------------------------------------- /docs/install/package.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Packaging Ghostty 3 | description: |- 4 | For people who want to build and package Ghostty for their platform. 5 | --- 6 | 7 | 8 | **This page is for people who want to build and package Ghostty for 9 | others.** If you want to just use Ghostty, you should follow the 10 | [installation instructions](/download). 11 | 12 | 13 | Ghostty relies on downstream package maintainers to distribute 14 | Ghostty to end-users. If you're interested in packaging Ghostty for 15 | your platform, _thank you very much_. 16 | 17 | We've put together a 18 | [packaging guide](https://github.com/ghostty-org/ghostty/blob/main/PACKAGING.md) 19 | within the Ghostty source tree. This is the most up to date 20 | information on how to package Ghostty. Please refer to that guide. 21 | 22 | If you have any questions or need help, we're very happy to help. 23 | Please open an issue directly on the Ghostty repository. 24 | -------------------------------------------------------------------------------- /docs/install/pre.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Prerelease Builds 3 | description: |- 4 | Help test the latest features and get the most recent bug fixes 5 | by running prerelease builds of Ghostty. 6 | --- 7 | 8 | If you're comfortable with running prerelease software, you can 9 | help test the latest features and get the most recent bug fixes 10 | by running prerelease builds of Ghostty. This helps the project 11 | significantly by providing feedback on new features and addressing 12 | possible issues before they are released to the public. 13 | 14 | 15 | During the private beta period of Ghostty, testers daily drove 16 | prerelease builds using this same process. Many didn't have a single 17 | issue for months or even over a year. The prerelease builds are 18 | generally stable, but users should always be prepared for the 19 | possibility of stability issues. 20 | 21 | 22 | An overview of how to install prerelease builds is provided below 23 | with more details in the sections following the table. 24 | 25 | | Platform | Description | 26 | | -------- | ----------- | 27 | | macOS | `auto-update-channel` to get the latest prerelease builds | 28 | | Nix | Standard `flake.nix` in the Ghostty repository | 29 | | Linux | [Build from source](/docs/install/build) | 30 | 31 | ## macOS 32 | 33 | For macOS, the Ghostty project provides signed and notarized builds 34 | for the latest commit on the `main` branch. These are available 35 | via [GitHub Releases](https://github.com/ghostty-org/ghostty/releases/tag/tip) 36 | but also via the standard macOS auto-update mechanism. 37 | 38 | If you are on a release build, you can switch to the prerelease channel 39 | by setting [`auto-update-channel`](/docs/config/reference#auto-update-channel) to `tip`. 40 | 41 | ```ini 42 | auto-update-channel = tip 43 | ``` 44 | 45 | 46 | Don't forget to restart Ghostty after changing the `auto-update-channel` 47 | setting! This setting does not take effect until Ghostty is restarted. 48 | 49 | 50 | 51 | While you can set this setting back to `stable` at any time, this will 52 | only take effect when a later stable release is available. If you want 53 | to downgrade back to the previous stable release, you must 54 | [re-download](/download) Ghostty. 55 | 56 | 57 | 58 | **Why is this setting called "tip"?** The term "tip" is a common term 59 | to refer to the latest commit on a branch in Git. Since we build 60 | prerelease builds from the latest commit on the `main` branch, we 61 | use the term "tip" to refer to the latest prerelease build rather than 62 | something like "nightly". 63 | 64 | 65 | ### Homebrew 66 | 67 | You can also install prerelease builds using Homebrew using the 68 | `@tip` version. 69 | 70 | ```shell-session 71 | brew install --cask ghostty@tip 72 | ``` 73 | 74 | 75 | This is community-maintained. The `auto-update-channel` setting 76 | is an official distribution channel. 77 | 78 | 79 | ## Nix 80 | 81 | For Nix users on Linux, there is a standard 82 | [`flake.nix` in the Ghostty repository](https://github.com/ghostty-org/ghostty/blob/main/flake.nix). 83 | Follow the same instructions as the 84 | [Nix Flake](/docs/install/build#building-with-nix) 85 | section on the building from source page. 86 | 87 | 88 | **The package in the flake only supports Linux.** Building macOS app 89 | bundles is not well supported by Nix, so the package in the flake 90 | only supports Linux. If you want to contribute a macOS package to 91 | the flake, feel free to make a pull request! 92 | 93 | 94 | 95 | -------------------------------------------------------------------------------- /docs/install/release-notes/1-1-2.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Ghostty 1.1.2 3 | description: |- 4 | Release notes for Ghostty 1.1.2, released on February 13, 2025. 5 | --- 6 | 7 | Ghostty 1.1.2 is a hotfix to fix a critical regression from 1.1.1 8 | on macOS that caused control-modified keys to not work properly 9 | in programs using Kitty Keyboard protocol such as Neovim and Fish 4.0. 10 | 11 | This was released on the same day as 1.1.1, please see the 12 | [1.1.1 release notes](/docs/install/release-notes/1-1-1). 13 | 14 | 15 | **There are no changes in this release for Linux.** Package managers 16 | can skip this release and stay on 1.1.1 until the next release if that 17 | is more convenient. 18 | 19 | -------------------------------------------------------------------------------- /docs/install/release-notes/index.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Release Notes 3 | description: |- 4 | List of all published version of Ghostty and their accompanying 5 | release notes. 6 | --- 7 | 8 | - [1.1.3](/docs/install/release-notes/1-1-3) - Released on March 24, 2025 9 | - [1.1.2](/docs/install/release-notes/1-1-2) - Released on February 13, 2025 10 | - [1.1.1](/docs/install/release-notes/1-1-1) – Released on February 13, 2025 11 | - [1.1.0](/docs/install/release-notes/1-1-0) – Released on January 30, 2025 12 | - [1.0.1](/docs/install/release-notes/1-0-1) – Released on December 31, 2024 13 | -------------------------------------------------------------------------------- /docs/vt/concepts/cursor.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Cursor 3 | description: |- 4 | The cursor is the row and column where the next 5 | character will be printed or location-sensitive control 6 | sequence will be executed. 7 | --- 8 | 9 | The cursor is always present in a terminal and is located at 10 | some row and column within the active screen area. The cursor 11 | may be visually hidden, but the terminal internal state always 12 | has a cursor, and it is always located at some active position. 13 | 14 | The cursor most commonly is associated with where the next 15 | printed character will be placed. However, the cursor is also 16 | used for location-sensitive control sequences. For example, 17 | when an [erase line control sequence](/docs/vt/csi/el) is 18 | executed, the cursor determines the first line to erase. 19 | 20 | The terminal has a single cursor [per screen](/docs/vt/concepts/screen). 21 | **Note that this document is about the cursor as it relates 22 | to the [terminal API](/docs/vt).** Applications such as editors may 23 | have their own concept known as a "cursor" that is completely 24 | unrelated to the terminal cursor. For example, an editor may support 25 | "multiple cursors", but the underlying terminal API is both a separate 26 | concept and only supports a single cursor at any given moment. 27 | 28 | ## Initial State 29 | 30 | The cursor is always initially located at the top-left corner of the screen. 31 | 32 | ## Pending Wrap State 33 | 34 | The pending wrap state is a boolean value that is set when a character 35 | is printed in the rightmost column of the screen to indicate that the 36 | next printed character should wrap to the next line. 37 | 38 | If the pending wrap state is set, the next printed character will 39 | move the cursor to the leftmost column of the next line, unset 40 | the pending wrap state, and then print the character[^1]. 41 | 42 | The pending wrap state may feel like an obvious and inconsequential 43 | feature, but it has a significant (but subtle) impact on cursor 44 | behavior. For example, print followed by [backspace](/docs/vt/control/bs) 45 | behaves differently depending on if you're printing in the rightmost 46 | column of the screen or not. 47 | 48 | If you print a character in any column other than the rightmost column 49 | and then send a [backspace](/docs/vt/control/bs) control character, the 50 | cursor will move back on top of the most recently printed character. 51 | But if you print a character in the rightmost column and then send a 52 | [backspace](/docs/vt/control/bs) control character, the cursor will move 53 | to the left of the character most recently printed. This is the 54 | source of [bugs in multiple popular shell prompts](https://github.com/ghostty-org/ghostty/issues/884). 55 | 56 | You will see that many control sequences note that they 57 | "unset the pending wrap state". This is just as it sounds: if the 58 | pending wrap state is set on the cursor, it becomes unset. The next 59 | printed character will not wrap to the next line. 60 | 61 | [^1]: This isn't strictly true. Wraparound modes and scroll regions 62 | can change this behavior. 63 | -------------------------------------------------------------------------------- /docs/vt/concepts/screen.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Screen 3 | description: TODO 4 | --- 5 | 6 | TODO 7 | -------------------------------------------------------------------------------- /docs/vt/control/bel.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Bell (BEL) 3 | description: Raise the attention of the user. 4 | --- 5 | 6 | 7 | 8 | The purpose of the bell sequence is to raise the attention 9 | of the user. 10 | 11 | Historically, this would 12 | [ring a physical bell](https://en.wikipedia.org/wiki/Bell_character). 13 | Today, many alternate behaviors are acceptable: 14 | 15 | - An audible sound can be played through the speakers 16 | - Background or border of a window can visually flash 17 | - The terminal window can come into focus or be put on top 18 | - Application icon can bounce or otherwise draw attention 19 | - A desktop notification can be shown 20 | 21 | Normally, the bell behavior is configurable and can be disabled. 22 | 23 | ## BEL as an OSC Terminator 24 | 25 | The `BEL` character is also a valid terminating character for 26 | OSC sequences, although `ST` is preferred. If `BEL` is the 27 | terminating character for an OSC sequence, any responses should 28 | also terminate with the `BEL` character.[^1] 29 | 30 | ## Ghostty Status 31 | 32 | This control character is not currently implemented in Ghostty, 33 | but can be used as an OSC terminator. If it is used as an OSC 34 | terminator, Ghostty will terminate any responses with the 35 | `BEL` character. 36 | 37 | There is an [open discussion](https://github.com/ghostty-org/ghostty/discussions/2710) 38 | about implementing the bell character in Ghostty and what 39 | that will look like. 40 | 41 | [^1]: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html 42 | -------------------------------------------------------------------------------- /docs/vt/control/bs.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Backspace (BS) 3 | description: Move the cursor backward one position. 4 | --- 5 | 6 | 7 | 8 | This sequence performs [cursor backward (CUB)](/docs/vt/csi/cub) 9 | with `Pn = 1`. There is no additional or different behavior for 10 | using `BS`. 11 | -------------------------------------------------------------------------------- /docs/vt/control/cr.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Carriage Return (CR) 3 | description: Move the cursor to the leftmost column. 4 | --- 5 | 6 | 7 | 8 | This sequence always unsets the pending wrap state. 9 | 10 | If [origin mode (mode 6)](#TODO) is enabled, the cursor is set to the 11 | [left margin](#TODO) of the scroll region and the operation is complete. 12 | 13 | If origin mode is _not_ set and the cursor is on or to the right of the 14 | left margin, the cursor is set to the left margin. If the cursor is to the left 15 | of the left margin, the cursor is moved to the leftmost column in the terminal. 16 | 17 | ## Validation 18 | 19 | ### CR V-1: Pending Wrap is Unset 20 | 21 | ```bash 22 | cols=$(tput cols) 23 | printf "\033[${cols}G" # move to last column 24 | printf "A" # set pending wrap state 25 | printf "\r" 26 | printf "X" 27 | echo 28 | ``` 29 | 30 | ``` 31 | |X________A| 32 | |c_________| 33 | ``` 34 | 35 | ### CR V-2: Left Margin 36 | 37 | ```bash 38 | cols=$(tput cols) 39 | printf "\033[1;1H" # move to top-left 40 | printf "\033[0J" # clear screen 41 | printf "\033[?69h" # enable left/right margin mode 42 | printf "\033[2;5s" # set left/right margin 43 | printf "\033[4G" 44 | printf "A" 45 | printf "\r" 46 | printf "X" 47 | ``` 48 | 49 | ``` 50 | |_XcA______| 51 | ``` 52 | 53 | ### CR V-3: Left of Left Margin 54 | 55 | ```bash 56 | cols=$(tput cols) 57 | printf "\033[1;1H" # move to top-left 58 | printf "\033[0J" # clear screen 59 | printf "\033[?69h" # enable left/right margin mode 60 | printf "\033[2;5s" # set left/right margin 61 | printf "\033[4G" 62 | printf "A" 63 | printf "\033[1G" 64 | printf "\r" 65 | printf "X" 66 | ``` 67 | 68 | ``` 69 | |Xc_A______| 70 | ``` 71 | 72 | ### CR V-3: Left Margin with Origin Mode 73 | 74 | ```bash 75 | cols=$(tput cols) 76 | printf "\033[1;1H" # move to top-left 77 | printf "\033[0J" # clear screen 78 | printf "\033[?6h" # enable origin mode 79 | printf "\033[?69h" # enable left/right margin mode 80 | printf "\033[2;5s" # set left/right margin 81 | printf "\033[4G" 82 | printf "A" 83 | printf "\033[1G" 84 | printf "\r" 85 | printf "X" 86 | ``` 87 | 88 | ``` 89 | |_XcA______| 90 | ``` 91 | -------------------------------------------------------------------------------- /docs/vt/control/lf.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Linefeed (LF) 3 | description: Move the cursor down one line, scrolling if necessary. 4 | --- 5 | 6 | 7 | 8 | This is an alias for [index (IND)](/docs/vt/esc/ind). 9 | 10 | If [linefeed mode (mode 20)](#TODO) is enabled, perform a 11 | [carriage return](/docs/vt/control/cr) after the IND operation. 12 | -------------------------------------------------------------------------------- /docs/vt/control/tab.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Tab (TAB) 3 | description: Move the cursor right to the next tab stop. 4 | --- 5 | 6 | 7 | 8 | This is an alias for [cursor horizontal tabulation (CHT)](/docs/vt/csi/cht) with 9 | `n = 1`. 10 | -------------------------------------------------------------------------------- /docs/vt/csi/cbt.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Cursor Backward Tabulation (CBT) 3 | description: Move the cursor `n` tabs left. 4 | --- 5 | 6 | 7 | 8 | The leftmost valid column for this operation is the first column. If 9 | [origin mode](#TODO) is enabled, then the leftmost valid column for this 10 | operation is the [left margin](#TODO). 11 | 12 | Move the cursor left until the cursor position is on a tabstop. If the 13 | cursor would move past the leftmost valid column, the cursor remains at 14 | the leftmost valid column and the operation completes. Repeat this process 15 | `n` times. 16 | 17 | Tabstops are dynamic and can be set with escape sequences such as 18 | [horizontal tab set (HTS)](/docs/vt/csi/hts), [tab clear (TBC)](/docs/vt/csi/tbc), etc. 19 | A terminal emulator may default tabstops at any interval, though an interval 20 | of 8 spaces is most common. 21 | 22 | ## Validation 23 | 24 | ### CBT V-1: Left Beyond First Column 25 | 26 | ```bash 27 | printf "\033[?5W" # reset tab stops 28 | printf "\033[10Z" 29 | printf "A" 30 | ``` 31 | 32 | ``` 33 | |Ac________| 34 | ``` 35 | 36 | ### CBT V-2: Left Starting After Tab Stop 37 | 38 | ```bash 39 | printf "\033[?5W" # reset tab stops 40 | printf "\033[1;10H" 41 | printf "X" 42 | printf "\033[Z" 43 | printf "A" 44 | ``` 45 | 46 | ``` 47 | |________AX| 48 | ``` 49 | 50 | ### CBT V-3: Left Starting on Tabstop 51 | 52 | ```bash 53 | printf "\033[?5W" # reset tab stops 54 | printf "\033[1;9H" 55 | printf "X" 56 | printf "\033[1;9H" 57 | printf "\033[Z" 58 | printf "A" 59 | ``` 60 | 61 | ``` 62 | |A_______X_| 63 | ``` 64 | 65 | ### CBT V-4: Left Margin with Origin Mode 66 | 67 | ```bash 68 | printf "\033[1;1H" # move to top-left 69 | printf "\033[0J" # clear screen 70 | printf "\033[?5W" # reset tab stops 71 | printf "\033[?6h" # enable origin mode 72 | printf "\033[?69h" # enable left/right margins 73 | printf "\033[3;6s" # scroll region left/right 74 | printf "\033[1;2H" # move cursor in region 75 | printf "X" 76 | printf "\033[Z" 77 | printf "A" 78 | ``` 79 | 80 | ``` 81 | |__AX______| 82 | ``` 83 | -------------------------------------------------------------------------------- /docs/vt/csi/cht.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Cursor Horizontal Tabulation (CHT) 3 | description: Move the cursor right `n` tabs. 4 | --- 5 | 6 | 7 | 8 | The parameter `n` must be an integer greater than or equal to 1. If `n` is less than 9 | or equal to 0, adjust `n` to be 1. If `n` is omitted, `n` defaults to 1. 10 | 11 | The rightmost valid column for this operation is the rightmost column in 12 | the terminal screen or the [right margin](#TODO), whichever is smaller. 13 | This sequence does not change behavior with [origin mode](#TODO) set. 14 | 15 | Move the cursor right until the cursor position is on a tabstop. If the 16 | cursor would move past the rightmost valid column, the cursor remains at 17 | the rightmost valid column and the operation completes. Repeat this process 18 | `n` times. 19 | 20 | Tabstops are dynamic and can be set with escape sequences such as 21 | [horizontal tab set (HTS)](/docs/vt/csi/hts), [tab clear (TBC)](/docs/vt/csi/tbc), etc. 22 | A terminal emulator may default tabstops at any interval, though an interval 23 | of 8 spaces is most common. 24 | 25 | ## Validation 26 | 27 | ### CHT V-1: Right Beyond Last Column 28 | 29 | ```bash 30 | printf "\033[?5W" # reset tab stops 31 | printf "\033[100I" # assuming the test terminal has less than 800 columns 32 | printf "A" 33 | ``` 34 | 35 | ``` 36 | |_________A| 37 | ``` 38 | 39 | ### CHT V-2: Right From Before a Tabstop 40 | 41 | ```bash 42 | printf "\033[?5W" # reset tab stops 43 | printf "\033[1;2H" 44 | printf "A" 45 | printf "\033[I" 46 | printf "X" 47 | ``` 48 | 49 | ``` 50 | |_A______X_| 51 | ``` 52 | 53 | ### CHT V-3: Right Margin 54 | 55 | ```bash 56 | printf "\033[1;1H" # move to top-left 57 | printf "\033[0J" # clear screen 58 | printf "\033[?5W" # reset tab stops 59 | printf "\033[?69h" # enable left/right margins 60 | printf "\033[3;6s" # scroll region left/right 61 | printf "\033[1;1H" # move cursor in region 62 | printf "X" 63 | printf "\033[I" 64 | printf "A" 65 | ``` 66 | 67 | ``` 68 | |__AX______| 69 | ``` 70 | -------------------------------------------------------------------------------- /docs/vt/csi/cnl.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Cursor Next Line (CNL) 3 | description: Move the cursor down `n` cells and to the beginning of the line. 4 | --- 5 | 6 | 7 | 8 | The parameter `n` must be an integer greater than or equal to 1. 9 | If `n` is less than or equal to 0, adjust `n` to be 1. If `n` is 10 | omitted, `n` defaults to 1. 11 | 12 | The logic of this sequence is identical to 13 | [Cursor Down (CUD)](/docs/vt/csi/cud) 14 | followed by [Carriage Return (CR)](/docs/vt/control/cr). 15 | -------------------------------------------------------------------------------- /docs/vt/csi/cpl.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Cursor Previous Line (CPL) 3 | description: Move the cursor up `n` cells and to the beginning of the line. 4 | --- 5 | 6 | 7 | 8 | The parameter `n` must be an integer greater than or equal to 1. If `n` is less than 9 | or equal to 0, adjust `n` to be 1. If `n` is omitted, `n` defaults to 1. 10 | 11 | The logic of this sequence is identical to [Cursor Up (CUU)](/docs/vt/csi/cuu) 12 | followed by [Carriage Return (CR)](/docs/vt/control/cr). 13 | -------------------------------------------------------------------------------- /docs/vt/csi/cud.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Cursor Down (CUD) 3 | description: Move the cursor down `n` cells. 4 | --- 5 | 6 | 7 | 8 | The parameter `n` must be an integer greater than or equal to 1. If `n` is less than 9 | or equal to 0, adjust `n` to be 1. If `n` is omitted, `n` defaults to 1. 10 | 11 | This sequence always unsets the pending wrap state. 12 | 13 | If the current cursor position is at or above the [bottom margin](#TODO), 14 | the lowest point the cursor can move is the bottom margin. If the current 15 | cursor position is below the bottom margin, the lowest point the cursor 16 | can move is the final row. 17 | 18 | This sequence never triggers scrolling. 19 | 20 | ## Validation 21 | 22 | ### CUD V-1: Cursor Down 23 | 24 | ```bash 25 | printf "A" 26 | printf "\033[2B" # cursor down 27 | printf "X" 28 | ``` 29 | 30 | ``` 31 | |A_________| 32 | |__________| 33 | |_Xc_______| 34 | ``` 35 | 36 | ### CUD V-2: Cursor Down Above Bottom Margin 37 | 38 | ```bash 39 | printf "\033[1;1H" # move to top-left 40 | printf "\033[0J" # clear screen 41 | printf "\n\n\n\n" # screen is 4 high 42 | printf "\033[1;3r" # set scrolling region 43 | printf "A" 44 | printf "\033[5B" # cursor down 45 | printf "X" 46 | ``` 47 | 48 | ``` 49 | |A_________| 50 | |__________| 51 | |_Xc_______| 52 | |__________| 53 | ``` 54 | 55 | ### CUD V-3: Cursor Down Below Bottom Margin 56 | 57 | ```bash 58 | printf "\033[1;1H" # move to top-left 59 | printf "\033[0J" # clear screen 60 | printf "\n\n\n\n\n" # screen is 5 high 61 | printf "\033[1;3r" # set scrolling region 62 | printf "A" 63 | printf "\033[4;1H" # move below region 64 | printf "\033[5B" # cursor down 65 | printf "X" 66 | ``` 67 | 68 | ``` 69 | |A_________| 70 | |__________| 71 | |__________| 72 | |__________| 73 | |_Xc_______| 74 | ``` 75 | -------------------------------------------------------------------------------- /docs/vt/csi/cuf.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Cursor Forward (CUF) 3 | description: Move the cursor `n` cells right. 4 | --- 5 | 6 | 7 | 8 | The parameter `n` must be an integer greater than or equal to 1. If `n` is less than 9 | or equal to 0, adjust `n` to be 1. If `n` is omitted, `n` defaults to 1. 10 | 11 | This sequence always unsets the pending wrap state. 12 | 13 | The rightmost boundary the cursor can move to is determined by the current 14 | cursor column and the [right margin](#TODO). If the cursor begins to the right 15 | of the right margin, modify the right margin to be the rightmost column 16 | of the screen for the duration of the sequence. The rightmost column the cursor 17 | can be on is the right margin. 18 | 19 | Move the cursor `n` cells to the right up to and including the rightmost boundary. 20 | This sequence never wraps or modifies cell content. This sequence is not affected 21 | by any terminal modes. 22 | 23 | ## Validation 24 | 25 | ### CUF V-1: Pending Wrap is Unset 26 | 27 | ```bash 28 | cols=$(tput cols) 29 | printf "\033[${cols}G" # move to last column 30 | printf "A" # set pending wrap state 31 | printf "\033[C" # move forward one 32 | printf "XYZ" 33 | ``` 34 | 35 | ``` 36 | |_________X| 37 | |YZ________| 38 | ``` 39 | 40 | ### CUF V-2: Rightmost Boundary with Reverse Wrap Disabled 41 | 42 | ```bash 43 | printf "A" 44 | printf "\033[500C" # forward larger than screen width 45 | printf "B" 46 | ``` 47 | 48 | ``` 49 | |A________Bc 50 | ``` 51 | 52 | ### CUF V-3: Left of the Right Margin 53 | 54 | ```bash 55 | printf "\033[1;1H" # move to top-left 56 | printf "\033[0J" # clear screen 57 | printf "\033[?69h" # enable left/right margins 58 | printf "\033[3;5s" # scroll region left/right 59 | printf "\033[1G" # move to left 60 | printf "\033[500C" # forward larger than screen width 61 | printf "X" 62 | ``` 63 | 64 | ``` 65 | |____X_____| 66 | ``` 67 | 68 | ### CUF V-4: Right of the Right Margin 69 | 70 | ```bash 71 | printf "\033[1;1H" # move to top-left 72 | printf "\033[0J" # clear screen 73 | printf "\033[?69h" # enable left/right margins 74 | printf "\033[3;5s" # scroll region left/right 75 | printf "\033[6G" # move to right of margin 76 | printf "\033[500C" # forward larger than screen width 77 | printf "X" 78 | ``` 79 | 80 | ``` 81 | |_________X| 82 | ``` 83 | -------------------------------------------------------------------------------- /docs/vt/csi/cup.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Cursor Position (CUP) 3 | description: Move the cursor to row `y` and column `x`. 4 | --- 5 | 6 | 7 | 8 | The parameters `y` and `x` must be integers greater than or equal to 1. 9 | If either is less than or equal to 0, adjust that parameter to be 1. 10 | 11 | The values `y` and `x` are both one-based. For example, the top row is row 1 12 | and the leftmost column on the screen is column 1. 13 | 14 | This sequence always unsets the pending wrap state. 15 | 16 | If [origin mode](#TODO) is **NOT** set, the cursor is moved exactly to the 17 | row and column specified by `y` and `x`. The maximum value for `y` is the 18 | bottom row of the screen and the maximum value for `x` is the rightmost 19 | column of the screen. 20 | 21 | If [origin mode](#TODO) is set, the cursor position is set relative 22 | to the top-left corner of the scroll region. `y = 1` corresponds to 23 | the [top margin](#TODO) and `x = 1` corresponds to the [left margin](#TODO). 24 | The maximum value for `y` is the [bottom margin](#TODO) and the maximum 25 | value for `x` is the [right margin](#TODO). 26 | 27 | When origin mode is set, it is impossible set a cursor position using 28 | this sequence outside the boundaries of the scroll region. 29 | 30 | ## Validation 31 | 32 | ### CUP V-1: Normal Usage 33 | 34 | ```bash 35 | printf "\033[1;1H" # move to top-left 36 | printf "\033[0J" # clear screen 37 | printf "\033[2;3H" 38 | printf "A" 39 | ``` 40 | 41 | ``` 42 | |__________| 43 | |__Ac______| 44 | ``` 45 | 46 | ### CUP V-2: Off the Screen 47 | 48 | ```bash 49 | printf "\033[1;1H" # move to top-left 50 | printf "\033[0J" # clear screen 51 | printf "\033[500;500H" 52 | printf "A" 53 | ``` 54 | 55 | ``` 56 | |__________| 57 | |__________| 58 | |_________Ac 59 | ``` 60 | 61 | ### CUP V-3: Relative to Origin 62 | 63 | ```bash 64 | printf "\033[1;1H" # move to top-left 65 | printf "\033[0J" # clear screen 66 | printf "\033[2;3r" # scroll region top/bottom 67 | printf "\033[?6h" # origin mode 68 | printf "\033[1;1H" # move to top-left 69 | printf "X" 70 | ``` 71 | 72 | ``` 73 | |__________| 74 | |X_________| 75 | ``` 76 | 77 | ### CUP V-4: Relative to Origin with Left/Right Margins 78 | 79 | ```bash 80 | printf "\033[1;1H" # move to top-left 81 | printf "\033[0J" # clear screen 82 | printf "\033[?69h" # enable left/right margins 83 | printf "\033[3;5s" # scroll region left/right 84 | printf "\033[2;3r" # scroll region top/bottom 85 | printf "\033[?6h" # origin mode 86 | printf "\033[1;1H" # move to top-left 87 | printf "X" 88 | ``` 89 | 90 | ``` 91 | |__________| 92 | |__X_______| 93 | ``` 94 | 95 | ### CUP V-5: Limits with Scroll Region and Origin Mode 96 | 97 | ```bash 98 | printf "\033[1;1H" # move to top-left 99 | printf "\033[0J" # clear screen 100 | printf "\033[?69h" # enable left/right margins 101 | printf "\033[3;5s" # scroll region left/right 102 | printf "\033[2;3r" # scroll region top/bottom 103 | printf "\033[?6h" # origin mode 104 | printf "\033[500;500H" # move to top-left 105 | printf "X" 106 | ``` 107 | 108 | ``` 109 | |__________| 110 | |__________| 111 | |____X_____| 112 | ``` 113 | 114 | ### CUP V-6: Pending Wrap is Unset 115 | 116 | ```bash 117 | cols=$(tput cols) 118 | printf "\033[${cols}G" # move to last column 119 | printf "A" # set pending wrap state 120 | printf "\033[1;1H" 121 | printf "X" 122 | ``` 123 | 124 | ``` 125 | |Xc_______X| 126 | ``` 127 | -------------------------------------------------------------------------------- /docs/vt/csi/cuu.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Cursor Up (CUU) 3 | description: Move the cursor `n` cells up. 4 | --- 5 | 6 | 7 | 8 | The parameter `n` must be an integer greater than or equal to 1. If `n` is less than 9 | or equal to 0, adjust `n` to be 1. If `n` is omitted, `n` defaults to 1. 10 | 11 | This sequence always unsets the pending wrap state. 12 | 13 | If the current cursor position is at or below the [top margin](#TODO), 14 | the highest point the cursor can move is the top margin. If the current 15 | cursor position is above the top margin, the highest point the cursor 16 | can move is the first row. 17 | 18 | ## Validation 19 | 20 | ### CUU V-1: Cursor Up 21 | 22 | ```bash 23 | printf "\033[1;1H" # move to top-left 24 | printf "\033[0J" # clear screen 25 | printf "\033[3;1H" 26 | printf "A" 27 | printf "\033[2A" # cursor up 28 | printf "X" 29 | ``` 30 | 31 | ``` 32 | |_Xc_______| 33 | |__________| 34 | |A_________| 35 | ``` 36 | 37 | ### CUU V-2: Cursor Up Below Top Margin 38 | 39 | ```bash 40 | printf "\033[1;1H" # move to top-left 41 | printf "\033[0J" # clear screen 42 | printf "\n\n\n\n" # screen is 4 high 43 | printf "\033[2;4r" # set scrolling region 44 | printf "\033[3;1H" 45 | printf "A" 46 | printf "\033[5A" # cursor up 47 | printf "X" 48 | ``` 49 | 50 | ``` 51 | |__________| 52 | |_Xc_______| 53 | |A_________| 54 | |__________| 55 | ``` 56 | 57 | ### CUU V-3: Cursor Up Above Top Margin 58 | 59 | ```bash 60 | printf "\033[1;1H" # move to top-left 61 | printf "\033[0J" # clear screen 62 | printf "\n\n\n\n\n" # screen is 5 high 63 | printf "\033[3;5r" # set scrolling region 64 | printf "\033[3;1H" 65 | printf "A" 66 | printf "\033[2;1H" # move above region 67 | printf "\033[5A" # cursor up 68 | printf "X" 69 | ``` 70 | 71 | ``` 72 | |Xc________| 73 | |__________| 74 | |A_________| 75 | |__________| 76 | |__________| 77 | ``` 78 | -------------------------------------------------------------------------------- /docs/vt/csi/dch.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Delete Character (DCH) 3 | description: |- 4 | Delete `n` characters at the current cursor position and shift existing 5 | cell contents left. 6 | --- 7 | 8 | 9 | 10 | The parameter `n` must be an integer greater than or equal to 1. If `n` is less than 11 | or equal to 0, adjust `n` to be 1. If `n` is omitted, `n` defaults to 1. 12 | 13 | If the current cursor position is outside of the current scroll region, 14 | this sequence does nothing. The cursor is outside of the current scroll 15 | region if it is left of the [left margin](#TODO), or right of the 16 | [right margin](#TODO). 17 | 18 | This sequence unsets the pending wrap state. This sequence does _not_ unset 19 | the pending wrap state if the cursor position is outside of the current 20 | scroll region. This has to be called out explicitly because this behavior 21 | differs from [Insert Character (ICH)](/docs/vt/csi/ich). 22 | 23 | Only cells within the scroll region are deleted or shifted. Cells to the 24 | right of the right margin are unmodified. 25 | The blank cells inserted from the right margin are blank with the background 26 | color colored according to the current SGR state. 27 | 28 | If a multi-cell character (such as "橋") is shifted so that the cell is split 29 | in half, the multi-cell character can either be clipped or erased. Typical 30 | behavior is to clip at the right edge of the screen and erase at a right 31 | margin, but either behavior is acceptable. 32 | 33 | ## Validation 34 | 35 | ### DCH V-1: Simple Delete Character 36 | 37 | ```bash 38 | printf "ABC123" 39 | printf "\033[3G" 40 | printf "\033[2P" 41 | ``` 42 | 43 | ``` 44 | |AB23____| 45 | ``` 46 | 47 | ### DCH V-2: SGR State 48 | 49 | ```bash 50 | printf "ABC123" 51 | printf "\033[3G" 52 | printf "\033[41m" 53 | printf "\033[2P" 54 | ``` 55 | 56 | ``` 57 | |AB23____| 58 | ``` 59 | 60 | The two rightmost cells should have a red background color. 61 | 62 | ### DCH V-3: Outside Left/Right Scroll Region 63 | 64 | ```bash 65 | printf "\033[1;1H" # move to top-left 66 | printf "\033[0J" # clear screen 67 | printf "ABC123" 68 | printf "\033[?69h" # enable left/right margins 69 | printf "\033[3;5s" # scroll region left/right 70 | printf "\033[2G" 71 | printf "\033[P" 72 | ``` 73 | 74 | ``` 75 | |ABC123__| 76 | ``` 77 | 78 | ### DCH V-4: Inside Left/Right Scroll Region 79 | 80 | ```bash 81 | printf "\033[1;1H" # move to top-left 82 | printf "\033[0J" # clear screen 83 | printf "ABC123" 84 | printf "\033[?69h" # enable left/right margins 85 | printf "\033[3;5s" # scroll region left/right 86 | printf "\033[4G" 87 | printf "\033[P" 88 | ``` 89 | 90 | ``` 91 | |ABC2_3__| 92 | ``` 93 | 94 | ### DCH V-5: Split Wide Character 95 | 96 | ```bash 97 | printf "\033[1;1H" # move to top-left 98 | printf "\033[0J" # clear screen 99 | printf "A橋123" 100 | printf "\033[3G" 101 | printf "\033[P" 102 | ``` 103 | 104 | ``` 105 | |A_123_____| 106 | ``` 107 | -------------------------------------------------------------------------------- /docs/vt/csi/decscusr.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Set Cursor Style (DECSCUSR) 3 | description: Set the cursor style. 4 | --- 5 | 6 | 7 | 8 | If `n` is omitted, `n` defaults to `0`. `n` must be an integer between 9 | 0 and 6 (inclusive). The mapping of `n` to cursor style is below: 10 | 11 | | n | style | 12 | | --- | --------------------- | 13 | | 0 | terminal default | 14 | | 1 | blinking block | 15 | | 2 | steady block | 16 | | 3 | blinking underline | 17 | | 4 | steady underline | 18 | | 5 | blinking vertical bar | 19 | | 6 | steady vertical bar | 20 | 21 | For `n = 0`, the terminal default is up to the terminal and is inconsistent 22 | across terminal implementations. The default may also be impacted by terminal 23 | configuration. 24 | -------------------------------------------------------------------------------- /docs/vt/csi/decslrm.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Set Left and Right Margins (DECSLRM) 3 | description: Set the left and right margins. 4 | --- 5 | 6 | 7 | 8 | Sets the left and right margins, otherwise known as the scroll region. 9 | To learn more about scroll regions in general, see 10 | [Set Top and Bottom Margins](/docs/vt/csi/decstbm). 11 | 12 | Parameters `l` and `r` are integer values. If either value is zero the 13 | value will be reset to default values. The default value for `l` is `1` 14 | and the default value of `r` is the number of columns in the screen. 15 | 16 | Values `l` and `r` can be omitted. If either value is omitted, their 17 | default values will be used. Note that it is impossible to omit `l` 18 | and not omit `r`. 19 | 20 | This sequence requires [enable left and right margin (mode 69)](#TODO) 21 | to be set. If mode 69 is not set, this sequence does nothing and left 22 | and right margins will not be set. 23 | 24 | This sequence conflicts with [save cursor (`CSI s`)](#TODO). If 25 | mode 69 is disabled, save cursor will be invoked. If mode 69 is enabled, 26 | the `CSI s` save cursor sequence will be disabled, but save cursor is always 27 | also available as `ESC 7`. 28 | 29 | If left is larger or equal to right, this sequence does nothing. A 30 | scroll region must be at least two columns (`r` must be greater than `l`). 31 | The rest of this sequence description assumes valid values for `l` and `r`. 32 | 33 | This sequence unsets the pending wrap state and moves the cursor to 34 | the top-left of the screen. If [origin mode](#TODO) is set, the cursor is 35 | moved to the top-left of the scroll region. 36 | 37 | To reset the left and right margins, call this sequence with both values set to 38 | "0". This will force the default values for both `l` and `r` which is 39 | the full screen. Unsetting mode 69 will also reset the left and right margins. 40 | 41 | ## Validation 42 | 43 | ### DECSLRM V-1: Full Screen 44 | 45 | ```bash 46 | printf "\033[1;1H" # move to top-left 47 | printf "\033[0J" # clear screen 48 | printf "ABC\n" 49 | printf "DEF\n" 50 | printf "GHI\n" 51 | printf "\033[?69h" # enable left/right margins 52 | printf "\033[s" # scroll region left/right 53 | printf "\033[X" 54 | ``` 55 | 56 | ``` 57 | |cBC_____| 58 | |DEF_____| 59 | |GHI_____| 60 | ``` 61 | 62 | ### DECSLRM V-2: Left Only 63 | 64 | ```bash 65 | printf "\033[1;1H" # move to top-left 66 | printf "\033[0J" # clear screen 67 | printf "ABC\n" 68 | printf "DEF\n" 69 | printf "GHI\n" 70 | printf "\033[?69h" # enable left/right margins 71 | printf "\033[2s" # scroll region left/right 72 | printf "\033[2G" # move cursor to column 2 73 | printf "\033[L" 74 | ``` 75 | 76 | ``` 77 | |Ac______| 78 | |DBC_____| 79 | |GEF_____| 80 | | HI_____| 81 | ``` 82 | 83 | ### DECSLRM V-3: Left And Right 84 | 85 | ```bash 86 | printf "\033[1;1H" # move to top-left 87 | printf "\033[0J" # clear screen 88 | printf "ABC\n" 89 | printf "DEF\n" 90 | printf "GHI\n" 91 | printf "\033[?69h" # enable left/right margins 92 | printf "\033[1;2s" # scroll region left/right 93 | printf "\033[2G" # move cursor to column 2 94 | printf "\033[L" 95 | ``` 96 | 97 | ``` 98 | |_cC_____| 99 | |ABF_____| 100 | |DEI_____| 101 | |GH______| 102 | ``` 103 | 104 | ### DECSLRM V-4: Left Equal to Right 105 | 106 | ```bash 107 | printf "\033[1;1H" # move to top-left 108 | printf "\033[0J" # clear screen 109 | printf "ABC\n" 110 | printf "DEF\n" 111 | printf "GHI\n" 112 | printf "\033[?69h" # enable left/right margins 113 | printf "\033[2;2s" # scroll region left/right 114 | printf "\033[X" 115 | ``` 116 | 117 | ``` 118 | |cBC_____| 119 | |DEF_____| 120 | |GHI_____| 121 | ``` 122 | -------------------------------------------------------------------------------- /docs/vt/csi/decstbm.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Set Top and Bottom Margins (DECSTBM) 3 | description: Set the top and bottom margins, otherwise known as the scroll region. 4 | --- 5 | 6 | 7 | 8 | Parameters `t` and `b` are integer values. If either value is zero the 9 | value will be reset to default values. The default value for `t` is `1` 10 | and the default value of `b` is the number of rows in the screen. 11 | 12 | Values `t` and `b` can be omitted. If either value is omitted, their 13 | default values will be used. Note that it is impossible to omit `t` 14 | and not omit `b`. The only valid sequences are `CSI t ; b r`, 15 | `CSI t r` and `CSI r`. 16 | 17 | If top is larger or equal to bottom, this sequence does nothing. A 18 | scroll region must be at least two rows (`b` must be greater than `t`). 19 | The rest of this sequence description assumes valid values for `t` and `b`. 20 | 21 | This sequence unsets the pending wrap state and moves the cursor to 22 | the top-left of the screen. If [origin mode](#TODO) is set, the cursor is 23 | moved to the top-left of the scroll region. 24 | 25 | To reset the scroll region, call this sequence with both values set to 26 | "0". This will force the default values for both `t` and `b` which is 27 | the full screen. 28 | 29 | The top and bottom margin constitute what is known as the _scroll region_. 30 | The scroll region impacts the operation of many sequences such as 31 | [insert line](/docs/vt/csi/il), [cursor down](/docs/vt/csi/cud), etc. Scroll regions are 32 | an effective and efficient way to constraint terminal modifications to a 33 | rectangular region of the screen. 34 | 35 | ## Validation 36 | 37 | ### DECSTBM V-1: Full Screen 38 | 39 | ```bash 40 | printf "\033[1;1H" # move to top-left 41 | printf "\033[0J" # clear screen 42 | printf "ABC\n" 43 | printf "DEF\n" 44 | printf "GHI\n" 45 | printf "\033[r" # scroll region top/bottom 46 | printf "\033[T" 47 | ``` 48 | 49 | ``` 50 | |c_______| 51 | |ABC_____| 52 | |DEF_____| 53 | |GHI_____| 54 | ``` 55 | 56 | ### DECSTBM V-2: Top Only 57 | 58 | ```bash 59 | printf "\033[1;1H" # move to top-left 60 | printf "\033[0J" # clear screen 61 | printf "ABC\n" 62 | printf "DEF\n" 63 | printf "GHI\n" 64 | printf "\033[2r" # scroll region top/bottom 65 | printf "\033[T" 66 | ``` 67 | 68 | ``` 69 | |ABC_____| 70 | |________| 71 | |DEF_____| 72 | |GHI_____| 73 | ``` 74 | 75 | ### DECSTBM V-3: Top and Bottom 76 | 77 | ```bash 78 | printf "\033[1;1H" # move to top-left 79 | printf "\033[0J" # clear screen 80 | printf "ABC\n" 81 | printf "DEF\n" 82 | printf "GHI\n" 83 | printf "\033[1;2r" # scroll region top/bottom 84 | printf "\033[T" 85 | ``` 86 | 87 | ``` 88 | |________| 89 | |ABC_____| 90 | |GHI_____| 91 | ``` 92 | 93 | ### DECSTBM V-4: Top Equal to Bottom 94 | 95 | ```bash 96 | printf "\033[1;1H" # move to top-left 97 | printf "\033[0J" # clear screen 98 | printf "ABC\n" 99 | printf "DEF\n" 100 | printf "GHI\n" 101 | printf "\033[2;2r" # scroll region top/bottom 102 | printf "\033[T" 103 | ``` 104 | 105 | ``` 106 | |________| 107 | |ABC_____| 108 | |DEF_____| 109 | |GHI_____| 110 | ``` 111 | -------------------------------------------------------------------------------- /docs/vt/csi/dl.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Delete Line (DL) 3 | description: |- 4 | Deletes `n` lines at the current cursor position and shifts existing 5 | lines up. 6 | --- 7 | 8 | 9 | 10 | The parameter `n` must be an integer greater than or equal to 1. If `n` is less than 11 | or equal to 0, adjust `n` to be 1. If `n` is omitted, `n` defaults to 1. 12 | 13 | If the current cursor position is outside of the current scroll region, 14 | this sequence does nothing. The cursor is outside of the current scroll 15 | region if it is above the [top margin](#TODO), below the [bottom margin](#TODO), 16 | left of the [left margin](#TODO), or right of the [right margin](#TODO). 17 | 18 | This sequence unsets the pending wrap state. 19 | 20 | This sequence moves the cursor column to the left margin. 21 | 22 | Remove the top `n` lines of the current scroll region, and shift existing 23 | lines up. The space created at the bottom of the scroll region should be 24 | blank with the background color set according to the current SGR state. 25 | 26 | If a [left margin](#TODO) or [right margin](#TODO) is set, only the cells 27 | within and including the margins are deleted or shifted. 28 | Other existing contents to the left of the left margin or right of the 29 | right margin remains untouched. 30 | 31 | If a multi-cell character would be split, erase the full multi-cell 32 | character. For example, if "橋" is printed to the left of the left margin 33 | and shifting the line down as a result of DL would split the character, 34 | the cell should be erased. 35 | 36 | ## Validation 37 | 38 | ### DL V-1: Simple Delete Line 39 | 40 | ```bash 41 | printf "\033[1;1H" # move to top-left 42 | printf "\033[0J" # clear screen 43 | printf "ABC\n" 44 | printf "DEF\n" 45 | printf "GHI\n" 46 | printf "\033[2;2H" 47 | printf "\033[M" 48 | ``` 49 | 50 | ``` 51 | |ABC_____| 52 | |GHI_____| 53 | ``` 54 | 55 | ### DL V-2: Cursor Outside of Scroll Region 56 | 57 | ```bash 58 | printf "\033[1;1H" # move to top-left 59 | printf "\033[0J" # clear screen 60 | printf "ABC\n" 61 | printf "DEF\n" 62 | printf "GHI\n" 63 | printf "\033[3;4r" # scroll region top/bottom 64 | printf "\033[2;2H" 65 | printf "\033[M" 66 | ``` 67 | 68 | ``` 69 | |ABC_____| 70 | |DEF_____| 71 | |GHI_____| 72 | ``` 73 | 74 | ### DL V-3: Top/Bottom Scroll Regions 75 | 76 | ```bash 77 | printf "\033[1;1H" # move to top-left 78 | printf "\033[0J" # clear screen 79 | printf "ABC\n" 80 | printf "DEF\n" 81 | printf "GHI\n" 82 | printf "123\n" 83 | printf "\033[1;3r" # scroll region top/bottom 84 | printf "\033[2;2H" 85 | printf "\033[M" 86 | ``` 87 | 88 | ``` 89 | |ABC_____| 90 | |GHI_____| 91 | |________| 92 | |123_____| 93 | ``` 94 | 95 | ### DL V-4: Left/Right Scroll Regions 96 | 97 | ```bash 98 | printf "\033[1;1H" # move to top-left 99 | printf "\033[0J" # clear screen 100 | printf "ABC123\n" 101 | printf "DEF456\n" 102 | printf "GHI789\n" 103 | printf "\033[?69h" # enable left/right margins 104 | printf "\033[2;4s" # scroll region left/right 105 | printf "\033[2;2H" 106 | printf "\033[M" 107 | ``` 108 | 109 | ``` 110 | |ABC123__| 111 | |DHI756__| 112 | |G___89__| 113 | ``` 114 | -------------------------------------------------------------------------------- /docs/vt/csi/dsr.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Device Status Report (DSR) 3 | description: Request information from the terminal. 4 | --- 5 | 6 | 7 | 8 | Request information from the terminal depending on the value of `n`. 9 | 10 | The possible valid values of `n` are described in the paragraphs below. If 11 | any other value of `n` is provided, this sequence does nothing. 12 | 13 | If `n = 5`, the _operating status_ is requested. The terminal responds 14 | to the program with `ESC [ 0 n` to indicate no malfunctions. 15 | 16 | If `n = 6`, the _cursor position_ is requested. The terminal responds to 17 | the program in the format `ESC [ y ; x R` where `y` is the row and `x` 18 | is the column, both one-indexed. If [origin mode (DEC Mode 6)](/vt/modes/origin) 19 | is enabled, the reported cursor position is relative to the top-left of the 20 | scroll region. 21 | 22 | ## Validation 23 | 24 | ### DSR V-1: Operating Status 25 | 26 | ```bash 27 | printf "\033[1;1H" # move to top-left 28 | printf "\033[0J" # clear screen 29 | printf "\033[5n" 30 | ``` 31 | 32 | ``` 33 | |^[[0n_____| 34 | ``` 35 | 36 | ### DSR V-2: Cursor Position 37 | 38 | ```bash 39 | printf "\033[1;1H" # move to top-left 40 | printf "\033[0J" # clear screen 41 | printf "\033[2;4H" # move to top-left 42 | printf "\033[6n" 43 | ``` 44 | 45 | ``` 46 | ^[[2;4R 47 | ``` 48 | -------------------------------------------------------------------------------- /docs/vt/csi/ed.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Erase Display (ED) 3 | description: |- 4 | Erase display contents with behavior depending on the command `n`. 5 | --- 6 | 7 | 8 | 9 | If `n` is unset, the value of `n` is 0. The only valid values for `n` are 10 | 0, 1, 2, or 3. If any other value of `n` is given, do not execute this sequence. 11 | The remainder of the sequence documentation assumes a valid value of `n`. 12 | 13 | For all valid values of `n` except 3, this sequence unsets the pending wrap state. 14 | The cursor position will remain unchanged under all circumstances throughout 15 | this sequence. 16 | 17 | If [Select Character Selection Attribute (DECSCA)](#TODO) is enabled 18 | or was the most recently enabled protection mode on the currently active screen, 19 | protected attributes are ignored. Otherwise, protected attributes will be 20 | respected. For more details on this specific logic for protected attribute 21 | handling, see [Erase Character (ECH)](/docs/vt/csi/ech). 22 | 23 | For all operations, if a multi-cell character would be split, erase the full multi-cell 24 | character. For example, if "橋" is printed and the erase would only erase the 25 | first or second cell of the two-cell character, both cells should be erased. 26 | 27 | This sequence does not respect any scroll regions (top, bottom, left, or 28 | right). The boundaries of the operation are the full visible screen. 29 | 30 | If `n` is `0`, perform an **erase display below** operation. Erase all 31 | cells to the right and below the cursor. The background color of erased cells 32 | is colored according to the current SGR state. 33 | 34 | If `n` is `1`, perform an **erase display above** operation. Erase all 35 | cells to the left and above the cursor. The background color of erased cells 36 | is colored according to the current SGR state. 37 | 38 | If `n` is `2`, **erase the entire display**. This is the equivalent of 39 | erase above (`n = 1`) and erase below (`n = 0`) both being executed. 40 | 41 | If `n` is `3`, **erase only the scrollback region**. This does not affect 42 | the visible display of the screen and does not move the cursor. The scrollback 43 | region is the region of the terminal that is currently above the visible 44 | area of the screen when the screen is scrolled completely to the bottom. 45 | 46 | ## Validation 47 | 48 | ### ED V-1: Simple Erase Below 49 | 50 | ```bash 51 | printf "\033[1;1H" # move to top-left 52 | printf "\033[0J" # clear screen 53 | printf "ABC\n" 54 | printf "DEF\n" 55 | printf "GHI\n" 56 | printf "\033[2;2H" 57 | printf "\033[0J" 58 | ``` 59 | 60 | ``` 61 | |ABC_____| 62 | |Dc______| 63 | |________| 64 | ``` 65 | 66 | ### ED V-2: Erase Below SGR State 67 | 68 | ```bash 69 | printf "\033[1;1H" # move to top-left 70 | printf "\033[0J" # clear screen 71 | printf "ABC\n" 72 | printf "DEF\n" 73 | printf "GHI\n" 74 | printf "\033[2;2H" 75 | printf "\033[41m" 76 | printf "\033[0J" 77 | ``` 78 | 79 | ``` 80 | |ABC_____| 81 | |Dc______| 82 | |________| 83 | ``` 84 | 85 | All the cells right and below of the cursor should be colored red. 86 | 87 | ### ED V-3: Erase Below Multi-Cell Character 88 | 89 | ```bash 90 | printf "\033[1;1H" # move to top-left 91 | printf "\033[0J" # clear screen 92 | printf "AB橋C\n" 93 | printf "DE橋F\n" 94 | printf "GH橋I\n" 95 | printf "\033[2;4H" 96 | printf "\033[0J" 97 | ``` 98 | 99 | ``` 100 | |AB橋C___| 101 | |DE_c____| 102 | |________| 103 | ``` 104 | 105 | ### ED V-4: Simple Erase Above 106 | 107 | ```bash 108 | printf "\033[1;1H" # move to top-left 109 | printf "\033[0J" # clear screen 110 | printf "ABC\n" 111 | printf "DEF\n" 112 | printf "GHI\n" 113 | printf "\033[2;2H" 114 | printf "\033[1J" 115 | ``` 116 | 117 | ``` 118 | |________| 119 | |_cF_____| 120 | |GHI_____| 121 | ``` 122 | 123 | ### ED V-5: Simple Erase Complete 124 | 125 | ```bash 126 | printf "\033[1;1H" # move to top-left 127 | printf "\033[0J" # clear screen 128 | printf "ABC\n" 129 | printf "DEF\n" 130 | printf "GHI\n" 131 | printf "\033[2;2H" 132 | printf "\033[2J" 133 | ``` 134 | 135 | ``` 136 | |________| 137 | |_c______| 138 | |________| 139 | ``` 140 | -------------------------------------------------------------------------------- /docs/vt/csi/hpa.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Horizontal Position Absolute (HPA) 3 | description: Move the cursor to a specific column. 4 | --- 5 | 6 | 7 | 8 | This sequence performs [cursor position (CUP)](/docs/vt/csi/cup) with `x` set 9 | to the parameterized value and `y` set to the current cursor position. 10 | There is no additional or different behavior for using `HPA`. 11 | 12 | Because this invokes `CUP`, the cursor row (`x`) can change if it is 13 | outside the bounds of the `CUP` operation. For example, if 14 | [origin mode](#TODO) is set and the current cursor position is outside 15 | of the scroll region, the row will be adjusted. 16 | -------------------------------------------------------------------------------- /docs/vt/csi/hpr.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Horizontal Position Relative (HPR) 3 | description: |- 4 | Move the cursor to a specific column relative to the current position. 5 | --- 6 | 7 | 8 | 9 | This sequence performs [cursor position (CUP)](/docs/vt/csi/cup) with `x` set 10 | to the current cursor column plus `x` and `y` set to the current cursor row. 11 | There is no additional or different behavior for using `HPR`. 12 | 13 | The parameter `x` must be an integer greater than or equal to 1. If `x` is less than 14 | or equal to 0, adjust `x` to be 1. If `x` is omitted, `x` defaults to 1. 15 | 16 | Because this invokes `CUP`, the cursor row (`y`) can change if it is 17 | outside the bounds of the `CUP` operation. For example, if 18 | [origin mode](#TODO) is set and the current cursor position is outside 19 | of the scroll region, the row will be adjusted. 20 | -------------------------------------------------------------------------------- /docs/vt/csi/ich.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Insert Character (ICH) 3 | description: Insert blank characters at the current cursor position. 4 | --- 5 | 6 | 7 | 8 | Insert `n` blank characters at the current cursor position and shift 9 | existing cell contents right. 10 | 11 | The parameter `n` must be an integer greater than or equal to 1. If `n` is less than 12 | or equal to 0, adjust `n` to be 1. If `n` is omitted, `n` defaults to 1. 13 | 14 | This sequence always unsets the pending wrap state. 15 | 16 | If the cursor position is outside of the [left and right margins](#TODO), 17 | this sequence does not change the screen, but the pending wrap state is 18 | still reset. 19 | 20 | Existing cells shifted beyond the right margin are deleted. Inserted cells 21 | are blank with the background color colored according to the current SGR state. 22 | 23 | If a multi-cell character (such as "橋") is shifted so that the cell is split 24 | in half, the multi-cell character can either be clipped or erased. Typical 25 | behavior is to clip at the right edge of the screen and erase at a right 26 | margin, but either behavior is acceptable. 27 | 28 | ## Validation 29 | 30 | ### ICH V-1: No Scroll Region, Fits on Screen 31 | 32 | ```bash 33 | printf "ABC" 34 | printf "\033[1G" 35 | printf "\033[2@" 36 | printf "X" 37 | ``` 38 | 39 | ``` 40 | |XcABC_____| 41 | ``` 42 | 43 | ### ICH V-2: SGR State 44 | 45 | ```bash 46 | printf "ABC" 47 | printf "\033[1G" 48 | printf "\033[41m" 49 | printf "\033[2@" 50 | printf "X" 51 | ``` 52 | 53 | ``` 54 | |c_ABC_____| 55 | ``` 56 | 57 | The `c_` cells should both have a red background. The `ABC` cells should 58 | remain unchanged in style. 59 | 60 | ### ICH V-3: Shifting Content Off the Screen 61 | 62 | ```bash 63 | cols=$(tput cols) 64 | printf "\033[${cols}G" 65 | printf "\033[2D" 66 | printf "ABC" 67 | printf "\033[2D" 68 | printf "\033[2@" 69 | printf "X" 70 | ``` 71 | 72 | ``` 73 | |_______XcA| 74 | ``` 75 | 76 | ### ICH V-4: Inside Left/Right Scroll Region 77 | 78 | ```bash 79 | printf "\033[1;1H" # move to top-left 80 | printf "\033[0J" # clear screen 81 | printf "\033[?69h" # enable left/right margins 82 | printf "\033[3;5s" # scroll region left/right 83 | printf "\033[3G" 84 | printf "ABC" 85 | printf "\033[3G" 86 | printf "\033[2@" 87 | printf "X" 88 | ``` 89 | 90 | ``` 91 | |__XcA_____| 92 | ``` 93 | 94 | ### ICH V-5: Outside Left/Right Scroll Region 95 | 96 | ```bash 97 | printf "\033[1;1H" # move to top-left 98 | printf "\033[0J" # clear screen 99 | printf "\033[?69h" # enable left/right margins 100 | printf "\033[3;5s" # scroll region left/right 101 | printf "\033[3G" 102 | printf "ABC" 103 | printf "\033[1G" 104 | printf "\033[2@" 105 | printf "X" 106 | ``` 107 | 108 | ``` 109 | |XcABC_____| 110 | ``` 111 | 112 | ### ICH V-6: Split Wide Character 113 | 114 | ```bash 115 | cols=$(tput cols) 116 | printf "\033[${cols}G" 117 | printf "\033[1D" 118 | printf "橋" 119 | printf "\033[2D" 120 | printf "\033[@" 121 | printf "X" 122 | ``` 123 | 124 | ``` 125 | |_______Xc_| 126 | ``` 127 | 128 | In this case, it is valid for the last cell to be blank or to clip the 129 | multi-cell character. xterm clips the character but many other terminals 130 | erase the cell. 131 | -------------------------------------------------------------------------------- /docs/vt/csi/il.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Insert Line (IL) 3 | description: Insert blank lines at the current cursor position. 4 | --- 5 | 6 | 7 | 8 | Inserts `n` lines at the current cursor position and shifts existing 9 | lines down. 10 | 11 | The parameter `n` must be an integer greater than or equal to 1. If `n` is less than 12 | or equal to 0, adjust `n` to be 1. If `n` is omitted, `n` defaults to 1. 13 | 14 | If the current cursor position is outside of the current scroll region, 15 | this sequence does nothing. The cursor is outside of the current scroll 16 | region if it is above the [top margin](#TODO), below the [bottom margin](#TODO), 17 | left of the [left margin](#TODO), or right of the [right margin](#TODO). 18 | 19 | This sequence unsets the pending wrap state. 20 | 21 | This sequence moves the cursor column to the left margin. 22 | 23 | From the current cursor row down `n` lines, insert blank lines colored 24 | with a background color according to the current SGR state. When a line is 25 | inserted, shift all existing content down one line. The bottommost row 26 | is the bottom margin. If content is shifted beyond the bottom margin, 27 | it is lost and the existing content beyond the bottom margin is preserved 28 | and not shifted. 29 | 30 | If a [left margin](#TODO) or [right margin](#TODO) is set, only the cells 31 | within and including the margins are blanked (when inserted) or shifted. 32 | Other existing contents to the left of the left margin or right of the 33 | right margin remains untouched. 34 | 35 | If a multi-cell character would be split, erase the full multi-cell 36 | character. For example, if "橋" is printed to the left of the left margin 37 | and shifting the line down as a result of IL would split the character, 38 | the cell should be erased. 39 | 40 | ## Validation 41 | 42 | ### IL V-1: Simple Insert Line 43 | 44 | ```bash 45 | printf "\033[1;1H" # move to top-left 46 | printf "\033[0J" # clear screen 47 | printf "ABC\n" 48 | printf "DEF\n" 49 | printf "GHI\n" 50 | printf "\033[2;2H" 51 | printf "\033[L" 52 | ``` 53 | 54 | ``` 55 | |ABC_____| 56 | |c_______| 57 | |DEF_____| 58 | |GHI_____| 59 | ``` 60 | 61 | ### IL V-2: Cursor Outside of Scroll Region 62 | 63 | ```bash 64 | printf "\033[1;1H" # move to top-left 65 | printf "\033[0J" # clear screen 66 | printf "ABC\n" 67 | printf "DEF\n" 68 | printf "GHI\n" 69 | printf "\033[3;4r" # scroll region top/bottom 70 | printf "\033[2;2H" 71 | printf "\033[L" 72 | ``` 73 | 74 | ``` 75 | |ABC_____| 76 | |DEF_____| 77 | |GHI_____| 78 | ``` 79 | 80 | ### IL V-3: Top/Bottom Scroll Regions 81 | 82 | ```bash 83 | printf "\033[1;1H" # move to top-left 84 | printf "\033[0J" # clear screen 85 | printf "ABC\n" 86 | printf "DEF\n" 87 | printf "GHI\n" 88 | printf "123\n" 89 | printf "\033[1;3r" # scroll region top/bottom 90 | printf "\033[2;2H" 91 | printf "\033[L" 92 | ``` 93 | 94 | ``` 95 | |ABC_____| 96 | |c_______| 97 | |DEF_____| 98 | |123_____| 99 | ``` 100 | 101 | ### IL V-4: Left/Right Scroll Regions 102 | 103 | ```bash 104 | printf "\033[1;1H" # move to top-left 105 | printf "\033[0J" # clear screen 106 | printf "ABC123\n" 107 | printf "DEF456\n" 108 | printf "GHI789\n" 109 | printf "\033[?69h" # enable left/right margins 110 | printf "\033[2;4s" # scroll region left/right 111 | printf "\033[2;2H" 112 | printf "\033[L" 113 | ``` 114 | 115 | ``` 116 | |ABC123__| 117 | |Dc__56__| 118 | |GEF489__| 119 | |_HI7____| 120 | ``` 121 | -------------------------------------------------------------------------------- /docs/vt/csi/rep.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Repeat Previous Character (REP) 3 | description: Repeat the previously printed character `n` times. 4 | --- 5 | 6 | 7 | 8 | The parameter `n` must be an integer greater than or equal to 1. If `n` is less than 9 | or equal to 0, adjust `n` to be 1. If `n` is omitted, `n` defaults to 1. 10 | 11 | In xterm, only characters with single byte (less than decimal 256) are 12 | supported. In most other mainstream terminals, any character is supported. 13 | 14 | Each repeated character behaves identically to if it was manually typed in. 15 | Therefore, soft-wrapping, margins, etc. all behave the same as if the 16 | character was typed. 17 | 18 | The previously printed character is any character that is printed through 19 | any means. The previously printed character is not limited to characters 20 | a user manually types. If there is no previously typed character, this sequence 21 | does nothing. 22 | 23 | ## Validation 24 | 25 | ### REP V-1: Simple Usage 26 | 27 | ```bash 28 | printf "\033[1;1H" # move to top-left 29 | printf "\033[0J" # clear screen 30 | printf "A" 31 | printf "\033[b" 32 | ``` 33 | 34 | ``` 35 | |AAc_______| 36 | ``` 37 | 38 | ### REP V-2: Soft-Wrap 39 | 40 | ```bash 41 | cols=$(tput cols) 42 | printf "\033[1;1H" # move to top-left 43 | printf "\033[0J" # clear screen 44 | printf "\033[${cols}G" 45 | printf "A" 46 | printf "\033[b" 47 | ``` 48 | 49 | ``` 50 | |_________A| 51 | |Ac________| 52 | ``` 53 | -------------------------------------------------------------------------------- /docs/vt/csi/sd.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Scroll Down (SD) 3 | description: |- 4 | Insert `n` lines at the top of the scroll region and 5 | shift existing lines down. 6 | --- 7 | 8 | 9 | 10 | This sequence is functionally identical to 11 | [Insert Line (IL)](/docs/vt/csi/il) with the cursor position set to the top of 12 | the scroll region. The cursor position after the operation must be unchanged 13 | from when SD was invoked. 14 | 15 | This sequence unsets the pending wrap state. 16 | 17 | ## Validation 18 | 19 | ### SD V-1: Outside of Top/Bottom Scroll Region 20 | 21 | ```bash 22 | printf "\033[1;1H" # move to top-left 23 | printf "\033[0J" # clear screen 24 | printf "ABC\n" 25 | printf "DEF\n" 26 | printf "GHI\n" 27 | printf "\033[3;4r" # scroll region top/bottom 28 | printf "\033[2;2H" 29 | printf "\033[T" 30 | ``` 31 | 32 | ``` 33 | |ABC_____| 34 | |DEF_____| 35 | |________| 36 | |GHI_____| 37 | ``` 38 | -------------------------------------------------------------------------------- /docs/vt/csi/su.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Scroll Up (SU) 3 | description: |- 4 | Remove `n` lines from the top of the scroll region and shift existing 5 | lines up. 6 | --- 7 | 8 | 9 | 10 | The parameter `n` must be an integer greater than or equal to 1. If `n` is less than 11 | or equal to 0, adjust `n` to be 1. If `n` is omitted, `n` defaults to 1. 12 | 13 | This sequence executes [Delete Line (DL)](/docs/vt/csi/dl) with the cursor position 14 | set to the top of the scroll region. There are some differences from DL 15 | which are explained below. 16 | 17 | The cursor position after the operation must be unchanged from when SU was 18 | invoked. The pending wrap state is _not_ reset. 19 | 20 | ## Validation 21 | 22 | ### SU V-1: Simple Usage 23 | 24 | ```bash 25 | printf "\033[1;1H" # move to top-left 26 | printf "\033[0J" # clear screen 27 | printf "ABC\n" 28 | printf "DEF\n" 29 | printf "GHI\n" 30 | printf "\033[2;2H" 31 | printf "\033[S" 32 | ``` 33 | 34 | ``` 35 | |DEF_____| 36 | |GHI_____| 37 | ``` 38 | 39 | ### SU V-2: Top/Bottom Scroll Region 40 | 41 | ```bash 42 | printf "\033[1;1H" # move to top-left 43 | printf "\033[0J" # clear screen 44 | printf "ABC\n" 45 | printf "DEF\n" 46 | printf "GHI\n" 47 | printf "\033[2;3r" # scroll region top/bottom 48 | printf "\033[1;1H" 49 | printf "\033[S" 50 | ``` 51 | 52 | ``` 53 | |ABC_____| 54 | |GHI_____| 55 | ``` 56 | 57 | ### SU V-3: Left/Right Scroll Regions 58 | 59 | ```bash 60 | printf "\033[1;1H" # move to top-left 61 | printf "\033[0J" # clear screen 62 | printf "ABC123\n" 63 | printf "DEF456\n" 64 | printf "GHI789\n" 65 | printf "\033[?69h" # enable left/right margins 66 | printf "\033[2;4s" # scroll region left/right 67 | printf "\033[2;2H" 68 | printf "\033[S" 69 | ``` 70 | 71 | ``` 72 | |AEF423__| 73 | |DHI756__| 74 | |G___89__| 75 | ``` 76 | 77 | ### SU V-4: Preserves Pending Wrap 78 | 79 | ```bash 80 | cols=$(tput cols) 81 | printf "\033[1;${cols}H" # move to top-right 82 | printf "\033[2J" # clear screen 83 | printf "A" 84 | printf "\033[2;${cols}H" 85 | printf "B" 86 | printf "\033[3;${cols}H" 87 | printf "C" 88 | printf "\033[S" 89 | printf "X" 90 | ``` 91 | 92 | ``` 93 | |_______B| 94 | |_______C| 95 | |________| 96 | |X_______| 97 | ``` 98 | 99 | ### SU V-5: Scroll Full Top/Bottom Scroll Region 100 | 101 | ```bash 102 | printf "\033[1;1H" # move to top-left 103 | printf "\033[0J" # clear screen 104 | printf "top" 105 | printf "\033[5;1H" 106 | printf "ABCDEF" 107 | printf "\033[2;5r" # scroll region top/bottom 108 | printf "\033[4S" 109 | ``` 110 | 111 | ``` 112 | |top_____| 113 | ``` 114 | -------------------------------------------------------------------------------- /docs/vt/csi/tbc.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Tab Clear (TBC) 3 | description: Clear one or all tab stops. 4 | --- 5 | 6 | 7 | 8 | The parameter `n` must be `0` or `3`. If `n` is omitted, `n` defaults to `0`. 9 | 10 | If the parameter `n` is `0`, the cursor column position is marked as 11 | not a tab stop. If the column was already not a tab stop, this does nothing. 12 | 13 | If the parameter `n` is `3`, all tab stops are cleared. 14 | 15 | ## Validation 16 | 17 | ### TBC V-1: Tab Clear Single 18 | 19 | ```bash 20 | printf "\033[1;1H" # move to top-left 21 | printf "\033[0J" # clear screen 22 | printf "\033[?5W" # reset tabs 23 | printf "\t" 24 | printf "\033[g" 25 | printf "\033[1G" 26 | printf "\t" 27 | ``` 28 | 29 | ``` 30 | |_______________c_______| 31 | ``` 32 | 33 | ### TBC V-3: Clear All Tabstops 34 | 35 | ```bash 36 | printf "\033[1;1H" # move to top-left 37 | printf "\033[0J" # clear screen 38 | printf "\033[?5W" # reset tabs 39 | printf "\033[3g" 40 | printf "\033[1G" 41 | printf "\t" 42 | ``` 43 | 44 | ``` 45 | |______________________c| 46 | ``` 47 | -------------------------------------------------------------------------------- /docs/vt/csi/vpa.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Vertical Position Absolute (VPA) 3 | description: Move the cursor to a specific row. 4 | --- 5 | 6 | 7 | 8 | This sequence performs [cursor position (CUP)](/docs/vt/csi/cup) with `y` set 9 | to the parameterized value and `x` set to the current cursor position. 10 | There is no additional or different behavior for using `VPA`. 11 | 12 | Because this invokes `CUP`, the cursor column (`y`) can change if it is 13 | outside the bounds of the `CUP` operation. For example, if 14 | [origin mode](#TODO) is set and the current cursor position is outside 15 | of the scroll region, the column will be adjusted. 16 | -------------------------------------------------------------------------------- /docs/vt/csi/vpr.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Vertical Position Relative (VPR) 3 | description: |- 4 | Move the cursor to a specific row relative to the current position. 5 | --- 6 | 7 | 8 | 9 | This sequence performs [cursor position (CUP)](/docs/vt/csi/cup) with `y` set 10 | to the current cursor row plus `y` and `x` set to the current cursor column. 11 | There is no additional or different behavior for using `VPR`. 12 | 13 | The parameter `y` must be an integer greater than or equal to 1. If `y` is less than 14 | or equal to 0, adjust `y` to be 1. If `y` is omitted, `y` defaults to 1. 15 | 16 | Because this invokes `CUP`, the cursor column (`x`) can change if it is 17 | outside the bounds of the `CUP` operation. For example, if 18 | [origin mode](#TODO) is set and the current cursor position is outside 19 | of the scroll region, the column will be adjusted. 20 | -------------------------------------------------------------------------------- /docs/vt/csi/xtshiftescape.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Set Shift-Escape (XTSHIFTESCAPE) 3 | description: |- 4 | Configure whether mouse reports are allowed to capture 5 | the `shift` modifier. 6 | --- 7 | 8 | ", "Pn", "s"]} /> 9 | 10 | The parameter `n` must be an integer equal to 0 or 1. If `n` is omitted, 11 | `n` defaults to 0. If `n` is an invalid value, this sequence does nothing. 12 | 13 | When a terminal program requests [mouse reporting](#TODO), some mouse 14 | reporting modes also report the modifier keys that are pressed (control, shift, 15 | etc.). This would disable the ability for a terminal user to natively select 16 | text if they typically select text using left-click and drag, since the 17 | left-click event is captured by the running program. 18 | 19 | To get around this limitation, many terminal emulators (including xterm) 20 | use the `shift` modifier to disable mouse reporting temporarily, allowing 21 | native text selection to work. In this scenario, however, the running 22 | terminal program cannot detect shift-clicks because the terminal emulator 23 | captures the event. 24 | 25 | This sequence (`XTSHIFTESCAPE`) allows configuring this behavior. If 26 | `n` is `0`, the terminal is allowed to override the shift key and not pass 27 | it through to the terminal program. If `n` is `1`, the terminal program 28 | is requesting that the shift modifier is sent using standard mouse 29 | reporting formats. 30 | 31 | In either case, the terminal emulator is not forced to respect this request. 32 | For example, `xterm` has a `never` and `always` terminal configuration 33 | to never allow terminal programs to capture shift or to always allow them, 34 | respectively. If either of these configurations are set, `XTSHIFTESCAPE` 35 | has zero effect. 36 | 37 | `xterm` also has `false` and `true` terminal configurations. In the `false` 38 | scenario, the terminal emulator will override `shift` (not allow the terminal 39 | program to see it) _unless it is explicitly requested_ via `XTSHIFTESCAPE`. 40 | The `true` scenario is the exact opposite: pass the shift modifier through 41 | to the running terminal program unless the terminal program explicitly states 42 | it doesn't need to know about it (`n = 0`). 43 | -------------------------------------------------------------------------------- /docs/vt/esc/decaln.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Screen Alignment Test (DECALN) 3 | description: |- 4 | Reset margins, move cursor to the top left, and fill the screen with `E`. 5 | --- 6 | 7 | 8 | 9 | Reset the top, bottom, left, and right margins and unset [origin mode](#TODO). 10 | The cursor is moved to the top-left corner of the screen. 11 | 12 | All stylistic SGR attributes are unset, such as bold, blink, etc. 13 | SGR foreground and background colors are preserved. 14 | The [protected attribute](#TODO) is not unset. 15 | 16 | The entire screen is filled with the character `E`. The letter `E` ignores 17 | the current SGR settings and is written with no styling. 18 | 19 | ## Validation 20 | 21 | ### DECALN V-1: Simple Usage 22 | 23 | ```bash 24 | printf "\033#8" 25 | ``` 26 | 27 | ``` 28 | |EEEEEEEE| 29 | |EEEEEEEE| 30 | |EEEEEEEE| 31 | ``` 32 | 33 | ### DECALN V-2: Reset Margins 34 | 35 | ```bash 36 | printf "\033[2;3r" # scroll region top/bottom 37 | printf "\033#8" 38 | printf "\033[T" 39 | ``` 40 | 41 | ``` 42 | |c_______| 43 | |EEEEEEEE| 44 | |EEEEEEEE| 45 | ``` 46 | -------------------------------------------------------------------------------- /docs/vt/esc/deckpam.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Keypad Application Mode (DECKPAM) 3 | description: Set the numeric keypad to application mode. 4 | --- 5 | 6 | "]} /> 7 | 8 | Set the numeric keypad to numeric mode. 9 | -------------------------------------------------------------------------------- /docs/vt/esc/decrc.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Restore Cursor (DECRC) 3 | description: Restore the cursor-related state saved via Save Cursor (DECSC). 4 | --- 5 | 6 | 7 | 8 | If a cursor was never previously saved, this sets all the typically saved 9 | values to their default values. 10 | 11 | ## Validation 12 | 13 | Validation is shared with [Save Cursor (DECSC)](/docs/vt/esc/decsc). 14 | -------------------------------------------------------------------------------- /docs/vt/esc/decsc.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Save Cursor (DECSC) 3 | description: |- 4 | Save various cursor-related state that can be restored with 5 | Restore Cursor (DECRC). 6 | --- 7 | 8 | 9 | 10 | The following attributes are saved: 11 | 12 | - Cursor row and column in absolute screen coordinates 13 | - Character sets 14 | - Pending wrap state 15 | - SGR attributes 16 | - [Origin mode (DEC Mode 6)](/vt/modes/origin) 17 | 18 | Only one cursor can be saved at any time. If save cursor is repeated, the 19 | previously save cursor is overwritten. 20 | 21 | Primary and alternate screens have separate saved cursor state. A cursor 22 | saved on the primary screen is inaccessible from the alternate screen 23 | and vice versa. 24 | 25 | ## Validation 26 | 27 | ### SC V-1: Cursor Position 28 | 29 | ```bash 30 | printf "\033[1;1H" # move to top-left 31 | printf "\033[0J" # clear screen 32 | printf "\033[1;5H" 33 | printf "A" 34 | printf "\0337" # Save Cursor 35 | printf "\033[1;1H" 36 | printf "B" 37 | printf "\0338" # Restore Cursor 38 | printf "X" 39 | ``` 40 | 41 | ``` 42 | |B___AX____| 43 | ``` 44 | 45 | ### SC V-2: Pending Wrap State 46 | 47 | ```bash 48 | cols=$(tput cols) 49 | printf "\033[1;1H" # move to top-left 50 | printf "\033[0J" # clear screen 51 | printf "\033[${cols}G" 52 | printf "A" 53 | printf "\0337" # Save Cursor 54 | printf "\033[1;1H" 55 | printf "B" 56 | printf "\0338" # Restore Cursor 57 | printf "X" 58 | ``` 59 | 60 | ``` 61 | |B________A| 62 | |X_________| 63 | ``` 64 | 65 | ### SC V-3: SGR Attributes 66 | 67 | ```bash 68 | printf "\033[1;1H" # move to top-left 69 | printf "\033[0J" # clear screen 70 | printf "\033[1;4;33;44m" 71 | printf "A" 72 | printf "\0337" # Save Cursor 73 | printf "\033[0m" 74 | printf "B" 75 | printf "\0338" # Restore Cursor 76 | printf "X" 77 | ``` 78 | 79 | ``` 80 | |AX________| 81 | ``` 82 | 83 | The "A" and "X" should have identical styling. 84 | -------------------------------------------------------------------------------- /docs/vt/esc/ind.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Index (IND) 3 | description: Move the cursor down one cell, scrolling if necessary. 4 | --- 5 | 6 | 7 | 8 | This sequence always unsets the pending wrap state. 9 | 10 | If the cursor is exactly on the bottom margin and is at or within the 11 | [left](#TODO) and [right margin](#TODO), [scroll up](#TODO) one line. 12 | If the scroll region is the full terminal screen and the terminal is on 13 | the [primary screen](#TODO), this may create scrollback. See the 14 | [scroll](#TODO) documentation for more details. 15 | 16 | If the cursor is outside of the scroll region or not on the bottom 17 | margin of the scroll region, perform the [cursor down](/docs/vt/csi/cud) operation with 18 | `n = 1`. 19 | 20 | This sequence will only scroll when the cursor is exactly on the bottom 21 | margin and within the remaining scroll region. If the cursor is outside 22 | the scroll region and on the bottom line of the terminal, the cursor 23 | does not move. 24 | 25 | ## Validation 26 | 27 | ### IND V-1: No Scroll Region, Top of Screen 28 | 29 | ```bash 30 | printf "\033[1;1H" # move to top-left 31 | printf "\033[0J" # clear screen 32 | printf "A" 33 | printf "\033D" # index 34 | printf "X" 35 | ``` 36 | 37 | ``` 38 | |A_________| 39 | |_Xc_______| 40 | ``` 41 | 42 | ### IND V-2: Bottom of Primary Screen 43 | 44 | ```bash 45 | lines=$(tput lines) 46 | printf "\033[1;1H" # move to top-left 47 | printf "\033[0J" # clear screen 48 | printf "\033[${lines};1H" # move to bottom-left 49 | printf "A" 50 | printf "\033D" # index 51 | printf "X" 52 | ``` 53 | 54 | ``` 55 | |A_________| 56 | |_Xc_______| 57 | ``` 58 | 59 | ### IND V-3: Inside Scroll Region 60 | 61 | ```bash 62 | printf "\033[1;1H" # move to top-left 63 | printf "\033[0J" # clear screen 64 | printf "\033[1;3r" # scroll region 65 | printf "A" 66 | printf "\033D" # index 67 | printf "X" 68 | ``` 69 | 70 | ``` 71 | |A_________| 72 | |_Xc_______| 73 | ``` 74 | 75 | ### IND V-4: Bottom of Scroll Region 76 | 77 | ```bash 78 | printf "\033[1;1H" # move to top-left 79 | printf "\033[0J" # clear screen 80 | printf "\033[1;3r" # scroll region 81 | printf "\033[4;1H" # below scroll region 82 | printf "B" 83 | printf "\033[3;1H" # move to last row of region 84 | printf "A" 85 | printf "\033D" # index 86 | printf "X" 87 | ``` 88 | 89 | ``` 90 | |__________| 91 | |A_________| 92 | |_Xc_______| 93 | |B_________| 94 | ``` 95 | 96 | ### IND V-5: Bottom of Primary Screen with Scroll Region 97 | 98 | ```bash 99 | lines=$(tput lines) 100 | printf "\033[1;1H" # move to top-left 101 | printf "\033[0J" # clear screen 102 | printf "\033[1;3r" # scroll region 103 | printf "\033[3;1H" # move to last row of region 104 | printf "A" 105 | printf "\033[${lines};1H" # move to bottom-left 106 | printf "\033D" # index 107 | printf "X" 108 | ``` 109 | 110 | ``` 111 | |__________| 112 | |__________| 113 | |A_________| 114 | |__________| 115 | |Xc________| 116 | ``` 117 | 118 | ### IND V-6: Outside of Left/Right Scroll Region 119 | 120 | ```bash 121 | printf "\033[1;1H" # move to top-left 122 | printf "\033[0J" # clear screen 123 | printf "\033[?69h" # enable left/right margins 124 | printf "\033[1;3r" # scroll region top/bottom 125 | printf "\033[3;5s" # scroll region left/right 126 | printf "\033[3;3H" 127 | printf "A" 128 | printf "\033[3;1H" 129 | printf "\033D" # index 130 | printf "X" 131 | ``` 132 | 133 | ``` 134 | |__________| 135 | |__________| 136 | |XcA_______| 137 | ``` 138 | 139 | ### IND V-7: Inside of Left/Right Scroll Region 140 | 141 | ```bash 142 | printf "\033[1;1H" # move to top-left 143 | printf "\033[0J" # clear screen 144 | printf "AAAAAA\n" 145 | printf "AAAAAA\n" 146 | printf "AAAAAA" 147 | printf "\033[?69h" # enable left/right margins 148 | printf "\033[1;3s" # set scroll region left/right 149 | printf "\033[1;3r" # set scroll region top/bottom 150 | printf "\033[3;1H" # Move to bottom left 151 | printf "\033D" # index 152 | ``` 153 | 154 | ``` 155 | |AAAAAA____| 156 | |AAAAAA____| 157 | |c__AAA____| 158 | ``` 159 | -------------------------------------------------------------------------------- /docs/vt/esc/ri.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Reverse Index (RI) 3 | description: Move the cursor up one cell, scrolling if necessary. 4 | --- 5 | 6 | 7 | 8 | This sequence does not unset the pending wrap state. 9 | 10 | If the cursor is exactly on the [top margin](/docs/vt/csi/decstbm) and is within 11 | [left and right margins](/docs/vt/csi/decslrm), invoke [scroll down (SD)](/docs/vt/csi/sd) 12 | with `n = 1`. The operation is complete. 13 | 14 | Otherwise, scrolling isn't necessary. Perform a 15 | [cursor up](/docs/vt/csi/cuu) operation with `n = 1`. 16 | 17 | ## Validation 18 | 19 | ### RI V-1: No Scroll Region, Top of Screen 20 | 21 | ```bash 22 | printf "\033[1;1H" # move to top-left 23 | printf "\033[0J" # clear screen 24 | printf "A\n" 25 | printf "B\n" 26 | printf "C\n" 27 | printf "\033[1;1H" # move to top-left 28 | printf "\033M" # reverse index 29 | printf "X" 30 | ``` 31 | 32 | ``` 33 | |Xc________| 34 | |A_________| 35 | |B_________| 36 | |C_________| 37 | ``` 38 | 39 | ### RI V-2: No Scroll Region, Not Top of Screen 40 | 41 | ```bash 42 | printf "\033[1;1H" # move to top-left 43 | printf "\033[0J" # clear screen 44 | printf "A\n" 45 | printf "B\n" 46 | printf "C\n" 47 | printf "\033[2;1H" 48 | printf "\033M" # reverse index 49 | printf "X" 50 | ``` 51 | 52 | ``` 53 | |Xc________| 54 | |B_________| 55 | |C_________| 56 | ``` 57 | 58 | ### RI V-3: Top/Bottom Scroll Region 59 | 60 | ```bash 61 | printf "\033[1;1H" # move to top-left 62 | printf "\033[0J" # clear screen 63 | printf "A\n" 64 | printf "B\n" 65 | printf "C\n" 66 | printf "\033[2;3r" # scroll region 67 | printf "\033[2;1H" 68 | printf "\033M" # reverse index 69 | ``` 70 | 71 | ``` 72 | |A_________| 73 | |c_________| 74 | |B_________| 75 | ``` 76 | 77 | ### RI V-4: Outside of Top/Bottom Scroll Region 78 | 79 | ```bash 80 | printf "\033[1;1H" # move to top-left 81 | printf "\033[0J" # clear screen 82 | printf "A\n" 83 | printf "B\n" 84 | printf "C\n" 85 | printf "\033[2;3r" # scroll region 86 | printf "\033[1;1H" 87 | printf "\033M" # reverse index 88 | ``` 89 | 90 | ``` 91 | |A_________| 92 | |B_________| 93 | |C_________| 94 | ``` 95 | 96 | ### RI V-5: Left/Right Scroll Region 97 | 98 | ```bash 99 | printf "\033[1;1H" # move to top-left 100 | printf "\033[0J" # clear screen 101 | printf "ABC\n" 102 | printf "DEF\n" 103 | printf "GHI\n" 104 | printf "\033[?69h" # enable left/right margins 105 | printf "\033[2;3s" # scroll region left/right 106 | printf "\033[1;2H" 107 | printf "\033M" 108 | ``` 109 | 110 | ``` 111 | |A_________| 112 | |DBC_______| 113 | |GEF_______| 114 | |_HI_______| 115 | ``` 116 | 117 | ### RI V-6: Outside Left/Right Scroll Region 118 | 119 | ```bash 120 | printf "\033[1;1H" # move to top-left 121 | printf "\033[0J" # clear screen 122 | printf "ABC\n" 123 | printf "DEF\n" 124 | printf "GHI\n" 125 | printf "\033[?69h" # enable left/right margins 126 | printf "\033[2;3s" # scroll region left/right 127 | printf "\033[2;1H" 128 | printf "\033M" 129 | ``` 130 | 131 | ``` 132 | |ABC_______| 133 | |DEF_______| 134 | |GHI_______| 135 | ``` 136 | 137 | Cursor on the `A`. 138 | -------------------------------------------------------------------------------- /docs/vt/esc/ris.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Full Reset (RIS) 3 | description: Reset the terminal to its initial state. 4 | --- 5 | 6 | 7 | 8 | The full reset operation does the following: 9 | 10 | - Set the cursor shape to the default 11 | - Reset the scroll region to the full screen 12 | - Disable [left and right margin mode (mode 69)](#TODO) 13 | - Disable [origin mode (mode 6)](#TODO) 14 | - Unset cursor foreground and background colors 15 | - Reset charsets to the default 16 | - Reset [cursor key mode (DECCKM)](#TODO) 17 | - Reset [disable keyboard input (KAM)](#TODO) 18 | - Reset [application keypad mode](/docs/vt/esc/deckpnm) 19 | - Reset xterm keyboard modifier state to the default 20 | - Disable cursor [protected attribute](#TODO) 21 | - Disable any [protected area](#TODO) 22 | - Reset all [mouse tracking modes](#TODO) 23 | - Reset tabstops to default 24 | - Enable [send-receive mode (mode 12)](#TODO) 25 | - Reset [backspace sends delete (mode 67)](#TODO) 26 | - Return to the primary screen and clear it 27 | - Move the cursor to the top-left corner 28 | - Reset the pending wrap state 29 | - Reset saved cursor state 30 | -------------------------------------------------------------------------------- /docs/vt/index.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Terminal API (VT) 3 | description: |- 4 | APIs for developers writing applications that run in Ghostty. 5 | --- 6 | 7 | Programs running in terminals use **control sequences** to interact with the 8 | terminal. Control sequences are a series of special characters that the terminal 9 | interprets as commands. This is sometimes referred as **VT sequences** or 10 | **escape codes**. 11 | 12 | For a full list of supported sequences, see the 13 | [reference](/docs/vt/reference). For a conceptual overview 14 | of control sequences, see the [concepts page](/docs/vt/concepts/sequences). 15 | The current page will give an overview of Ghostty's control 16 | sequence support. 17 | 18 | 19 | -------------------------------------------------------------------------------- /docs/vt/reference.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: VT Sequence Reference 3 | description: A reference of all VT sequences supported by Ghostty. 4 | --- 5 | 6 | This page lists many of the VT sequences that Ghostty supports. 7 | 8 | This page is a work-in-progress. Ghostty supports many more sequences 9 | than are listed here and for the sequences listed here the quality 10 | of the linked documentation varies. This is a very contributor 11 | friendly area to help improve the documentation! 12 | 13 | They are currently grouped by sequence type (control, esc, CSI, etc.) 14 | and the listed alphabetically by syntax. In the future, we will 15 | introduce better organization and search capabilities. 16 | 17 | | Name | Syntax | Description | 18 | |------|------|-------------| 19 | | [BEL](/docs/vt/control/bel) | `0x07` | Alert the user (beep) | 20 | | [BS](/docs/vt/control/bs) | `0x08` | Move cursor backward one position | 21 | | [TAB](/docs/vt/control/tab) | `0x09` | Move cursor right to the next tab stop | 22 | | [LF](/docs/vt/control/lf) | `0x0A` | Move cursor down one line, scrolling if necessary | 23 | | [CR](/docs/vt/control/cr) | `0x0D` | Move cursor to the left margin | 24 | | [DECSC](/docs/vt/esc/decsc) | `ESC 7` | Save cursor | 25 | | [DECRC](/docs/vt/esc/decrc) | `ESC 8` | Restore cursor | 26 | | [IND](/docs/vt/esc/ind) | `ESC D` | Move cursor down, scrolling if necessary | 27 | | [RI](/docs/vt/esc/ri) | `ESC M` | Move cursor up, scrolling if necessary | 28 | | [RIS](/docs/vt/esc/ris) | `ESC c` | Full reset | 29 | | [DECSCUSR](/docs/vt/csi/decscusr) | `CSI Pn " " q` | Set cursor style | 30 | | [DECKPAM](/docs/vt/esc/deckpam) | `ESC =` | Set numeric keypad to application mode | 31 | | [DECKPNM](/docs/vt/esc/deckpnm) | `ESC >` | Set numeric keypad to numeric mode | 32 | | [DECALN](/docs/vt/esc/decaln) | `ESC # 8` | Screen alignment test | 33 | | [CUU](/docs/vt/csi/cuu) | `CSI Pn A` | Move cursor up | 34 | | [CUD](/docs/vt/csi/cud) | `CSI Pn B` | Move cursor down | 35 | | [CUF](/docs/vt/csi/cuf) | `CSI Pn C` | Move cursor right | 36 | | [CUB](/docs/vt/csi/cub) | `CSI Pn D` | Move cursor left | 37 | | [CNL](/docs/vt/csi/cnl) | `CSI Pn E` | Move cursor down `n` lines and to the leftmost column | 38 | | [CPL](/docs/vt/csi/cpl) | `CSI Pn F` | Move cursor up `n` lines and to the leftmost column | 39 | | [CUP](/docs/vt/csi/cup) | `CSI Py ; Px H` | Move cursor to the specified row and column | 40 | | [CHT](/docs/vt/csi/cht) | `CSI Pn I` | Move cursor right `n` tabs | 41 | | [ED](/docs/vt/csi/ed) | `CSI Pn J` | Erase display | 42 | | [EL](/docs/vt/csi/el) | `CSI Pn K` | Erase line | 43 | | [DL](/docs/vt/csi/dl) | `CSI Pn M` | Delete `n` lines at the cursor | 44 | | [IL](/docs/vt/csi/il) | `CSI Pn L` | Insert `n` lines at the cursor | 45 | | [DCH](/docs/vt/csi/dch) | `CSI Pn P` | Delete `n` characters at the cursor | 46 | | [SU](/docs/vt/csi/su) | `CSI Pn S` | Scroll up `n` lines | 47 | | [SD](/docs/vt/csi/sd) | `CSI Pn T` | Scroll down `n` lines | 48 | | [ECH](/docs/vt/csi/ech) | `CSI Pn X` | Erase `n` characters at the cursor | 49 | | [CBT](/docs/vt/csi/cbt) | `CSI Pn Z` | Move cursor left `n` tabs | 50 | | [HPR](/docs/vt/csi/hpr) | `CSI Pn a` | Move cursor to a column relative to the cursor | 51 | | [REP](/docs/vt/csi/rep) | `CSI Pn b` | Repeat the preceding character `n` times | 52 | | [VPA](/docs/vt/csi/vpa) | `CSI Py d` | Move cursor to the specified row | 53 | | [VPR](/docs/vt/csi/vpr) | `CSI Pn e` | Move cursor down `n` rows relative to the cursor | 54 | | [TBC](/docs/vt/csi/tbc) | `CSI Pn g` | Clear one or all tab stops | 55 | | [DSR](/docs/vt/csi/dsr) | `CSI Pn n` | Device status report | 56 | | [DECSTBM](/docs/vt/csi/decstbm) | `CSI Pt ; Pb r` | Set top and bottom margins | 57 | | [DECSLRM](/docs/vt/csi/decslrm) | `CSI Pl ; Pr s` | Set left and right margins | 58 | | [ICH](/docs/vt/csi/ich) | `CSI Pn @` | Insert `n` characters at the cursor | 59 | | [HPA](/docs/vt/csi/hpa) | `CSI Px ` ` | Move cursor to the specified column | 60 | | [XTSHIFTESCAPE](/docs/vt/csi/xtshiftescape) | `CSI > Pn s` | Configure `shift` modifier behavior with mouse reports | 61 | -------------------------------------------------------------------------------- /flake.lock: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "flake-utils": { 4 | "inputs": { 5 | "systems": "systems" 6 | }, 7 | "locked": { 8 | "lastModified": 1731533236, 9 | "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", 10 | "owner": "numtide", 11 | "repo": "flake-utils", 12 | "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", 13 | "type": "github" 14 | }, 15 | "original": { 16 | "owner": "numtide", 17 | "repo": "flake-utils", 18 | "type": "github" 19 | } 20 | }, 21 | "nixpkgs": { 22 | "locked": { 23 | "lastModified": 1734449207, 24 | "narHash": "sha256-/OYWCU/MMrx/OKRdV3VojgtQtmlexkgwh+o/jCPRLNE=", 25 | "owner": "nixos", 26 | "repo": "nixpkgs", 27 | "rev": "3716288b1d6e3063a61634d2a596d069157eb1d7", 28 | "type": "github" 29 | }, 30 | "original": { 31 | "owner": "nixos", 32 | "ref": "release-24.11", 33 | "repo": "nixpkgs", 34 | "type": "github" 35 | } 36 | }, 37 | "root": { 38 | "inputs": { 39 | "flake-utils": "flake-utils", 40 | "nixpkgs": "nixpkgs" 41 | } 42 | }, 43 | "systems": { 44 | "locked": { 45 | "lastModified": 1681028828, 46 | "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", 47 | "owner": "nix-systems", 48 | "repo": "default", 49 | "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", 50 | "type": "github" 51 | }, 52 | "original": { 53 | "owner": "nix-systems", 54 | "repo": "default", 55 | "type": "github" 56 | } 57 | } 58 | }, 59 | "root": "root", 60 | "version": 7 61 | } 62 | -------------------------------------------------------------------------------- /flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | description = "👻.org"; 3 | 4 | inputs = { 5 | nixpkgs.url = "github:nixos/nixpkgs/release-24.11"; 6 | flake-utils.url = "github:numtide/flake-utils"; 7 | }; 8 | 9 | outputs = { 10 | self, 11 | flake-utils, 12 | nixpkgs, 13 | ... 14 | }: 15 | flake-utils.lib.eachDefaultSystem ( 16 | system: let 17 | pkgs = import nixpkgs {inherit system;}; 18 | in { 19 | devShell = pkgs.callPackage ./nix/devShell.nix {}; 20 | formatter = pkgs.alejandra; 21 | } 22 | ); 23 | } 24 | -------------------------------------------------------------------------------- /next.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = { 3 | reactStrictMode: true, 4 | }; 5 | 6 | export default nextConfig; 7 | -------------------------------------------------------------------------------- /nix/devShell.nix: -------------------------------------------------------------------------------- 1 | { 2 | mkShell, 3 | alejandra, 4 | bash, 5 | nodejs_22, 6 | }: 7 | mkShell rec { 8 | name = "ghostty.org"; 9 | 10 | packages = [ 11 | # Our Makefile requires a modern bash. If the developer computer is 12 | # running macOS then it ships with an old broken version of bash. 13 | # This ensures that the Makefile works. Alternately, we can just 14 | # fix the Makefile. 15 | bash 16 | nodejs_22 17 | 18 | # Required for CI for format checking. 19 | alejandra 20 | ]; 21 | } 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ghostty-website", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "next lint && prettier 'src/**/*.ts' --write" 10 | }, 11 | "dependencies": { 12 | "@r4ai/remark-callout": "^0.6.2", 13 | "classnames": "^2.5.1", 14 | "gray-matter": "^4.0.3", 15 | "klaw-sync": "^6.0.0", 16 | "lucide-react": "^0.424.0", 17 | "next": "^14.2.21", 18 | "next-mdx-remote": "^5.0.0", 19 | "postcss-flexbugs-fixes": "^5.0.2", 20 | "postcss-preset-env": "^9.6.0", 21 | "react": "^18", 22 | "react-dom": "^18", 23 | "react-intersection-observer": "^9.14.0", 24 | "rehype-highlight": "^7.0.1", 25 | "remark-gfm": "^4.0.0", 26 | "slugify": "^1.6.6", 27 | "xml2js": "^0.6.2", 28 | "zustand": "^5.0.2" 29 | }, 30 | "devDependencies": { 31 | "@types/klaw-sync": "^6.0.5", 32 | "@types/node": "20.14.13", 33 | "@types/react": "18.3.3", 34 | "@types/react-dom": "^18", 35 | "@types/xml2js": "^0.4.14", 36 | "eslint": "^8", 37 | "eslint-config-next": "14.2.5", 38 | "prettier": "3.4.2", 39 | "typescript": "5.5.4" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /postcss.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | "postcss-flexbugs-fixes", 4 | [ 5 | "postcss-preset-env", 6 | { 7 | "autoprefixer": { 8 | "flexbox": "no-2009" 9 | }, 10 | "stage": 3, 11 | "features": { 12 | "custom-properties": false, 13 | "nesting-rules": true 14 | } 15 | } 16 | ] 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /public/favicon-128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ghostty-org/website/7093f052a2f35e0247fcb828ede644ed74a749a0/public/favicon-128.png -------------------------------------------------------------------------------- /public/favicon-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ghostty-org/website/7093f052a2f35e0247fcb828ede644ed74a749a0/public/favicon-16.png -------------------------------------------------------------------------------- /public/favicon-256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ghostty-org/website/7093f052a2f35e0247fcb828ede644ed74a749a0/public/favicon-256.png -------------------------------------------------------------------------------- /public/favicon-32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ghostty-org/website/7093f052a2f35e0247fcb828ede644ed74a749a0/public/favicon-32.png -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ghostty-org/website/7093f052a2f35e0247fcb828ede644ed74a749a0/public/favicon.ico -------------------------------------------------------------------------------- /public/ghostty-404.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ghostty-org/website/7093f052a2f35e0247fcb828ede644ed74a749a0/public/ghostty-404.png -------------------------------------------------------------------------------- /public/ghostty-logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /public/images/1-1-0/csd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ghostty-org/website/7093f052a2f35e0247fcb828ede644ed74a749a0/public/images/1-1-0/csd.png -------------------------------------------------------------------------------- /public/images/1-1-0/linear-corrected_blending.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ghostty-org/website/7093f052a2f35e0247fcb828ede644ed74a749a0/public/images/1-1-0/linear-corrected_blending.png -------------------------------------------------------------------------------- /public/images/1-1-0/linear_blending.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ghostty-org/website/7093f052a2f35e0247fcb828ede644ed74a749a0/public/images/1-1-0/linear_blending.png -------------------------------------------------------------------------------- /public/images/1-1-0/p3_blending.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ghostty-org/website/7093f052a2f35e0247fcb828ede644ed74a749a0/public/images/1-1-0/p3_blending.png -------------------------------------------------------------------------------- /public/images/1-1-0/srgb_blending.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ghostty-org/website/7093f052a2f35e0247fcb828ede644ed74a749a0/public/images/1-1-0/srgb_blending.png -------------------------------------------------------------------------------- /public/images/1-1-0/ssd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ghostty-org/website/7093f052a2f35e0247fcb828ede644ed74a749a0/public/images/1-1-0/ssd.png -------------------------------------------------------------------------------- /public/next.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/social-share-card.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ghostty-org/website/7093f052a2f35e0247fcb828ede644ed74a749a0/public/social-share-card.jpg -------------------------------------------------------------------------------- /public/vercel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/blockquote/Blockquote.module.css: -------------------------------------------------------------------------------- 1 | .blockquote { 2 | border-left: 3px solid var(--gray-9); 3 | padding: 6px 20px; 4 | display: block; 5 | margin: 32px 0; 6 | } 7 | -------------------------------------------------------------------------------- /src/components/blockquote/index.tsx: -------------------------------------------------------------------------------- 1 | import classNames from "classnames"; 2 | import s from "./Blockquote.module.css"; 3 | 4 | interface BlockquoteProps { 5 | className?: string; 6 | children?: React.ReactNode; 7 | } 8 | 9 | export default function Blockquote({ className, children }: BlockquoteProps) { 10 | return ( 11 |
12 | {children} 13 |
14 | ); 15 | } 16 | -------------------------------------------------------------------------------- /src/components/breadcrumbs/Breadcrumbs.module.css: -------------------------------------------------------------------------------- 1 | .breadcrumbs { 2 | display: flex; 3 | flex-wrap: wrap; 4 | list-style: none; 5 | 6 | & li { 7 | display: flex; 8 | align-items: center; 9 | 10 | & a { 11 | &.active { 12 | text-decoration: underline; 13 | } 14 | } 15 | 16 | & .chevron { 17 | margin: 0 10px; 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/components/breadcrumbs/index.tsx: -------------------------------------------------------------------------------- 1 | import classNames from "classnames"; 2 | import { ChevronRight } from "lucide-react"; 3 | import Link from "../link"; 4 | import { LI } from "../text"; 5 | import s from "./Breadcrumbs.module.css"; 6 | 7 | export interface Breadcrumb { 8 | text: string; 9 | href: string | null; 10 | } 11 | 12 | interface BreadcrumbsProps { 13 | className?: string; 14 | breadcrumbs: Breadcrumb[]; 15 | } 16 | 17 | export default function Breadcrumbs({ 18 | className, 19 | breadcrumbs, 20 | }: BreadcrumbsProps) { 21 | return ( 22 |
    23 | {breadcrumbs.map((breadcrumb, i) => ( 24 |
  • 25 | {breadcrumb.href ? ( 26 | 33 | ) : ( 34 | <>{breadcrumb.text} 35 | )} 36 | {i + 1 < breadcrumbs.length && ( 37 | 38 | )} 39 |
  • 40 | ))} 41 |
42 | ); 43 | } 44 | -------------------------------------------------------------------------------- /src/components/button-links/ButtonLinks.module.css: -------------------------------------------------------------------------------- 1 | .buttonLinks { 2 | & ul { 3 | --gap: 14px; 4 | --cols: var(--large-size-columns); 5 | @media(max-width: 950px) { 6 | --cols: var(--small-size-columns); 7 | } 8 | @media(max-width: 784px) { 9 | --cols: var(--large-size-columns); 10 | } 11 | @media(max-width: 650px) { 12 | --cols: var(--small-size-columns); 13 | } 14 | 15 | padding: 0; 16 | list-style: none; 17 | display: flex; 18 | flex-wrap: wrap; 19 | gap: var(--gap); 20 | 21 | & li { 22 | --flex-basis: calc((100% / var(--cols)) - (var(--gap) * (var(--cols) - 1)) / var(--cols)); 23 | flex-basis: var(--flex-basis); 24 | max-width: var(--flex-basis); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/components/button-links/index.tsx: -------------------------------------------------------------------------------- 1 | import { ButtonLink, ButtonTheme } from "../link"; 2 | import s from "./ButtonLinks.module.css"; 3 | 4 | interface ButtonLinks { 5 | text: string; 6 | href: string; 7 | theme?: ButtonTheme; 8 | } 9 | 10 | interface ButtonLinkProps { 11 | links: ButtonLinks[]; 12 | columns?: number; 13 | columnsWhenSmall?: number; 14 | } 15 | 16 | export default function ButtonLinks({ 17 | links, 18 | columns = links.length, 19 | columnsWhenSmall = columns, 20 | }: ButtonLinkProps) { 21 | return ( 22 |
31 |
    32 | {links.map(({ href, text, theme = "brand" }) => { 33 | return ( 34 |
  • 35 | 36 |
  • 37 | ); 38 | })} 39 |
40 |
41 | ); 42 | } 43 | -------------------------------------------------------------------------------- /src/components/button/Button.module.css: -------------------------------------------------------------------------------- 1 | .button { 2 | display: flex; 3 | align-items: center; 4 | justify-content: center; 5 | gap: 6px; 6 | cursor: pointer; 7 | user-select: none; 8 | font-weight: 500; 9 | border: 1px solid; 10 | border-radius: var(--form-border-radius); 11 | transition: border-color .15s ease, background-color .15s ease; 12 | box-shadow: var(--form-box-shadow); 13 | 14 | &.neutral { 15 | color: var(--gray-2); 16 | border-color: var(--gray-7); 17 | background-color: var(--white); 18 | &:hover { 19 | @media(hover: hover) { 20 | background-color: var(--gray-9); 21 | } 22 | } 23 | &:active { 24 | background-color: var(--gray-8); 25 | } 26 | } 27 | 28 | &.brand { 29 | border: none; 30 | color: var(--gray-9); 31 | background-color: var(--brand-color); 32 | &:hover { 33 | @media(hover: hover) { 34 | background-color: hsl(var(--brand-color-hsl)/.7); 35 | } 36 | } 37 | &:active { 38 | background-color: hsl(var(--brand-color-hsl)/.7); 39 | } 40 | } 41 | 42 | &.small { 43 | font-size: 12px; 44 | padding: 2px 8px; 45 | } 46 | &.medium { 47 | font-size: 14px; 48 | padding: 4px 12px; 49 | } 50 | &.large { 51 | font-size: 16px; 52 | padding: 6px 16px; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/components/button/index.tsx: -------------------------------------------------------------------------------- 1 | import classNames from "classnames"; 2 | import { FocusEvent, forwardRef, LegacyRef } from "react"; 3 | import s from "./Button.module.css"; 4 | 5 | export type ButtonSize = "small" | "medium" | "large"; 6 | export type ButtonTheme = "neutral" | "brand"; 7 | 8 | interface ButtonProps { 9 | size?: ButtonSize; 10 | theme?: ButtonTheme; 11 | className?: string; 12 | children?: React.ReactNode; 13 | onBlur?: (e: FocusEvent) => void; 14 | onClick?: React.MouseEventHandler; 15 | } 16 | 17 | function Button( 18 | { 19 | size = "medium", 20 | theme = "neutral", 21 | className, 22 | children, 23 | onBlur, 24 | onClick, 25 | }: ButtonProps, 26 | ref?: LegacyRef 27 | ) { 28 | return ( 29 | 43 | ); 44 | } 45 | 46 | export default forwardRef(Button); 47 | -------------------------------------------------------------------------------- /src/components/callout/Callout.module.css: -------------------------------------------------------------------------------- 1 | .callout { 2 | &.note { 3 | --callout-color: var(--callout-note); 4 | } 5 | &.tip { 6 | --callout-color: var(--callout-tip); 7 | } 8 | &.important { 9 | --callout-color: var(--callout-important); 10 | } 11 | &.warning { 12 | --callout-color: var(--callout-warning); 13 | } 14 | &.caution { 15 | --callout-color: var(--callout-caution); 16 | } 17 | border-left: 3px solid var(--callout-color); 18 | 19 | & .header { 20 | display: flex; 21 | flex-align: center; 22 | margin-bottom: 14px; 23 | color: var(--callout-color); 24 | & .icon { 25 | margin-right: 8px; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/components/callout/index.tsx: -------------------------------------------------------------------------------- 1 | import classNames from "classnames"; 2 | import Blockquote from "../blockquote"; 3 | import { BodyParagraph, P } from "../text"; 4 | import s from "./Callout.module.css"; 5 | import { 6 | Info, 7 | Lightbulb, 8 | MessageSquareWarning, 9 | OctagonAlert, 10 | TriangleAlert, 11 | } from "lucide-react"; 12 | 13 | type CalloutType = "note" | "tip" | "important" | "warning" | "caution"; 14 | 15 | interface CalloutProps { 16 | type: CalloutType; 17 | children: React.ReactNode | string; 18 | } 19 | type SpecificCalloutProps = Omit; 20 | 21 | export default function Callout({ type, children }: CalloutProps) { 22 | return ( 23 |
32 |

33 | {" "} 34 | {type.charAt(0).toUpperCase() + type.slice(1)} 35 |

36 | {typeof children === "string" ? ( 37 | {children} 38 | ) : ( 39 | children 40 | )} 41 |
42 | ); 43 | } 44 | 45 | function CalloutIcon({ 46 | className, 47 | type, 48 | }: { 49 | className: string; 50 | type: CalloutType; 51 | }): React.ReactNode { 52 | switch (type) { 53 | case "note": 54 | return ; 55 | case "tip": 56 | return ; 57 | case "important": 58 | return ; 59 | case "warning": 60 | return ; 61 | case "caution": 62 | return ; 63 | } 64 | } 65 | 66 | export function Note({ children }: SpecificCalloutProps) { 67 | return {children}; 68 | } 69 | 70 | export function Tip({ children }: SpecificCalloutProps) { 71 | return {children}; 72 | } 73 | 74 | export function Important({ children }: SpecificCalloutProps) { 75 | return {children}; 76 | } 77 | 78 | export function Warning({ children }: SpecificCalloutProps) { 79 | return {children}; 80 | } 81 | 82 | export function Caution({ children }: SpecificCalloutProps) { 83 | return {children}; 84 | } 85 | -------------------------------------------------------------------------------- /src/components/card-links/CardLinks.module.css: -------------------------------------------------------------------------------- 1 | .cardLinks { 2 | & ul { 3 | --gap: 20px; 4 | --cols: 2; 5 | @media(max-width: 950px) { 6 | --cols: 1; 7 | } 8 | @media(max-width: 784px) { 9 | --cols: 2; 10 | } 11 | @media(max-width: 650px) { 12 | --cols: 1; 13 | } 14 | 15 | margin: 16px 0; 16 | padding: 0; 17 | list-style: none; 18 | display: flex; 19 | flex-wrap: wrap; 20 | gap: var(--gap); 21 | 22 | & li { 23 | --flex-basis: calc((100% / var(--cols)) - (var(--gap) * (var(--cols) - 1)) / var(--cols)); 24 | flex-basis: var(--flex-basis); 25 | max-width: var(--flex-basis); 26 | border: 1px solid var(--gray-2); 27 | border-radius: 10px; 28 | 29 | &:hover { 30 | background-color: var(--gray-2); 31 | } 32 | 33 | & .link { 34 | text-decoration: none; 35 | display: block; 36 | padding: 20px; 37 | 38 | & .title { 39 | margin-bottom: 10px; 40 | } 41 | } 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/components/card-links/index.tsx: -------------------------------------------------------------------------------- 1 | import Link from "next/link"; 2 | import { H4, P } from "../text"; 3 | import s from "./CardLinks.module.css"; 4 | 5 | interface CardLink { 6 | title: string; 7 | description: string; 8 | href: string; 9 | } 10 | 11 | interface CardLinkProps { 12 | cards: CardLink[]; 13 | } 14 | 15 | export default function CardLinks({ cards }: CardLinkProps) { 16 | return ( 17 |
18 |
    19 | {cards.map((cardLink) => { 20 | return ( 21 |
  • 22 | 23 |

    {cardLink.title}

    24 |

    {cardLink.description}

    25 | 26 |
  • 27 | ); 28 | })} 29 |
30 |
31 | ); 32 | } 33 | -------------------------------------------------------------------------------- /src/components/codeblock/CodeBlock.module.css: -------------------------------------------------------------------------------- 1 | .codeBlock { 2 | border-radius: 5px; 3 | font-size: 16px; 4 | margin: 16px 0; 5 | overflow: hidden; 6 | border: 1px solid var(--gray-3); 7 | position: relative; 8 | } 9 | 10 | .codeBlock code { 11 | padding: 16px; 12 | /* Fix for https://github.com/ghostty-org/website/issues/312 13 | * In the case a user has custom fonts disabled, this ensures 14 | * that the code block is still readable. 15 | * 16 | * Additionally, `inherit` here didn't seem to be working? So we're 17 | * setting the font-family explicitly. 18 | */ 19 | font-family: var(--jetbrains-mono), monospace; 20 | } 21 | 22 | .copyButton { 23 | position: absolute; 24 | right: 12px; 25 | top: 12px; 26 | background-color: var(--gray-2); 27 | padding: 4px; 28 | border: none; 29 | border-radius: 5px; 30 | cursor: pointer; 31 | transition: 32 | background-color 0.25s ease-in-out, 33 | opacity 0.25s ease-in-out; 34 | color: var(--gray-6); 35 | opacity: 0; 36 | } 37 | 38 | .codeBlock:hover .copyButton, 39 | .copyButton:focus { 40 | opacity: 1; 41 | } 42 | 43 | .copyButton:hover { 44 | background-color: var(--gray-3); 45 | } 46 | 47 | .copyButtonSuccess { 48 | color: var(--atom-one-green); 49 | } 50 | -------------------------------------------------------------------------------- /src/components/codeblock/index.tsx: -------------------------------------------------------------------------------- 1 | import classNames from "classnames"; 2 | import { jetbrainsMono } from "../text"; 3 | import s from "./CodeBlock.module.css"; 4 | import { useCallback, useState } from "react"; 5 | import { CheckIcon, CopyIcon } from "lucide-react"; 6 | 7 | interface CodeblockProps { 8 | children?: React.ReactNode; 9 | } 10 | 11 | export default function CodeBlock({ children }: CodeblockProps) { 12 | return ( 13 |
14 |       {children}
15 |       
16 |     
17 | ); 18 | } 19 | 20 | function CopyButton() { 21 | const [isCopied, setIsCopied] = useState(false); 22 | 23 | const copyToClipboard = useCallback( 24 | async (e: React.MouseEvent) => { 25 | await navigator.clipboard.writeText( 26 | e.currentTarget.parentNode?.textContent ?? "", 27 | ); 28 | setIsCopied(true); 29 | const timeout = setTimeout(() => setIsCopied(false), 1000); 30 | return () => clearTimeout(timeout); 31 | }, 32 | [], 33 | ); 34 | 35 | return ( 36 | 44 | ); 45 | } 46 | -------------------------------------------------------------------------------- /src/components/custom-mdx/CustomMDX.module.css: -------------------------------------------------------------------------------- 1 | .customMDX { 2 | & > p { 3 | margin: 0.8em 0; 4 | } 5 | & > ul, & > ol { 6 | padding-left: 42px; 7 | margin: 16px 0; 8 | } 9 | & .image { 10 | max-width: 100%; 11 | border: 1px solid var(--gray-5); 12 | margin: 32px auto; 13 | display: block; 14 | } 15 | 16 | & hr { 17 | border: none; 18 | border-bottom: 1px solid var(--gray-2); 19 | margin: 42px 0; 20 | } 21 | 22 | & sup { 23 | vertical-align: top; 24 | font-size: 0.65em; 25 | margin-left: 3px; 26 | & > a { 27 | scroll-margin-top: var(--header-height); 28 | &:target { 29 | border-radius: 15px; 30 | animation: footnote-reference-highlight 1.5s ease-in-out; 31 | } 32 | } 33 | } 34 | 35 | & :global(.footnotes) { 36 | & > ol > li { 37 | &:target { 38 | border-radius: 5px; 39 | animation: footnote-highlight 1.5s ease-in-out; 40 | } 41 | & a { 42 | vertical-align: top; 43 | font-size: 0.8em; 44 | } 45 | } 46 | } 47 | } 48 | 49 | @keyframes footnote-highlight { 50 | from { 51 | background-color: color-mix(in srgb, var(--brand-color) 70%, transparent); 52 | padding: 5px 0; 53 | margin: -5px 0; 54 | } 55 | to { 56 | background-color: transparent; 57 | padding: 0; 58 | margin: 0; 59 | } 60 | } 61 | 62 | @keyframes footnote-reference-highlight { 63 | from { 64 | background-color: color-mix(in srgb, var(--brand-color) 70%, transparent); 65 | padding: 10px 15px; 66 | margin: -10px -15px; 67 | } 68 | to { 69 | background-color: transparent; 70 | padding: 0px; 71 | margin: 0px; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/components/custom-mdx/index.tsx: -------------------------------------------------------------------------------- 1 | import { MDXRemote, MDXRemoteSerializeResult } from "next-mdx-remote"; 2 | import Blockquote from "../blockquote"; 3 | import ButtonLinks from "../button-links"; 4 | import Callout, { Caution, Important, Note, Tip, Warning } from "../callout"; 5 | import CardLinks from "../card-links"; 6 | import CodeBlock from "../codeblock"; 7 | import JumplinkHeader from "../jumplink-header"; 8 | import { BodyParagraph, LI } from "../text"; 9 | import VTSequence from "../vt-sequence"; 10 | import s from "./CustomMDX.module.css"; 11 | import { useStore } from "@/lib/use-store"; 12 | import { useEffect } from "react"; 13 | 14 | interface CustomMDXProps { 15 | content: MDXRemoteSerializeResult; 16 | } 17 | 18 | export default function CustomMDX({ content }: CustomMDXProps) { 19 | const resetHeaderIdsInView = useStore((state) => state.resetHeaderIdsInView); 20 | useEffect(() => { 21 | // When we do a client-side navigation to another page 22 | // the content will change & we will need to do a reset. 23 | resetHeaderIdsInView(); 24 | }, [content]); 25 | 26 | return ( 27 |
28 | JumplinkHeader({ ...props, as: "h1" }), 32 | h2: (props) => JumplinkHeader({ ...props, as: "h2" }), 33 | h3: (props) => JumplinkHeader({ ...props, as: "h3" }), 34 | h4: (props) => JumplinkHeader({ ...props, as: "h4" }), 35 | h5: (props) => JumplinkHeader({ ...props, as: "h5" }), 36 | h6: (props) => JumplinkHeader({ ...props, as: "h6" }), 37 | li: LI, 38 | p: BodyParagraph, 39 | pre: CodeBlock, 40 | blockquote: Blockquote, 41 | img: (props) => ( 42 | // eslint-disable-next-line @next/next/no-img-element 43 | {props.alt} 44 | ), 45 | VTSequence, 46 | CardLinks, 47 | ButtonLinks, 48 | /* Callout Variants */ 49 | Callout, 50 | Note, 51 | Tip, 52 | Important, 53 | Warning, 54 | Caution, 55 | "callout-title": () => null, 56 | }} 57 | /> 58 |
59 | ); 60 | } 61 | -------------------------------------------------------------------------------- /src/components/footer/Footer.module.css: -------------------------------------------------------------------------------- 1 | .footer { 2 | background: var(--gray-0); 3 | border-top: 1px solid var(--gray-2); 4 | padding: 32px 0; 5 | 6 | @media print { 7 | display: none; 8 | } 9 | 10 | & .gridContainer { 11 | display: flex; 12 | justify-content: space-between; 13 | flex-direction: row; 14 | align-items: center; 15 | @media(max-width: 768px) { 16 | flex-direction: column; 17 | align-items: flex-start; 18 | } 19 | 20 | & .linkList { 21 | list-style: none; 22 | display: flex; 23 | gap: 40px; 24 | @media(max-width: 768px) { 25 | flex-wrap: wrap; 26 | margin-bottom: 38px; 27 | gap: 32px; 28 | } 29 | & a { 30 | color: var(--gray-5); 31 | text-decoration: none; 32 | &:hover { 33 | color: var(--gray-9); 34 | } 35 | } 36 | } 37 | & .copyright { 38 | color: var(--gray-5); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/components/footer/index.tsx: -------------------------------------------------------------------------------- 1 | import classNames from "classnames"; 2 | import GridContainer, { NavAndFooterGridConfig } from "../grid-container"; 3 | import Link, { SimpleLink } from "../link"; 4 | import { P } from "../text"; 5 | import s from "./Footer.module.css"; 6 | 7 | interface FooterProps { 8 | className?: string; 9 | links?: SimpleLink[]; 10 | copyright: string; 11 | } 12 | 13 | export default function Footer({ className, links, copyright }: FooterProps) { 14 | return ( 15 |
16 | 20 | {links && ( 21 |
    22 | {links.map((link) => { 23 | return ( 24 |
  • 25 | 26 |
  • 27 | ); 28 | })} 29 |
30 | )} 31 |

{copyright}

32 |
33 |
34 | ); 35 | } 36 | -------------------------------------------------------------------------------- /src/components/generic-404/Generic404.module.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ghostty-org/website/7093f052a2f35e0247fcb828ede644ed74a749a0/src/components/generic-404/Generic404.module.css -------------------------------------------------------------------------------- /src/components/generic-404/index.tsx: -------------------------------------------------------------------------------- 1 | export default function Generic404() { 2 | return
404
; 3 | } 4 | -------------------------------------------------------------------------------- /src/components/generic-card/GenericCard.module.css: -------------------------------------------------------------------------------- 1 | .genericCard { 2 | border: 1px solid var(--gray-2); 3 | border-radius: 10px; 4 | 5 | & .title { 6 | margin-bottom: 8px; 7 | } 8 | 9 | & .description { 10 | margin-bottom: 16px; 11 | } 12 | 13 | & .children { 14 | display: flex; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/components/generic-card/index.tsx: -------------------------------------------------------------------------------- 1 | import { SpacingProp } from "@/types/style"; 2 | import { H2, P } from "../text"; 3 | import s from "./GenericCard.module.css"; 4 | 5 | interface GenericCardProps { 6 | title: string; 7 | description: string; 8 | padding?: SpacingProp; 9 | children?: React.ReactNode; 10 | } 11 | 12 | export default function GenericCard({ 13 | title, 14 | padding = "56px", 15 | description, 16 | children, 17 | }: GenericCardProps) { 18 | return ( 19 |
20 |

{title}

21 |

{description}

22 | {children ?
{children}
: null} 23 |
24 | ); 25 | } 26 | -------------------------------------------------------------------------------- /src/components/grid-container/GridContainer.module.css: -------------------------------------------------------------------------------- 1 | .gridContainer { 2 | padding-left: var(--desktop-padding); 3 | padding-right: var(--desktop-padding); 4 | max-width: var(--max-width); 5 | position: relative; 6 | width: 100%; 7 | margin-left: auto; 8 | margin-right: auto; 9 | 10 | @media(max-width: 768px) { 11 | padding-left: var(--mobile-padding); 12 | padding-right: var(--mobile-padding); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/components/grid-container/index.tsx: -------------------------------------------------------------------------------- 1 | import classNames from "classnames"; 2 | import s from "./GridContainer.module.css"; 3 | import { UnitProp } from "@/types/style"; 4 | 5 | export const NavAndFooterGridConfig: GridConfig = { 6 | mobilePadding: "28px", 7 | desktopPadding: "40px", 8 | maxWidth: "1700px", 9 | }; 10 | 11 | export const StandardGridConfig: GridConfig = { 12 | desktopPadding: "48px", 13 | mobilePadding: "24px", 14 | maxWidth: "1300px", 15 | }; 16 | 17 | interface GridContainerProps { 18 | className?: string; 19 | children?: React.ReactNode; 20 | gridConfig?: GridConfig; 21 | } 22 | 23 | interface GridConfig { 24 | desktopPadding: UnitProp; 25 | mobilePadding: UnitProp; 26 | maxWidth: UnitProp; 27 | } 28 | 29 | export default function GridContainer({ 30 | className, 31 | children, 32 | gridConfig = StandardGridConfig, 33 | }: GridContainerProps) { 34 | return ( 35 |
45 | {children} 46 |
47 | ); 48 | } 49 | -------------------------------------------------------------------------------- /src/components/info-cards-section/InfoCards.module.css: -------------------------------------------------------------------------------- 1 | .infoCardsSection { 2 | & .sectionTitle { 3 | max-width: 772px; 4 | margin-bottom: 100px; 5 | @media(max-width: 768px) { 6 | margin-bottom: 50px; 7 | } 8 | } 9 | } 10 | 11 | .infoCards { 12 | --gap: 20px; 13 | --cols: 3; 14 | @media(max-width: 1100px) { 15 | --cols: 2; 16 | } 17 | @media(max-width: 768px) { 18 | --cols: 1; 19 | } 20 | 21 | list-style: none; 22 | display: flex; 23 | flex-wrap: wrap; 24 | gap: var(--gap); 25 | 26 | & li { 27 | flex-basis: calc((100% / var(--cols)) - (var(--gap) * (var(--cols) - 1)) / var(--cols)); 28 | border: 1px solid var(--gray-2); 29 | border-radius: 10px; 30 | padding: 28px; 31 | display: flex; 32 | flex-direction: column; 33 | 34 | & .iconContainer { 35 | display: flex; 36 | background: var(--gray-1); 37 | padding: 14px; 38 | align-self: flex-start; 39 | border-radius: 5px; 40 | & > svg { 41 | color: var(--brand-color); 42 | } 43 | } 44 | & > h3 { 45 | margin-top: 22px; 46 | } 47 | & > p { 48 | margin-top: 8px; 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/components/info-cards-section/index.tsx: -------------------------------------------------------------------------------- 1 | import classNames from "classnames"; 2 | import { ReactNode } from "react"; 3 | import SectionWrapper from "../section-wrapper"; 4 | import { H1, H3, P } from "../text"; 5 | import s from "./InfoCards.module.css"; 6 | 7 | interface InfoCardsSectionProps { 8 | className?: string; 9 | title: string; 10 | infoCards: InfoCard[]; 11 | } 12 | 13 | interface InfoCard { 14 | title: string; 15 | description: string; 16 | icon: ReactNode; 17 | } 18 | 19 | export default function InfoCardsSection({ 20 | className, 21 | title, 22 | infoCards, 23 | }: InfoCardsSectionProps) { 24 | return ( 25 | 26 |

{title}

27 |
    28 | {infoCards.map((infoCard, i) => { 29 | return ( 30 |
  • 31 |
    {infoCard.icon}
    32 |

    {infoCard.title}

    33 |

    {infoCard.description}

    34 |
  • 35 | ); 36 | })} 37 |
38 |
39 | ); 40 | } 41 | -------------------------------------------------------------------------------- /src/components/jumplink-header/JumplinkHeader.module.css: -------------------------------------------------------------------------------- 1 | .jumplinkHeader { 2 | display: inline; 3 | scroll-margin-top: var(--header-height); 4 | &:target { 5 | & .text { 6 | animation: jumplink-highlight 1.5s ease-in-out; 7 | border-radius: 8px; 8 | } 9 | } 10 | 11 | & .content { 12 | display: flex; 13 | align-items: center; 14 | 15 | &.h1{ 16 | margin-top: 68px; 17 | margin-bottom: 26px; 18 | } 19 | &.h2{ 20 | margin-top: 52px; 21 | margin-bottom: 20px; 22 | } 23 | &.h3{ 24 | margin-top: 39px; 25 | margin-bottom: 15px; 26 | } 27 | &.h4{ 28 | margin-top: 34px; 29 | margin-bottom: 13px; 30 | } 31 | &.h5{ 32 | margin-top: 26px; 33 | margin-bottom: 10px; 34 | } 35 | &.h6{ 36 | margin-top: 23px; 37 | margin-bottom: 9px; 38 | } 39 | 40 | 41 | & .jumplinkCopy { 42 | margin-left: 16px; 43 | transition: opacity .2s ease; 44 | opacity: 0; 45 | } 46 | &:hover { 47 | & .jumplinkCopy { 48 | opacity: 1; 49 | } 50 | } 51 | } 52 | 53 | & + .jumplinkHeader { 54 | & .content { 55 | margin-top: 0; 56 | } 57 | } 58 | } 59 | 60 | .jumplinkCopy { 61 | display: flex; 62 | padding: 4px; 63 | border: 1px solid var(--gray-4); 64 | border-radius: 3px; 65 | 66 | & .icon { 67 | width: 16px; 68 | height: 16px; 69 | color: var(--gray-9); 70 | } 71 | } 72 | 73 | @keyframes jumplink-highlight { 74 | from { 75 | background-color: color-mix(in srgb, var(--brand-color) 60%, transparent); 76 | padding: 6px 16px; 77 | margin: -6px -16px; 78 | } 79 | to { 80 | background-color: transparent; 81 | padding: 0px; 82 | margin: 0px; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/components/jumplink-header/index.tsx: -------------------------------------------------------------------------------- 1 | import classNames from "classnames"; 2 | import { Link } from "lucide-react"; 3 | import slugify from "slugify"; 4 | import Text from "../text"; 5 | import s from "./JumplinkHeader.module.css"; 6 | import { useInView } from "react-intersection-observer"; 7 | import { useEffect, useState } from "react"; 8 | import { useStore } from "@/lib/use-store"; 9 | 10 | interface JumplinkHeaderProps { 11 | as: "h1" | "h2" | "h3" | "h4" | "h5" | "h6"; 12 | className?: string; 13 | children?: React.ReactNode; 14 | } 15 | 16 | export default function JumplinkHeader({ 17 | className, 18 | children, 19 | as, 20 | }: JumplinkHeaderProps) { 21 | const id = headerDeeplinkIdentifier(children); 22 | const { ref, inView } = useInView({ 23 | // This is our header height! This also impacts our 24 | // margin below, but TBH I actually like it needing to 25 | // peek out a little bit further before this triggers 26 | rootMargin: "-72px", 27 | threshold: 1, 28 | }); 29 | const updateHeaderIdInView = useStore((state) => state.updateHeaderIdInView); 30 | useEffect(() => { 31 | updateHeaderIdInView(inView, id); 32 | }, [inView]); 33 | 34 | return ( 35 |
36 |
46 | 53 | {children} 54 | 55 | 56 | 57 | 58 |
59 |
60 | ); 61 | } 62 | 63 | /** 64 | * @param children the MDX children node of the header element 65 | * @returns The resulting id string which should be applied to the jumplink-header 66 | */ 67 | function headerDeeplinkIdentifier(children?: React.ReactNode): string { 68 | const unexpectedError = () => { 69 | return new Error(`We have encountered an unsupported MDX element in a Header, 70 | and are unable to generate an identifier for it. This likely a bug 71 | with the function which threw this error. 72 | 73 | Header MDX: 74 | ${JSON.stringify(children, null, 2)}`); 75 | }; 76 | 77 | if (typeof children === "string") { 78 | // # Some simple string header 79 | return slugify(children, { lower: true }); 80 | } else if (Array.isArray(children)) { 81 | // # Some `multi-component` header 82 | var flattenedTitle = ""; 83 | children.forEach((value) => { 84 | if (typeof value === "string") { 85 | flattenedTitle += value; 86 | } else if (typeof value === "object" && value.type === "code") { 87 | flattenedTitle += value.props.children; 88 | } else { 89 | throw unexpectedError(); 90 | } 91 | }); 92 | return slugify(flattenedTitle, { lower: true }); 93 | } else if (children != null && typeof children === "object") { 94 | if ((children as any).type === "code") { 95 | // # `just-some-code` 96 | return slugify((children as any).props.children, { lower: true }); 97 | } 98 | throw unexpectedError(); 99 | } else { 100 | throw unexpectedError(); 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/components/link/Link.module.css: -------------------------------------------------------------------------------- 1 | .link { 2 | display: flex; 3 | align-items: center; 4 | text-decoration: none; 5 | & svg { 6 | margin-left: 4px; 7 | & path { 8 | transition-duration: 0.1s; 9 | transition-timing-function: ease-in-out; 10 | } 11 | } 12 | &:hover { 13 | text-decoration: underline; 14 | & svg.externalLinkIcon { 15 | /* Note: This is targeting the arrow paths. It's possible 16 | that the path order changes and this breaks, but it seems 17 | unlikely enough to justify adding this nice polish. */ 18 | & path:not(:first-of-type) { 19 | transform: translate(2px, -2px); 20 | } 21 | } 22 | } 23 | &.weightLight { 24 | font-weight: 400; 25 | & strong { 26 | font-weight: 500; 27 | } 28 | } 29 | &.weightRegular { 30 | font-weight: 500; 31 | & strong { 32 | font-weight: 600; 33 | } 34 | } 35 | &.weightMedium { 36 | font-weight: 600; 37 | & strong { 38 | font-weight: 700; 39 | } 40 | } 41 | 42 | & .icon { 43 | margin-right: 6px; 44 | display: flex; 45 | } 46 | } 47 | 48 | .buttonLink { 49 | display: flex; 50 | justify-content: center; 51 | &.brand { 52 | --button-color: var(--brand-color); 53 | } 54 | &.neutral { 55 | --button-color: var(--gray-3); 56 | } 57 | border: 1px solid var(--button-color); 58 | &:hover { 59 | background-color: var(--button-color); 60 | text-decoration: none; 61 | } 62 | 63 | border-radius: 6px; 64 | transition: background-color 0.15s; 65 | &.small { 66 | font-size: 14px; 67 | padding: 4px 12px; 68 | } 69 | &.medium { 70 | font-size: 15px; 71 | padding: 6px 18px; 72 | } 73 | &.large { 74 | font-size: 16px; 75 | padding: 10px 34px; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/components/link/index.tsx: -------------------------------------------------------------------------------- 1 | import classNames from "classnames"; 2 | import { SquareArrowOutUpRight } from "lucide-react"; 3 | import NextLink from "next/link"; 4 | import { pretendardStdVariable } from "../text"; 5 | import s from "./Link.module.css"; 6 | 7 | export interface SimpleLink { 8 | text: string; 9 | href: string; 10 | } 11 | 12 | export interface LinkProps extends SimpleLink { 13 | className?: string; 14 | weight?: "light" | "regular" | "medium"; 15 | showExternalIcon?: boolean; 16 | icon?: React.ReactNode; 17 | onClick?: React.MouseEventHandler; 18 | } 19 | 20 | export default function Link({ 21 | className, 22 | text, 23 | href, 24 | weight = "light", 25 | icon, 26 | showExternalIcon = true, 27 | onClick, 28 | }: LinkProps) { 29 | const isExternal = !href.startsWith("/"); 30 | return ( 31 | 46 | {icon &&
{icon}
} 47 | {text}{" "} 48 | {showExternalIcon && isExternal && ( 49 | 50 | )} 51 |
52 | ); 53 | } 54 | 55 | export type ButtonSize = "small" | "medium" | "large"; 56 | export type ButtonTheme = "neutral" | "brand"; 57 | 58 | export interface ButtonLinkProps extends LinkProps { 59 | size?: ButtonSize; 60 | theme?: ButtonTheme; 61 | } 62 | 63 | export function ButtonLink({ 64 | size = "medium", 65 | theme = "brand", 66 | className, 67 | ...props 68 | }: ButtonLinkProps) { 69 | return ( 70 | 80 | ); 81 | } 82 | -------------------------------------------------------------------------------- /src/components/nav-tree/NavTree.module.css: -------------------------------------------------------------------------------- 1 | .navTree { 2 | display: flex; 3 | flex-direction: column; 4 | } 5 | 6 | ul.nodesList { 7 | --node-font-size: 16px; 8 | --node-padding: 12px 6px; 9 | --node-border-radius: 5px; 10 | 11 | display: flex; 12 | flex-direction: column; 13 | list-style: none; 14 | 15 | & li { 16 | margin-bottom: 2px; 17 | width: 100%; 18 | 19 | & .folderNode { 20 | --svg-rotation: 0deg; /* Resets rotation when nested */ 21 | & button { 22 | border-style: none; 23 | background: none; 24 | width: 100%; 25 | text-align: left; 26 | font-weight: 500; 27 | color: var(--gray-6); 28 | font-size: var(--node-font-size); 29 | line-height: var(--node-font-size); 30 | padding: var(--node-padding); 31 | border-radius: var(--node-border-radius); 32 | cursor: pointer; 33 | display: flex; 34 | justify-content: space-between; 35 | align-items: center; 36 | 37 | &:hover { 38 | color: var(--gray-9); 39 | background: var(--gray-2); 40 | } 41 | } 42 | 43 | & svg { 44 | transition: transform 0.2s; 45 | } 46 | 47 | &.isOpen { 48 | --svg-rotation: -180deg; 49 | & svg { 50 | transform: rotate(var(--svg-rotation)); 51 | } 52 | } 53 | 54 | &>.children { 55 | margin-left: 12px; 56 | } 57 | } 58 | 59 | & .breakNode { 60 | border: none; 61 | border-bottom: 1px solid var(--gray-2); 62 | margin: 20px 0; 63 | } 64 | 65 | & .linkNode { 66 | display: block; 67 | font-weight: 400; 68 | color: var(--gray-5); 69 | font-size: var(--node-font-size); 70 | line-height: var(--node-font-size); 71 | padding: var(--node-padding); 72 | border-radius: var(--node-border-radius); 73 | text-decoration: none; 74 | 75 | &:hover { 76 | background: var(--gray-2); 77 | color: var(--gray-9); 78 | } 79 | 80 | &.active { 81 | background: var(--brand-color); 82 | color: var(--gray-9); 83 | } 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/components/navbar/Navbar.module.css: -------------------------------------------------------------------------------- 1 | .navbar { 2 | position: sticky; 3 | top: 0; 4 | z-index: 1; 5 | height: var(--header-height); 6 | background: var(--gray-0); 7 | border-bottom: 1px solid var(--gray-2); 8 | 9 | @media print { 10 | display: none; 11 | } 12 | 13 | & .gridContainer { 14 | height: 100%; 15 | display: flex; 16 | justify-content: space-between; 17 | align-items: center; 18 | 19 | & .desktopLinks { 20 | display: flex; 21 | align-items: center; 22 | @media(max-width: 768px) { 23 | display: none; 24 | } 25 | 26 | & .linkList { 27 | list-style: none; 28 | display: flex; 29 | gap: 30px; 30 | & a { 31 | color: var(--gray-5); 32 | text-decoration: none; 33 | &.active { 34 | text-decoration: underline; 35 | } 36 | &:hover { 37 | color: var(--gray-9); 38 | } 39 | } 40 | } 41 | 42 | & .cta { 43 | margin-left: 30px; 44 | } 45 | } 46 | } 47 | 48 | & .mobileContent { 49 | display: none; 50 | overflow: scroll; 51 | &.mobileMenuOpen { 52 | display: block; 53 | } 54 | background: var(--gray-0); 55 | height: calc(100dvh - var(--header-height)); 56 | border-top: 1px solid var(--gray-2); 57 | padding: 24px; 58 | } 59 | } 60 | 61 | .menuToggle { 62 | display: none; 63 | @media(max-width: 768px) { 64 | display: block; 65 | } 66 | 67 | /* reset appearance */ 68 | appearance: none; 69 | background: none; 70 | border: none; 71 | cursor: pointer; 72 | touch-action: manipulation; 73 | 74 | /* make the clickable area really big since people seeing this are on mobile */ 75 | height: var(--header-height); 76 | padding: 0 24px; 77 | margin: 0 -24px 0 0; 78 | 79 | & .hamburger { 80 | position: relative; 81 | width: 24px; 82 | height: 1em; 83 | 84 | & .hamburgerLayer { 85 | position: absolute; 86 | width: 100%; 87 | height: 2px; 88 | border-radius: 1px; 89 | background: var(--white); 90 | 91 | transition-property: transform, opacity; 92 | transition-timing-function: ease-in-out; 93 | transition-duration: 100ms; 94 | 95 | @media (prefers-reduced-motion) { 96 | transition: none; 97 | } 98 | 99 | &:nth-child(1) { 100 | transform: translate(0, 0.0em) rotate(0); 101 | } 102 | &:nth-child(2) { 103 | transform: translate(0, calc(0.5em - 1px)) rotate(0); 104 | } 105 | &:nth-child(3) { 106 | transform: translate(0, calc(1.0em - 2px)) rotate(0); 107 | } 108 | } 109 | 110 | &[data-open=true] .hamburgerLayer { 111 | &:nth-child(1) { 112 | transform: translate(0, 0.5em) rotate(45deg); 113 | } 114 | &:nth-child(2) { 115 | transform: translate(0, 0.5em) rotate(30deg); 116 | opacity: 0; 117 | } 118 | &:nth-child(3) { 119 | transform: translate(0, 0.5em) rotate(-45deg); 120 | } 121 | } 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /src/components/scroll-to-top/ScrollToTop.module.css: -------------------------------------------------------------------------------- 1 | & .scrollToTopButton { 2 | position: fixed; 3 | bottom: 20px; 4 | right: 20px; 5 | background: var(--gray-2); 6 | color: var(--gray-9); 7 | border-radius: 5px; 8 | border: none; 9 | padding: 8px 12px; 10 | font-size: 16px; 11 | cursor: pointer; 12 | transition: background .2s; 13 | z-index: 10; 14 | width: 40px; 15 | height: 40px; 16 | display: flex; 17 | justify-content: center; 18 | align-items: center; 19 | 20 | transition: bottom .25s ease; 21 | @media (prefers-reduced-motion) { 22 | transition: none; 23 | } 24 | &.isAtBottom { 25 | bottom: 100px; 26 | } 27 | &:hover { 28 | background: var(--gray-3); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/components/scroll-to-top/index.tsx: -------------------------------------------------------------------------------- 1 | import { ArrowUp } from "lucide-react"; 2 | import { useEffect, useRef, useState } from "react"; 3 | import s from "./ScrollToTop.module.css"; 4 | import classNames from "classnames"; 5 | 6 | export default function ScrollToTopButton() { 7 | const [showScrollToTopButton, setShowScrollToTopButton] = useState(false); 8 | const [isAtBottom, setIsAtBottom] = useState(false); 9 | const setScrollToTopButtonRef = useRef( 10 | null 11 | ); 12 | useEffect(() => { 13 | const handleScroll = () => { 14 | const windowHeight = window.innerHeight; 15 | const documentHeight = document.documentElement.scrollHeight; 16 | const scrollTop = window.scrollY; 17 | 18 | // Show button when scrolled down more than 100px 19 | if (scrollTop > 100) { 20 | setShowScrollToTopButton(true); 21 | } else { 22 | setShowScrollToTopButton(false); 23 | } 24 | 25 | // Keep track if we're completely at the bottom of the page, as to lift 26 | // the scroll to top button as to not cover the footer. 27 | const isNotAtBottom = documentHeight - (scrollTop + windowHeight) > 50; 28 | if(isNotAtBottom) { 29 | setIsAtBottom(false) 30 | } else { 31 | setIsAtBottom(true) 32 | } 33 | }; 34 | 35 | window.addEventListener("scroll", handleScroll); 36 | return () => { 37 | window.removeEventListener("scroll", handleScroll); 38 | }; 39 | }, []); 40 | 41 | return ( 42 | showScrollToTopButton && 43 | 52 | ); 53 | }; 54 | -------------------------------------------------------------------------------- /src/components/section-wrapper/SectionWrapper.module.css: -------------------------------------------------------------------------------- 1 | .sectionWrapper { 2 | padding: 100px 0; 3 | @media(max-width: 1100px) { 4 | padding: 75px 0; 5 | } 6 | @media(max-width: 768px) { 7 | padding: 48px 0; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/components/section-wrapper/index.tsx: -------------------------------------------------------------------------------- 1 | import classNames from "classnames"; 2 | import GridContainer from "../grid-container"; 3 | import s from "./SectionWrapper.module.css"; 4 | 5 | interface SectionWrapperProps { 6 | className?: string; 7 | children?: React.ReactNode; 8 | } 9 | 10 | export default function SectionWrapper({ 11 | children, 12 | className, 13 | }: SectionWrapperProps) { 14 | return ( 15 |
16 | {children} 17 |
18 | ); 19 | } 20 | -------------------------------------------------------------------------------- /src/components/sidecar/Sidecar.module.css: -------------------------------------------------------------------------------- 1 | .sidecar { 2 | --left-padding: 12px; 3 | --base-depth: 2; /* H1's & H2's will have standard padding */ 4 | --depth-padding: 8px; /* Additional padding for each header number above H2 */ 5 | --gradient-height: 20px; 6 | --gradient-color: var(--gray-0); 7 | 8 | &::before, 9 | &::after { 10 | content: ""; 11 | position: sticky; 12 | display: block; 13 | left: 0; 14 | right: 0; 15 | height: var(--gradient-height); 16 | pointer-events: none; 17 | opacity: 1; 18 | transition: opacity 0.3s ease; 19 | z-index: 1; 20 | } 21 | 22 | &::before { 23 | top: 0; 24 | background: linear-gradient(to bottom, var(--gradient-color), transparent); 25 | } 26 | 27 | &::after { 28 | bottom: 0; 29 | background: linear-gradient(to top, var(--gradient-color), transparent); 30 | } 31 | 32 | & ul { 33 | list-style: none; 34 | position: relative; 35 | z-index: 0; 36 | & li { 37 | --item-color: var(--gray-4); 38 | &:hover { 39 | --item-color: var(--gray-6); 40 | } 41 | &.active { 42 | --item-color: var(--gray-8); 43 | } 44 | border-left: 2px solid var(--item-color); 45 | padding: 2px 0; 46 | padding-left: calc( 47 | var(--left-padding) + 48 | ( 49 | (max(var(--depth), var(--base-depth)) - (var(--base-depth) - 1)) * 50 | var(--depth-padding) 51 | ) 52 | ); 53 | & p { 54 | color: var(--item-color); 55 | overflow-wrap: break-word; 56 | } 57 | & a { 58 | text-decoration-line: none; 59 | text-decoration-color: var(--item-color); 60 | &:hover { 61 | text-decoration-line: underline; 62 | } 63 | } 64 | &.active a { 65 | text-decoration-line: underline; 66 | } 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/components/sidecar/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useMemo, useRef, useState } from "react"; 2 | import classNames from "classnames"; 3 | import { H6, P } from "../text"; 4 | import s from "./Sidecar.module.css"; 5 | import { useStore } from "@/lib/use-store"; 6 | 7 | interface SidecarItem { 8 | id: string; 9 | title: string; 10 | depth: number; 11 | } 12 | 13 | interface SidecarProps { 14 | className?: string; 15 | items: SidecarItem[]; 16 | hidden?: boolean; 17 | } 18 | 19 | // If there's less items than this, the sidecar content won't render 20 | // it does not make sense to have a single item in the sidecar. 21 | const MIN_SIDECAR_ITEMS = 2; 22 | 23 | // H4s and below will only display in the sidecar 24 | const MAX_SIDECAR_HEADER_DEPTH = 4; 25 | 26 | export default function Sidecar({ 27 | className, 28 | items, 29 | hidden = false, 30 | }: SidecarProps) { 31 | const activeItemRef = useRef(null); 32 | const sidecarRef = useRef(null); 33 | const headerIdsInView = useStore((state) => state.headerIdsInView); 34 | const shownItems = useMemo(() => { 35 | return items.filter((v) => v.depth <= MAX_SIDECAR_HEADER_DEPTH); 36 | }, [items]); 37 | const [lastActiveHeaderID, setLastActiveHeaderID] = useState( 38 | null, 39 | ); 40 | const activeHeaderID = useMemo(() => { 41 | const currentActiveID = shownItems.find((v) => 42 | headerIdsInView.includes(v.id), 43 | )?.id; 44 | if (currentActiveID && currentActiveID !== lastActiveHeaderID) { 45 | setLastActiveHeaderID(currentActiveID); 46 | return currentActiveID; 47 | } 48 | return lastActiveHeaderID; 49 | }, [shownItems, headerIdsInView, lastActiveHeaderID]); 50 | 51 | useEffect(() => { 52 | if (activeItemRef.current && sidecarRef.current) { 53 | const sidecarElement = sidecarRef.current; 54 | const activeItemRect = activeItemRef.current.getBoundingClientRect(); 55 | const sidecarRect = sidecarElement.getBoundingClientRect(); 56 | 57 | // Keep the active item in the center of the sidecar, if possible 58 | const targetPosition = sidecarRect.height / 2 - activeItemRect.height / 2; 59 | const currentPosition = activeItemRect.top - sidecarRect.top; 60 | const scrollAmount = currentPosition - targetPosition; 61 | 62 | sidecarElement.scrollBy({ top: scrollAmount, behavior: "smooth" }); 63 | } 64 | }, [activeHeaderID]); 65 | 66 | return ( 67 |
68 | {items.length > MIN_SIDECAR_ITEMS && !hidden && ( 69 |
    70 | {items.map(({ id, title, depth }) => { 71 | const active = id === activeHeaderID; 72 | return ( 73 |
  • 83 | {/* Intentionally using an a tag and not next/link: 84 | as we want our :target selectors to trigger here. 85 | See: https://github.com/vercel/next.js/issues/51346 86 | Also, we're remaining on the same page always here, 87 | so no client-side routing handing is needed. */} 88 | 89 |

    {title}

    90 |
    91 |
  • 92 | ); 93 | })} 94 |
95 | )} 96 |
97 | ); 98 | } 99 | -------------------------------------------------------------------------------- /src/components/tabbed-terminals-section/TabbedTerminalsSection.module.css: -------------------------------------------------------------------------------- 1 | .tabbedTerminalsSection { 2 | & .content { 3 | border: 1px solid var(--gray-2); 4 | border-radius: 10px; 5 | display: flex; 6 | flex-direction: row; 7 | overflow: hidden; 8 | padding: 100px 0 100px 50px; 9 | @media(max-width: 1100px) { 10 | padding: 50px 28px; 11 | } 12 | 13 | & .primary { 14 | @media(max-width: 1100px) { 15 | width: 100%; 16 | } 17 | & .tabs { 18 | list-style: none; 19 | margin-top: 62px; 20 | @media(max-width: 1100px) { 21 | margin-top: 36px; 22 | } 23 | 24 | & > li { 25 | --lr-padding: 22px; 26 | --tb-padding: 30px; 27 | @media(max-width: 1100px) { 28 | --lr-padding: 14px; 29 | --tb-padding: 26px; 30 | } 31 | overflow: hidden; 32 | margin-left: calc(-1 * var(--lr-padding)); 33 | margin-right: calc(-1 * var(--lr-padding)); 34 | padding: var(--tb-padding) var(--lr-padding); 35 | &:hover { 36 | cursor: pointer; 37 | background: var(--gray-1); 38 | } 39 | &:not(:last-of-type) { 40 | border-bottom: 1px solid var(--gray-2); 41 | } 42 | 43 | --header-items-color: var(--gray-5); 44 | &:hover,&.active { 45 | --header-items-color: var(--gray-9); 46 | } 47 | & .header { 48 | display: flex; 49 | justify-content: space-between; 50 | align-items: center; 51 | & > * { 52 | transition: color .25s; 53 | color: var(--header-items-color); 54 | } 55 | & > svg { 56 | width: auto; 57 | } 58 | } 59 | 60 | & .tabContent { 61 | & .terminal { 62 | margin-top: 20px; 63 | display: none; 64 | } 65 | & .description { 66 | margin-top: 14px; 67 | color: var(--gray-9); 68 | padding-right: 48px; 69 | } 70 | 71 | @media(max-width: 1100px) { 72 | display: flex; 73 | flex-direction: row-reverse; 74 | justify-content: space-between; 75 | & .terminal { 76 | margin-top: 14px; 77 | display: flex; 78 | } 79 | & .description { 80 | max-width: 400px; 81 | padding-right: 16x; 82 | align-self: flex-start; 83 | } 84 | } 85 | @media(max-width: 768px) { 86 | flex-direction: column; 87 | & .description { 88 | max-width: 100%; 89 | padding-right: 0; 90 | } 91 | } 92 | } 93 | } 94 | } 95 | } 96 | 97 | & .spotlight { 98 | margin-left: 108px; 99 | margin-right: -30px; 100 | margin-top: 55px; 101 | @media(max-width: 1100px) { 102 | display: none; 103 | } 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/components/tabbed-terminals-section/index.tsx: -------------------------------------------------------------------------------- 1 | import classNames from "classnames"; 2 | import { ChevronDown } from "lucide-react"; 3 | import { useState } from "react"; 4 | import SectionWrapper from "../section-wrapper"; 5 | import Terminal from "../terminal"; 6 | import { H1, H4, P } from "../text"; 7 | import s from "./TabbedTerminalsSection.module.css"; 8 | 9 | interface TerminalTab { 10 | title: string; 11 | description: string; 12 | lines: string[]; 13 | } 14 | 15 | interface TabbedTerminalsSectionProps { 16 | className?: string; 17 | title: string; 18 | terminalTabs: TerminalTab[]; 19 | } 20 | 21 | export default function TabbedTerminalsSection({ 22 | className, 23 | title, 24 | terminalTabs, 25 | }: TabbedTerminalsSectionProps) { 26 | const [activeTabIndex, setActiveTabIndex] = useState(0); 27 | 28 | return ( 29 | 30 |
31 |
32 |

{title}

33 |
    34 | {terminalTabs.map((tab, i) => { 35 | const isActiveTab = i === activeTabIndex; 36 | return ( 37 |
  • setActiveTabIndex(i)} 43 | > 44 |
    45 |

    {tab.title}

    46 | 47 |
    48 | {isActiveTab && ( 49 |
    50 | 58 |

    {tab.description}

    59 |
    60 | )} 61 |
  • 62 | ); 63 | })} 64 |
65 |
66 | 67 |
68 | 75 |
76 |
77 |
78 | ); 79 | } 80 | -------------------------------------------------------------------------------- /src/components/tag-list/TagList.module.css: -------------------------------------------------------------------------------- 1 | .tagList { 2 | list-style: none; 3 | display: flex; 4 | gap: 6px; 5 | 6 | & .tag { 7 | --tag-color: var(--brand-color); 8 | 9 | &.blue { 10 | --tag-color: var(--atom-one-blue) 11 | } 12 | 13 | &.green { 14 | --tag-color: var(--atom-one-green) 15 | } 16 | 17 | &.yellow { 18 | --tag-color: var(--atom-one-yellow) 19 | } 20 | 21 | &.purple { 22 | --tag-color: var(--atom-one-purple) 23 | } 24 | 25 | &.red { 26 | --tag-color: var(--atom-one-red) 27 | } 28 | 29 | &.orange { 30 | --tag-color: var(--atom-one-orange) 31 | } 32 | 33 | &.teal { 34 | --tag-color: var(--atom-one-teal) 35 | } 36 | 37 | &.gray { 38 | --tag-color: var(--atom-one-gray) 39 | } 40 | 41 | &.white { 42 | --tag-color: var(--atom-one-white) 43 | } 44 | 45 | position: relative; 46 | padding: 0 8px 2px; 47 | border: 1px solid transparent; 48 | border-radius: var(--form-border-radius); 49 | box-shadow: var(--form-box-shadow); 50 | background-color: color-mix(in srgb, var(--tag-color) 50%, transparent); 51 | color: var(--tag-color); 52 | 53 | 54 | & .text { 55 | font-size: 12px; 56 | color: var(--white) 57 | } 58 | 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/components/tag-list/index.tsx: -------------------------------------------------------------------------------- 1 | import classNames from "classnames"; 2 | import s from "./TagList.module.css"; 3 | import { Span } from "../text"; 4 | 5 | type TagColor = 6 | | "brand" 7 | | "blue" 8 | | "green" 9 | | "yellow" 10 | | "purple" 11 | | "red" 12 | | "orange" 13 | | "teal" 14 | | "gray" 15 | | "white"; 16 | 17 | interface TagProps { 18 | name: string; 19 | color?: TagColor; 20 | } 21 | 22 | interface TagListProps { 23 | tags: TagProps[]; 24 | } 25 | 26 | export default function TagList({ tags }: TagListProps) { 27 | return ( 28 |
    29 | {tags.map(({ name, color }, index) => ( 30 | 31 | ))} 32 |
33 | ); 34 | } 35 | 36 | function Tag({ name, color }: TagProps) { 37 | return ( 38 |
  • 52 | {name} 53 |
  • 54 | ); 55 | } 56 | -------------------------------------------------------------------------------- /src/components/terminal-cards-section/TerminalCardsSection.module.css: -------------------------------------------------------------------------------- 1 | .terminalCardsSection { 2 | & .header { 3 | display: flex; 4 | justify-content: space-between; 5 | margin-bottom: 88px; 6 | & .title { 7 | max-width: 500px; 8 | } 9 | & .description { 10 | max-width: 500px; 11 | } 12 | @media(max-width: 1100px) { 13 | flex-direction: column; 14 | margin-bottom: 32px; 15 | & .title { 16 | max-width: 100%; 17 | } 18 | & .description { 19 | margin-top: 12px; 20 | max-width: 100%; 21 | } 22 | } 23 | } 24 | } 25 | 26 | .cards { 27 | --gap: 20px; 28 | --cols: 3; 29 | @media(max-width: 1100px) { 30 | --cols: 2; 31 | } 32 | @media(max-width: 768px) { 33 | --cols: 1; 34 | } 35 | list-style: none; 36 | display: flex; 37 | flex-wrap: wrap; 38 | gap: var(--gap); 39 | 40 | & > li { 41 | --flex-basis: calc((100% / var(--cols)) - (var(--gap) * (var(--cols) - 1)) / var(--cols)); 42 | flex-basis: var(--flex-basis); 43 | max-width: var(--flex-basis); 44 | & .terminalWrapper { 45 | border: 1px solid var(--gray-2); 46 | border-radius: 10px; 47 | height: 280px; 48 | overflow: hidden; 49 | & .terminal { 50 | margin-top: 40px; 51 | margin-left: 40px; 52 | @media(max-width: 1100px) { 53 | margin-top: 28px; 54 | margin-left: 28px; 55 | min-width: 100%; 56 | } 57 | } 58 | } 59 | & .title { 60 | margin-top: 32px; 61 | } 62 | & .description { 63 | margin-top: 6px; 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/components/terminal-cards-section/index.tsx: -------------------------------------------------------------------------------- 1 | import classNames from "classnames"; 2 | import SectionWrapper from "../section-wrapper"; 3 | import Terminal from "../terminal"; 4 | import { H1, H3, P } from "../text"; 5 | import s from "./TerminalCardsSection.module.css"; 6 | 7 | interface TerminalCardsSectionProps { 8 | className?: string; 9 | title: string; 10 | description?: string; 11 | cards: TerminalCard[]; 12 | } 13 | 14 | interface TerminalCard { 15 | title: string; 16 | description: string; 17 | terminal: TerminalState; 18 | } 19 | 20 | interface TerminalState { 21 | title: string; 22 | lines: string[]; 23 | } 24 | 25 | export default function TerminalCardsSection({ 26 | className, 27 | title, 28 | description, 29 | cards, 30 | }: TerminalCardsSectionProps) { 31 | return ( 32 | 33 |
    34 |

    {title}

    35 |

    {description}

    36 |
    37 |
      38 | {cards.map((card, i) => { 39 | return ( 40 |
    • 41 |
      42 | 50 |
      51 |

      {card.title}

      52 |

      {card.description}

      53 |
    • 54 | ); 55 | })} 56 |
    57 |
    58 | ); 59 | } 60 | -------------------------------------------------------------------------------- /src/components/text/Text.module.css: -------------------------------------------------------------------------------- 1 | .text { 2 | &.weightLight { 3 | font-weight: 300; 4 | & strong { 5 | font-weight: 400; 6 | } 7 | } 8 | &.weightRegular{ 9 | font-weight: 400; 10 | & strong { 11 | font-weight: 500; 12 | } 13 | } 14 | &.weightMedium { 15 | font-weight: 500; 16 | & strong { 17 | font-weight: 600; 18 | } 19 | } 20 | &p, &span, &li { 21 | font-size: 16px; 22 | line-height: 1.4em; 23 | color: var(--gray-4); 24 | & strong { 25 | color: var(--gray-5); 26 | } 27 | &.body { 28 | color: var(--gray-5); 29 | line-height: 1.6em; 30 | & strong { 31 | color: var(--gray-6); 32 | } 33 | } 34 | & code { 35 | font-size: 15px; 36 | line-height: 1.25em; 37 | } 38 | } 39 | &h1, &h2, &h3, &h4, &h5, &h6 { 40 | color: var(--gray-9); 41 | line-height: 1.2em; 42 | } 43 | &h1 { 44 | font-size: 35px; 45 | @media(max-width: 768px) { 46 | font-size: 30px; 47 | } 48 | } 49 | &h2 { 50 | font-size: 25px; 51 | @media(max-width: 768px) { 52 | font-size: 22px; 53 | } 54 | } 55 | &h3 { 56 | font-size: 18px; 57 | @media(max-width: 768px) { 58 | font-size: 16px; 59 | } 60 | } 61 | &h4 { 62 | font-size: 16px; 63 | @media(max-width: 768px) { 64 | font-size: 14px; 65 | } 66 | } 67 | &h5 { 68 | font-size: 14px; 69 | @media(max-width: 768px) { 70 | font-size: 12px; 71 | } 72 | } 73 | &h6 { 74 | font-size: 12px; 75 | @media(max-width: 768px) { 76 | font-size: 10px; 77 | } 78 | } 79 | & > code { 80 | /* Fix for https://github.com/ghostty-org/website/issues/312 81 | * In the case a user has custom fonts disabled, this ensures 82 | * that the code block is still readable. 83 | */ 84 | font-family: var(--jetbrains-mono), monospace; 85 | border: 1px solid var(--gray-3); 86 | background: var(--gray-2); 87 | border-radius: 4px; 88 | padding: 1px 6px; 89 | margin: -1px 0; 90 | color: var(--gray-9); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/components/text/font/pretendard-std-variable.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ghostty-org/website/7093f052a2f35e0247fcb828ede644ed74a749a0/src/components/text/font/pretendard-std-variable.woff2 -------------------------------------------------------------------------------- /src/components/text/index.tsx: -------------------------------------------------------------------------------- 1 | import classNames from "classnames"; 2 | import localFont from "next/font/local"; 3 | 4 | import { JetBrains_Mono } from "next/font/google"; 5 | 6 | import { forwardRef, UIEventHandler } from "react"; 7 | import s from "./Text.module.css"; 8 | 9 | // https://github.com/orioncactus/pretendard/tree/main/packages/pretendard-std 10 | export const pretendardStdVariable = localFont({ 11 | src: "./font/pretendard-std-variable.woff2", 12 | display: "auto", 13 | variable: "--pretendard-std-variable", 14 | }); 15 | 16 | export const jetbrainsMono = JetBrains_Mono({ 17 | display: "auto", 18 | weight: "400", 19 | variable: "--jetbrains-mono", 20 | subsets: ["latin"], 21 | }); 22 | 23 | interface TextProps { 24 | children?: React.ReactNode; 25 | className?: string; 26 | id?: string; 27 | as: "code" | "p" | "span" | "li" | "h1" | "h2" | "h3" | "h4" | "h5" | "h6"; 28 | font?: "display" | "body" | "code"; 29 | weight?: "light" | "regular" | "medium"; 30 | onScroll?: UIEventHandler; 31 | } 32 | 33 | const Text = forwardRef(function Text( 34 | { 35 | as: Tag, 36 | children, 37 | className, 38 | id, 39 | font = "body", 40 | weight = "light", 41 | onScroll, 42 | }: TextProps, 43 | ref: React.Ref, 44 | ) { 45 | return ( 46 | 59 | {children} 60 | 61 | ); 62 | }); 63 | export default Text; 64 | 65 | type SpecificTagTextProps = Omit; 66 | 67 | export const Code = forwardRef(function Code( 68 | props: SpecificTagTextProps, 69 | ref: React.Ref, 70 | ) { 71 | return ; 72 | }); 73 | 74 | export function LI(props: SpecificTagTextProps) { 75 | return ; 76 | } 77 | 78 | export function BodyParagraph(props: SpecificTagTextProps) { 79 | const { className, ...otherProps } = props; 80 | return ( 81 | 88 | ); 89 | } 90 | 91 | export function P(props: SpecificTagTextProps) { 92 | return ; 93 | } 94 | 95 | export function Span(props: SpecificTagTextProps) { 96 | return ; 97 | } 98 | 99 | export function H1(props: SpecificTagTextProps) { 100 | return ; 101 | } 102 | 103 | export function H2(props: SpecificTagTextProps) { 104 | return ; 105 | } 106 | 107 | export function H3(props: SpecificTagTextProps) { 108 | return ; 109 | } 110 | 111 | export function H4(props: SpecificTagTextProps) { 112 | return ; 113 | } 114 | 115 | export function H5(props: SpecificTagTextProps) { 116 | return ; 117 | } 118 | 119 | export function H6(props: SpecificTagTextProps) { 120 | return ; 121 | } 122 | -------------------------------------------------------------------------------- /src/components/vt-sequence/VTSequence.module.css: -------------------------------------------------------------------------------- 1 | .vtsequence { 2 | --sequence-border: 1px solid var(--gray-3); 3 | 4 | margin: 32px 0 calc(32px - 0.5em) 0; 5 | display: inline-flex; 6 | flex-direction: column; 7 | align-items: flex-start; 8 | font-size: 14px; 9 | 10 | & .unimplemented { 11 | /* Fix for https://github.com/ghostty-org/website/issues/312 12 | * In the case a user has custom fonts disabled, this ensures 13 | * that the code block is still readable. 14 | */ 15 | font-family: var(--jetbrains-mono), monospace; 16 | font-size: 14px; 17 | background: var(--atom-one-red); 18 | color: var(--gray-0); 19 | border-left: var(--sequence-border); 20 | border-right: var(--sequence-border); 21 | border-top: var(--sequence-border); 22 | padding: 5px 12px; 23 | display: flex; 24 | justify-content: center; 25 | & .alert { 26 | margin-right: 4px; 27 | } 28 | } 29 | 30 | & .sequence { 31 | flex-grow: 1; 32 | width: 100%; 33 | display: flex; 34 | font-family: var(--jetbrains-mono), monospace; 35 | list-style: none; 36 | 37 | & .vtelem { 38 | flex-grow: 1; 39 | text-align: center; 40 | border: var(--sequence-border); 41 | border-right-width: 0; 42 | 43 | &:last-child { 44 | border-right-width: 1px; 45 | } 46 | 47 | & dt, 48 | & dd { 49 | padding: 5px 12px; 50 | } 51 | 52 | & dt { 53 | color: var(--gray-6); 54 | background: var(--gray-1); 55 | border-bottom: 1px solid var(--gray-2); 56 | } 57 | 58 | & dd { 59 | color: var(--gray-9); 60 | } 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/components/vt-sequence/index.tsx: -------------------------------------------------------------------------------- 1 | import { useMemo } from "react"; 2 | import s from "./VTSequence.module.css"; 3 | import { OctagonAlert } from "lucide-react"; 4 | 5 | interface VTSequenceProps { 6 | sequence: string | [string]; 7 | unimplemented?: boolean; 8 | } 9 | 10 | // Draw a diagram showing the VT sequence. 11 | // 12 | // There are some special sequence elements that can be used: 13 | // 14 | // - CSI will be replaced with ESC [. 15 | // - Pn will be considered a parameter 16 | // 17 | export default function VTSequence({ 18 | sequence, 19 | unimplemented = false, 20 | }: VTSequenceProps) { 21 | const sequenceElements = useMemo(() => parseSequence(sequence), [sequence]); 22 | return ( 23 |
    24 | {unimplemented && ( 25 |
    26 | 27 | Unimplemented 28 |
    29 | )} 30 |
      31 | {sequenceElements.map(({ value, hex }, i) => ( 32 |
    1. 33 |
      34 |
      {hex ? hex : "____"}
      35 |
      {value}
      36 |
      37 |
    2. 38 | ))} 39 |
    40 |
    41 | ); 42 | } 43 | 44 | const special: Record = { 45 | BEL: 0x07, 46 | BS: 0x08, 47 | TAB: 0x09, 48 | LF: 0x0a, 49 | CR: 0x0d, 50 | ESC: 0x1b, 51 | "...": 0, 52 | }; 53 | 54 | function parseSequence(sequence: string | string[]) { 55 | let sequenceArray = typeof sequence === "string" ? [sequence] : sequence; 56 | if (sequenceArray[0] === "CSI") { 57 | sequenceArray.shift(); 58 | sequenceArray.unshift("ESC", "["); 59 | } else if (sequenceArray[0] === "OSC") { 60 | sequenceArray.shift(); 61 | sequenceArray.unshift("ESC", "]"); 62 | } 63 | 64 | return sequenceArray.map((value) => { 65 | // Pn is a param with name n. 66 | const param = value.match(/\P(\w)/)?.[1]; 67 | if (param) return { value: param }; 68 | 69 | // Use special lookup if it exists 70 | const specialChar = special[value]; 71 | if (specialChar !== undefined) { 72 | if (specialChar === 0) return { value: "..." }; 73 | const hex = specialChar.toString(16).padStart(2, "0").toUpperCase(); 74 | return { value, hex: `0x${hex}` }; 75 | } 76 | 77 | // Otherwise, encode as UTF-8 78 | const utf8Bytes = new TextEncoder().encode(value); 79 | const hex = Array.from(utf8Bytes) 80 | .map((byte) => `0x${byte.toString(16).padStart(2, "0").toUpperCase()}`) 81 | .join(" "); 82 | 83 | return { value, hex }; 84 | }); 85 | } 86 | -------------------------------------------------------------------------------- /src/layouts/nav-footer-layout/index.tsx: -------------------------------------------------------------------------------- 1 | import Footer from "@/components/footer"; 2 | import { SimpleLink } from "@/components/link"; 3 | import { NavTreeNode } from "@/components/nav-tree"; 4 | import Navbar from "@/components/navbar"; 5 | import RootLayout, { RootLayoutProps } from "../root-layout"; 6 | 7 | const navLinks: Array = [ 8 | { 9 | text: "Docs", 10 | href: "/docs", 11 | }, 12 | { 13 | text: "Discord", 14 | href: "https://discord.gg/ghostty", 15 | }, 16 | { 17 | text: "GitHub", 18 | href: "https://github.com/ghostty-org/ghostty", 19 | }, 20 | ]; 21 | 22 | type NavFooterLayoutProps = RootLayoutProps & { 23 | docsNavTree: NavTreeNode[]; 24 | }; 25 | 26 | export default function NavFooterLayout(props: NavFooterLayoutProps) { 27 | const currentYear = new Date().getFullYear(); 28 | 29 | const { children, docsNavTree, ...otherProps } = props; 30 | return ( 31 | 32 | 40 | {children} 41 |