├── .eslintrc ├── .github └── dependabot.yml ├── .gitignore ├── .prettierrc ├── .vscode └── settings.json ├── CODE_OF_CONDUCT.md ├── LICENSE ├── README.md ├── assets ├── logo.png └── opengraph.png ├── example ├── README.md ├── code-notes │ ├── annotations.mdx │ ├── deeply │ │ └── nested │ │ │ └── note.md │ ├── logo.png │ ├── markdown-features.md │ ├── mdx.mdx │ ├── note-frontmatter.md │ ├── opengraph.png │ ├── syntax-highlighting.md │ ├── untagged.md │ └── wikilinks.md ├── gatsby-config.js ├── package.json ├── src │ └── __generated__ │ │ └── gatsby-types.ts └── tsconfig.json ├── gatsby-theme-code-notes ├── .gitignore ├── .release-it.json ├── README.md ├── gatsby-browser.js ├── gatsby-config.js ├── gatsby-node.js ├── gatsby-ssr.js ├── index.js ├── package.json ├── src │ ├── components │ │ ├── BackLinks │ │ │ ├── BackLinks.tsx │ │ │ └── index.ts │ │ ├── Contents │ │ │ ├── Contents.tsx │ │ │ └── index.ts │ │ ├── Layout │ │ │ ├── Layout.tsx │ │ │ └── index.ts │ │ ├── NavItem │ │ │ ├── NavItem.tsx │ │ │ └── index.ts │ │ ├── NoteList │ │ │ ├── NoteList.tsx │ │ │ ├── SortButton.tsx │ │ │ ├── index.ts │ │ │ └── useSortableData.ts │ │ ├── NoteListItem │ │ │ ├── NoteListItem.tsx │ │ │ └── index.ts │ │ ├── NotePage │ │ │ ├── NotePage.tsx │ │ │ └── index.ts │ │ ├── NotesPage │ │ │ ├── NotesPage.tsx │ │ │ └── index.ts │ │ ├── Rough │ │ │ ├── Rough.tsx │ │ │ └── index.ts │ │ ├── Search │ │ │ ├── SearchInput.tsx │ │ │ ├── SearchProvider.tsx │ │ │ ├── SearchResults.tsx │ │ │ └── index.ts │ │ ├── Sidebar │ │ │ ├── Sidebar.tsx │ │ │ └── index.ts │ │ ├── TagDot │ │ │ ├── TagDot.tsx │ │ │ └── index.ts │ │ ├── TagList │ │ │ ├── TagList.tsx │ │ │ └── index.ts │ │ └── TagNav │ │ │ ├── TagNav.tsx │ │ │ └── index.ts │ ├── gatsby-plugin-theme-ui │ │ ├── badges.js │ │ ├── borderWidths.js │ │ ├── breakpoints.js │ │ ├── buttons.js │ │ ├── colors.js │ │ ├── components.js │ │ ├── fonts.js │ │ ├── forms.js │ │ ├── images.js │ │ ├── index.js │ │ ├── links.js │ │ ├── radii.js │ │ ├── shadows.js │ │ ├── sizes.js │ │ ├── space.js │ │ ├── styles.js │ │ ├── text.js │ │ ├── utils.js │ │ └── zIndices.js │ ├── gatsby │ │ └── wrapRootElement.js │ ├── templates │ │ ├── 404.js │ │ ├── Note.js │ │ ├── Notes.js │ │ ├── TagPage.js │ │ └── UntaggedTagPage.js │ ├── use-all-tags.ts │ ├── use-site-metadata.ts │ └── utils │ │ ├── createOpenSearch.js │ │ ├── getColourFromString.ts │ │ └── resolve-url.js └── tsconfig.json ├── package.json └── yarn.lock /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["eslint:recommended", "prettier", "plugin:react/recommended"], 3 | "plugins": ["react", "react-hooks", "prettier"], 4 | "settings": { 5 | "react": { 6 | "version": "detect" 7 | } 8 | }, 9 | "parserOptions": { 10 | "ecmaVersion": 2018, 11 | "sourceType": "module" 12 | }, 13 | "rules": { 14 | "react/display-name": 0, 15 | "react/prop-types": 0 16 | }, 17 | "env": { 18 | "browser": true, 19 | "node": true, 20 | "commonjs": true 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "npm" 9 | directory: "/" 10 | schedule: 11 | interval: "weekly" 12 | - package-ecosystem: "npm" 13 | directory: "/gatsby-theme-code-notes" 14 | schedule: 15 | interval: "weekly" 16 | - package-ecosystem: "npm" 17 | directory: "/example" 18 | schedule: 19 | interval: "weekly" 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .cache 2 | public 3 | node_modules 4 | /mtc-vectors.txt 5 | .env 6 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": false, 3 | "singleQuote": true, 4 | "tabWidth": 2, 5 | "trailingComma": "es5" 6 | } 7 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "workbench.colorCustomizations": { 3 | "activityBar.background": "#7295b1", 4 | "activityBar.activeBackground": "#7295b1", 5 | "activityBar.activeBorder": "#e8d6e0", 6 | "activityBar.foreground": "#15202b", 7 | "activityBar.inactiveForeground": "#15202b99", 8 | "activityBarBadge.background": "#e8d6e0", 9 | "activityBarBadge.foreground": "#15202b", 10 | "titleBar.activeBackground": "#557c9b", 11 | "titleBar.inactiveBackground": "#557c9b99", 12 | "titleBar.activeForeground": "#e7e7e7", 13 | "titleBar.inactiveForeground": "#e7e7e799", 14 | "statusBar.background": "#557c9b", 15 | "statusBarItem.hoverBackground": "#7295b1", 16 | "statusBar.foreground": "#e7e7e7" 17 | }, 18 | "peacock.color": "#557c9b", 19 | "typescript.tsdk": "node_modules/typescript/lib", 20 | "deno.enable": false 21 | } -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at hi@zander.wtf. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Zander Martineau 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 |

5 | 6 | gatsby-theme-code-notes is released under the MIT license. 7 | 8 | 9 | Current npm package version. 10 | 11 | 12 | Downloads per month on npm. 13 | 14 | 15 | Total downloads on npm. 16 | 17 | PRs welcome! 18 | 19 | Follow @MrMartineau 20 | 21 |

22 | 23 |

24 | Features • 25 | Installation • 26 | Usage • 27 | Demo 28 |

29 | 30 | Code Notes - A Gatsby theme for publishing code-related notes online | Product Hunt Embed 31 | 32 |
33 | 34 | > **Warning** 35 | > This work on this project is now archived. I have created an up-to-date version of this using [Eleventy](https://www.11ty.dev/) and the source code for it can be found [here](https://github.com/mrmartineau/notes.zander.wtf). If anyone would like to continue work on this theme, please let me know. 36 | 37 | ## Features 38 | 39 | - Notes can: 40 | - be written using Markdown (`.md`) or [MDX](https://mdxjs.com/) (`.mdx`) 41 | - have zero, one or many tags. See an example [here](https://code-notes-example.netlify.app/syntax-highlighting) 42 | - have associated emojis 👏 43 | - be nested in subfolders so you can organise them how you like 44 | - sketchy annotations (highlights, strike-thoughs etc). Find out more [here](https://code-notes-example.netlify.app/annotations) 45 | - Extra markdown features have also been added. Find out more [here](https://code-notes-example.netlify.app/markdown-features) 46 | - Note search powered by the super-fast [Flexsearch](https://github.com/nextapps-de/flexsearch) 47 | 48 | ## Installation 49 | 50 | ```sh 51 | mkdir my-site 52 | cd my-site 53 | yarn init 54 | 55 | # install gatsby-theme-code-notes and it's dependencies 56 | yarn add gatsby-theme-code-notes gatsby react react-dom 57 | 58 | # or 59 | 60 | npm install gatsby-theme-code-notes gatsby react react-dom 61 | ``` 62 | 63 | ### Using the Gatsby starter 64 | 65 | #### Step 1: Starter installation 66 | 67 | Source code for the starter can be found at: https://github.com/MrMartineau/gatsby-starter-code-notes 68 | 69 | ##### With `gatsby-cli`: 70 | 71 | ```sh 72 | gatsby new code-notes https://github.com/MrMartineau/gatsby-starter-code-notes 73 | ``` 74 | 75 | ##### With `git clone`: 76 | 77 | ```sh 78 | git clone git@github.com:MrMartineau/gatsby-starter-code-notes.git 79 | 80 | cd code-notes 81 | 82 | yarn 83 | ``` 84 | 85 | #### Step 2: Develop & Build 86 | 87 | Once installed or cloned locally and all packages are installed you can begin developing your site. 88 | 89 | ```sh 90 | # Run localhost 91 | yarn dev 92 | 93 | # Build your Gatsby site 94 | yarn build 95 | ``` 96 | 97 | ## Usage 98 | 99 | ### Theme Options 100 | 101 | | Key | Default value | Description | 102 | | -------------------------- | --------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | 103 | | `basePath` | `/` | Root url for all notes pages | 104 | | `contentPath` | `/content/notes` | Location of notes content | 105 | | `logo` | `''` (empty string) | Path to your site's logo. Will be used as the `src` attribute for an image | 106 | | `showDescriptionInSidebar` | `true` | Show `config.site.description` in the sidebar | 107 | | `showDate` | `false` | Show the note's modified date | 108 | | `gitRepoContentPath` | `''` | Set the location for your notes if they're hosted online, e.g. your git repo. This will show a "Edit this page" link underneath each note | 109 | | `showThemeInfo` | `true` | Show info about this Gatsby theme | 110 | | `mdxOtherwiseConfigured` | `true` | Configure `gatsby-plugin-mdx`. Note that most sites will not need to use this flag. If your site has already configured `gatsby-plugin-mdx` separately, set this flag `false`. | 111 | | `flexSearchEngineOptions` | `{ encode: 'icase', tokenize: 'forward', resolution: 9 }` | Configure FlexSearch's index method. The default value uses FlexSearch's `default` preset. Find out your other options [here](https://github.com/nextapps-de/flexsearch#presets). | 112 | | `openSearch` | `{ }` | Configure the `opensearch.xml` file contents. This file is generated during the build process. If you want to add opensearch support, ensure you set a `siteUrl` in the config. See [below](#example-usage) for more information. | 113 | 114 | ### Example usage 115 | 116 | This example overrides some of the theme defaults and shows the various options for the opensearch config. 117 | 118 | ```js 119 | // gatsby-config.js 120 | module.exports = { 121 | plugins: [ 122 | { 123 | resolve: `gatsby-theme-code-notes`, 124 | options: { 125 | basePath: '/', 126 | contentPath: '/content/notes', 127 | gitRepoContentPath: 128 | 'https://github.com/mrmartineau/gatsby-theme-code-notes/tree/master/example/code-notes/', 129 | showDescriptionInSidebar: true, 130 | showThemeInfo: false, 131 | logo: 'https://brand.zander.wtf/Avatar.png', 132 | showDate: true, 133 | 134 | // Opensearch is used to enhance the search on your site. 135 | // If you want to add it, ensure you set a `siteUrl` 136 | openSearch: { 137 | siteUrl: 'https://code-notes-example.netlify.app', // required if you want opensearch 138 | siteShortName: 'Gatsby Theme Code Notes Example', // override the default value of 'Search` 139 | siteTags: 'front-end', // optional 140 | siteContact: 'https://twitter.com/MrMartineau', // optional 141 | siteDescription: 'A Gatsby theme for storing your code-related notes', // optional 142 | }, 143 | }, 144 | }, 145 | ], 146 | } 147 | ``` 148 | 149 | Add notes to your site by creating `md` or `mdx` files inside `/content/notes`. 150 | 151 | > Note that if you've changed the default `contentPath` in the configuration, you'll want to add your markdown files in the directory specified by that path. 152 | 153 | ### Note frontmatter 154 | 155 | Frontmatter information (written in YAML) can be used to add metadata and extra information for your notes 156 | 157 | Only the `title` field is required, the rest are optional. 158 | 159 | ```yaml 160 | --- 161 | title: Note metadata 162 | emoji: 😃 163 | tags: 164 | - metadata 165 | - info 166 | link: https://zander.wtf 167 | --- 168 | 169 | ``` 170 | 171 | #### Link 172 | 173 | The `link` item is used to display a link that is related to the note itself. It will appear below the title if. 174 | 175 | #### Emoji 176 | 177 | The `emoji` frontmatter item will add an emoji beside the title on listing views and above the title on individual note pages 178 | 179 | #### Tags 180 | 181 | The `tags` array frontmatter item allows you to add as many tags to a note as you'd like. 182 | 183 | #### Dates 184 | 185 | The `modified` frontmatter item allows you set a date for your note. This means they can then be sorted (ascending & descending) when viewed in the note list pages. This was introduced in v2.0.0. 186 | 187 | The `created` frontmatter item works in a similar way, but it is not being used at the moment so it can be ommitted. 188 | 189 | ##### 1. Add new `modified` key to your YAML frontmatter 190 | 191 | This will mean that you have to update all your notes with a timestamp. 192 | 193 | ```yaml 194 | --- 195 | title: Storybook 196 | tags: 197 | - testing 198 | emoji: 📖 199 | link: 'https://storybook.js.org' 200 | created: 2020-02-27T23:02:00.000Z # this is not used by the theme at the moment 201 | modified: 2021-01-16T10:31:32.000Z 202 | 203 | # any valid ISO timestamp should work, like this: 204 | # modified: 2021-01-16 205 | --- 206 | 207 | ``` 208 | 209 | If you have many notes and want to speed up adding all those timestamps, I created an npm package ([`frontmatter-date-setter`](https://github.com/mrmartineau/frontmatter-date-setter)) to automate it based on your last git or file modification dates. 210 | 211 | Use the `frontmatter-date-setter` (or `fds`) CLI like so: (where `notes` is the directory of all your notes) 212 | 213 | ```sh 214 | fds --directory=notes --debug 215 | ``` 216 | 217 | The package does have a few issues that I'd like to improve. For example, it will convert most emojis to unicode strings, and will format other parts of your frontmatter. So take care when you run it. 218 | 219 | ##### 2. Set `showDate: true` in `gatsby-config.js` 220 | 221 | Setting this value in this plugin's config renders the interface to switch to date sorting as well as showing the date in other parts of the interface. 222 | 223 | ### Advanced usage 224 | 225 | #### PWA 226 | 227 | Turn your code notes into a PWA using [this extra config](https://github.com/mrmartineau/notes.zander.wtf/blob/master/gatsby-config.js#L20-L38). This requires `gatsby-plugin-manifest` and `gatsby-plugin-offline`. 228 | 229 | ```js 230 | // gatsby-config.js 231 | { 232 | resolve: `gatsby-plugin-manifest`, 233 | options: { 234 | name: `Zander's Code Notes`, 235 | short_name: `CodeNotes`, 236 | description: `Notes on code. My memory bank.`, 237 | start_url: `/`, 238 | background_color: `hsl(210, 38%, 95%)`, 239 | theme_color: `hsl(345, 100%, 69%)`, 240 | display: `standalone`, 241 | icon: `static/logo.png`, 242 | showDate: true, 243 | }, 244 | }, 245 | { 246 | resolve: `gatsby-plugin-offline`, 247 | options: { 248 | precachePages: [`/*`, `/tag/*`], 249 | }, 250 | }, 251 | ``` 252 | 253 | --- 254 | 255 | ## License 256 | 257 | [MIT](https://choosealicense.com/licenses/mit/) © [Zander Martineau](https://zander.wtf) 258 | 259 | > Made by Zander • [zander.wtf](https://zander.wtf) • [GitHub](https://github.com/mrmartineau/) • [Twitter](https://twitter.com/mrmartineau/) 260 | -------------------------------------------------------------------------------- /assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrmartineau/gatsby-theme-code-notes/40dfa0e379e23a536608447879aed685ecac6b68/assets/logo.png -------------------------------------------------------------------------------- /assets/opengraph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrmartineau/gatsby-theme-code-notes/40dfa0e379e23a536608447879aed685ecac6b68/assets/opengraph.png -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | # Gatsby Theme Minimal Example 2 | 3 | A usage of 4 | [gatsby-theme-minimal](https://github.com/ChristopherBiscardi/gatsby-theme-minimal) 5 | that does nothing but use the theme. As a result you will see `Error: Missing resources for /` when navigating to `localhost:8000`. To get 6 | rid of that, create a page in `src/pages/index.js`. 7 | -------------------------------------------------------------------------------- /example/code-notes/annotations.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Annotations 3 | created: 2020-07-15T11:36:18.000Z 4 | modified: 2020-07-15T12:12:28.000Z 5 | --- 6 | 7 | Note annotations are made possible using a React port of [Rough Notation](https://roughnotation.com). Annotations can be added by wrapping your text with one of the components provided in your notes. 8 | 9 | Annotation defaults have been defined, but you can ovverride them if you want. See all the options [here](https://github.com/linkstrifer/react-rough-notation#props). 10 | 11 | ```mdx 12 | I want to highlight some text 13 | ``` 14 | 15 | ## Highlight 16 | 17 | Himenaeos in a ante pretium cubilia ut nisl dictumst ultrices massa, elementum iaculis varius ad cursus vehicula torquent eros penatibus, rhoncus porttitor senectus est consequat vitae tempus mollis mus. Posuere sociis morbi suspendisse aliquam arcu convallis ac montes himenaeos, quisque iaculis eget lobortis auctor tempus feugiat fames integer, platea lacinia dignissim egestas nunc orci ad ipsum. 18 | 19 | ```mdx 20 | I want to highlight some text 21 | ``` 22 | 23 | ## Underline 24 | 25 | Habitant ipsum lorem penatibus condimentum orci curabitur ligula tempus aenean dignissim adipiscing interdum mi, quis sapien convallis nisl vel dolor lacus pharetra montes taciti, nibh venenatis consectetur dictumst maecenas cum morbi senectus curae imperdiet massa platea. 26 | 27 | ```mdx 28 | I want to underline some text 29 | ``` 30 | 31 | ## Box 32 | 33 | Eros scelerisque erat mauris euismod tincidunt euismod sit cubilia ornare fringilla, congue risus mauris nostra lobortis consequat torquent suscipit, montes in non himenaeos adipiscing aliquam. 34 | 35 | ```mdx 36 | I want to put a box around some text 37 | ``` 38 | 39 | ## Circle 40 | 41 | Ante et dis platea fusce sapien faucibus nullam, eget dictum dui mus nunc purus integer facilisis, facilisi pellentesque cubilia iaculis augue arcu. 42 | 43 | ```mdx 44 | I want to put a circle around some text 45 | ``` 46 | 47 | ## StrikeThrough 48 | 49 | Netus dolor aliquam mauris sociis imperdiet commodo taciti, odio nullam vitae vulputate ullamcorper phasellus cubilia fringilla, adipiscing in pharetra per tincidunt consectetur. 50 | 51 | ```mdx 52 | I want to strike-through some text 53 | ``` 54 | 55 | ## CrossedOff 56 | 57 | Habitant ligula pharetra elementum pellentesque lacus ante id ipsum imperdiet, fermentum rhoncus integer erat egestas blandit risus odio dictum, dui mattis nullam nisl arcu ridiculus a penatibus. 58 | 59 | ```mdx 60 | I want to cross-off some text 61 | ``` 62 | -------------------------------------------------------------------------------- /example/code-notes/deeply/nested/note.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Notes in subfolders 3 | created: 2020-06-16T11:30:46.000Z 4 | modified: 2020-06-16T11:30:46.000Z 5 | --- 6 | 7 | Notes can reside anywhere in the content directory's tree. This note is located at `/deeply/nested/note.md` 8 | -------------------------------------------------------------------------------- /example/code-notes/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrmartineau/gatsby-theme-code-notes/40dfa0e379e23a536608447879aed685ecac6b68/example/code-notes/logo.png -------------------------------------------------------------------------------- /example/code-notes/markdown-features.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Markdown features 3 | tags: 4 | - markdown 5 | created: 2020-03-20T11:04:49.000Z 6 | modified: 2020-06-22T11:55:48.000Z 7 | --- 8 | 9 | > This theme adds a few additional features to standard markdown. 10 | 11 | ## Images 12 | 13 | ![](/opengraph.png) 14 | ![](/logo.png) 15 | 16 | Embed images using the usual markdown syntax. 17 | 18 | ```md 19 | ![](/opengraph.png) 20 | ``` 21 | 22 | ## Fenced code blocks 23 | 24 | Like you use with GitHub, e.g. 25 | 26 | ```js 27 | const variable = 42 28 | ``` 29 | 30 | ### Highlighting lines in code blocks 31 | 32 | To highlight lines, wrap line with this comment before: `// highlight-start` and this after `// highlight-end`. **Ensure that these comments are not indented.** 33 | 34 | ```css 35 | .grid { 36 | // highlight-start 37 | display: grid; 38 | grid-gap: 30px; 39 | // highlight-end 40 | grid-template-columns: repeat(auto-fill, 112px); 41 | /* or this */ 42 | grid-template-columns: repeat(auto-fill, minmax(112px, 1fr)); 43 | } 44 | ``` 45 | 46 | ```css 47 | .grid { 48 | // highlight-start 49 | display: grid; 50 | grid-gap: 30px; 51 | // highlight-end 52 | grid-template-columns: repeat(auto-fill, 112px); 53 | /* or this */ 54 | grid-template-columns: repeat(auto-fill, minmax(112px, 1fr)); 55 | } 56 | ``` 57 | 58 | ## Emoji short codes 59 | 60 | Like this: :joy: :eggplant: 61 | 62 | ```md 63 | Like this: :joy: :eggplant: 64 | ``` 65 | 66 | ## Details/Summary 67 | 68 |
69 | Open example 70 | 71 | Tada! :tada: 72 | 73 | ```html 74 |
75 | Open example 76 | 77 | Tada! :tada: 78 |
79 | ``` 80 | 81 |
82 | 83 | ## Tables 84 | 85 | Tables are responsive by default. If you need to limit line length for a cell, add a `
` tag to break the lines. 86 | 87 | | Tables | Are | Cool | 88 | | ------------------- | ----------------------------------------------------------------------------------- | --------------------------------------------------------- | 89 | | This is a row | with some | content | 90 | | This is another row | with a lot more content.
Nullam netus eu fringilla turpis parturient dignissim | Velit ut mauris penatibus
turpis commodo consectetur | 91 | 92 | ## Link truncation 93 | 94 | This uses [remark-truncate-links](https://github.com/GaiAma/Coding4GaiAma/tree/master/packages/remark-truncate-links) to truncate links. e.g. https://github.com/mrmartineau/gatsby-theme-code-notes 95 | -------------------------------------------------------------------------------- /example/code-notes/mdx.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: MDX example 3 | tags: 4 | - mdx 5 | emoji: 👩‍🚀 6 | created: 2020-03-13T14:16:50.000Z 7 | modified: 2020-03-20T11:04:49.000Z 8 | --- 9 | 10 | import { Button } from 'theme-ui' 11 | 12 | This example uses MDX.. 13 | 14 | ## React component 15 | 16 | 17 | 18 | ```jsx 19 | import { Button } from 'theme-ui' 20 | 21 | ; 22 | ``` 23 | 24 | ### JSX 25 | 26 |
27 | This is JSX 28 |
29 | 30 | ```jsx 31 |
32 | This is JSX 33 |
34 | ``` 35 | 36 | ## `iframe` 37 | 38 | 54 | -------------------------------------------------------------------------------- /example/code-notes/note-frontmatter.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Note frontmatter 3 | emoji: 😃 4 | tags: 5 | - metadata 6 | link: 'https://zander.wtf' 7 | created: 2020-05-11T09:59:36.000Z 8 | modified: 2020-06-11T10:08:56.000Z 9 | --- 10 | 11 | Frontmatter information (written in YAML) can be used to add metadata and extra information for your notes 12 | 13 | This is the frontmatter for this note: 14 | 15 | ```yaml 16 | --- 17 | title: Note metadata 18 | emoji: 😃 19 | tags: 20 | - metadata 21 | link: https://zander.wtf 22 | --- 23 | 24 | ``` 25 | 26 | ## Link 27 | 28 | The `link` item is used to display a link that is related to the note itself. It will appear below the title if. 29 | 30 | ## Emoji 31 | 32 | The `emoji` frontmatter item will add an emoji beside the title on listing views and above the title on individual note pages 33 | 34 | ## Tags 35 | 36 | The `tags` array frontmatter item allows you to add as many tags to a note as you'd like. 37 | -------------------------------------------------------------------------------- /example/code-notes/opengraph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrmartineau/gatsby-theme-code-notes/40dfa0e379e23a536608447879aed685ecac6b68/example/code-notes/opengraph.png -------------------------------------------------------------------------------- /example/code-notes/syntax-highlighting.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Syntax highlighting examples 3 | tags: 4 | - html 5 | - css 6 | - javascript 7 | - Javascript 8 | - JavaScript 9 | - yaml 10 | emoji: 🌭 11 | created: 2020-03-13T14:16:50.000Z 12 | modified: 2020-06-11 13 | --- 14 | 15 | Here is some `inline code`.. 16 | 17 | ## CSS 18 | 19 | ```css 20 | .grid { 21 | display: grid; 22 | grid-gap: 30px; 23 | grid-template-columns: repeat(auto-fill, 112px); 24 | /* or this */ 25 | grid-template-columns: repeat(auto-fill, minmax(112px, 1fr)); 26 | } 27 | 28 | /* To select modern Grid browsers and IE 11 */ 29 | @supports (display: grid) { 30 | grid-gap: 20px; 31 | } 32 | ``` 33 | 34 | ### Another CSS item 35 | 36 | ```css 37 | @font-face { 38 | font-family: 'MyWebFont'; 39 | src: url('myfont.woff2') format('woff2'), url('myfont.woff') format('woff'); 40 | font-display: 'swap'; 41 | } 42 | ``` 43 | 44 | ### Another item 45 | 46 | blah blah blah 47 | blah blah blah 48 | blah blah blah 49 | 50 | #### Even more nested item 51 | 52 | blah blah blah 53 | blah blah blah 54 | blah blah blah 55 | 56 | ## JavaScript 57 | 58 | ```js 59 | import { graphql, useStaticQuery } from 'gatsby' 60 | 61 | const testVar = '22' 62 | 63 | export const useAllTags = () => { 64 | const data = useStaticQuery(graphql` 65 | { 66 | allMdx { 67 | tags: group(field: frontmatter___tags) { 68 | tag: fieldValue 69 | totalCount 70 | } 71 | } 72 | } 73 | `) 74 | 75 | return data 76 | } 77 | 78 | const three = [1, 2, 3] 79 | const doubled = three.map((item) => { 80 | return item * 2 81 | }) 82 | console.log(three === doubled, doubled) // false, [2, 4, 6] 83 | ``` 84 | 85 | ## TypeScript 86 | 87 | ```ts 88 | // Convert specified properties of T to be optional 89 | type WithOptional = Omit & Partial> 90 | 91 | type NewType = WithOptional 92 | ``` 93 | 94 | ## JSX 95 | 96 | ```jsx 97 | import React from 'react' 98 | 99 | // We again use a simple, functional component. 100 | const OurComponent = ({ query, results, clearQuery }) => ( 101 |
102 | query: {query} 103 | 104 |
    105 | {results.map((result) => ( 106 |
  • 107 | {result.title} 108 | {result.title} 109 |
  • 110 | ))} 111 |
112 |
113 | ) 114 | ``` 115 | 116 | ## HTML 117 | 118 | ```html 119 | 120 | 121 | 122 | 126 | 130 | Free Cash! 131 | 132 | ``` 133 | 134 | ## Shell 135 | 136 | ```sh 137 | git branch --merged | egrep -v "(^\*|master|develop)" 138 | ``` 139 | 140 | ## YAML 141 | 142 | ```yml 143 | version: 2 144 | 145 | references: 146 | npm_auth: &npm_auth 147 | run: 148 | name: Authenticate with registry 149 | command: echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" > ~/.npmrc 150 | 151 | jobs: 152 | build: 153 | docker: 154 | - image: circleci/node:13.1.0-stretch 155 | steps: 156 | - checkout 157 | - *npm_auth 158 | - run: 159 | name: install 160 | command: yarn install 161 | - run: 162 | name: build 163 | command: yarn build 164 | - run: 165 | name: test 166 | command: yarn test 167 | 168 | workflows: 169 | version: 2 170 | build-test: 171 | jobs: 172 | - build 173 | ``` 174 | -------------------------------------------------------------------------------- /example/code-notes/untagged.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Untagged 3 | created: 2020-02-09T21:18:32.000Z 4 | modified: 2020-03-24T20:49:43.000Z 5 | --- 6 | 7 | This is an untagged note. If there are any notes that are untagged, a new navigation item ('Untagged') will be added. 8 | -------------------------------------------------------------------------------- /example/code-notes/wikilinks.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Wiki Links 3 | emoji: ⛓ 4 | tags: 5 | - markdown 6 | created: 2021-04-02T11:04:49.000Z 7 | modified: 2021-04-02T11:55:48.000Z 8 | --- 9 | 10 | ## Link your notes 11 | 12 | Make connections between your notes by using `[[note-name]]`. This allows you to easily surf between your connected notes! 13 | 14 | ## Back Links 15 | 16 | Links all the notes that references the current note 17 | 18 | ### Check out other notes 19 | 20 | - [[markdown-features]] 21 | - [[note-frontmatter]] 22 | -------------------------------------------------------------------------------- /example/gatsby-config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | siteMetadata: { 3 | title: 'Code notes', 4 | description: 'A Gatsby theme for storing your code-related notes', 5 | keywords: [], 6 | }, 7 | plugins: [ 8 | { 9 | resolve: 'gatsby-theme-code-notes', 10 | options: { 11 | contentPath: 'code-notes', 12 | basePath: '/', 13 | gitRepoContentPath: 14 | 'https://github.com/mrmartineau/gatsby-theme-code-notes/tree/master/example/code-notes/', 15 | showDescriptionInSidebar: true, 16 | showThemeInfo: true, 17 | logo: 18 | 'https://raw.githubusercontent.com/mrmartineau/gatsby-theme-code-notes/master/assets/logo.png', 19 | openSearch: { 20 | siteShortName: `Gatsby Theme Code Notes Example`, 21 | siteUrl: 'https://code-notes-example.netlify.app', 22 | siteTags: 'front-end', 23 | siteContact: 'https://twitter.com/MrMartineau', 24 | siteDescription: 'A Gatsby theme for storing your code-related notes', 25 | }, 26 | showDate: true, 27 | }, 28 | }, 29 | ], 30 | } 31 | -------------------------------------------------------------------------------- /example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "name": "example", 4 | "version": "1.0.0", 5 | "main": "index.js", 6 | "author": { 7 | "name": "Zander Martineau", 8 | "url": "https://zander.wtf" 9 | }, 10 | "license": "MIT", 11 | "scripts": { 12 | "build": "gatsby build", 13 | "clean": "gatsby clean", 14 | "dev": "gatsby develop", 15 | "start": "gatsby serve" 16 | }, 17 | "dependencies": { 18 | "gatsby": "^3.3.1", 19 | "gatsby-theme-code-notes": "*", 20 | "react": "^17.0.2", 21 | "react-dom": "^17.0.2" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /example/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowJs": true, 4 | "esModuleInterop": true, 5 | "forceConsistentCasingInFileNames": true, 6 | "isolatedModules": true, 7 | "jsx": "preserve", 8 | "lib": ["dom", "dom.iterable", "esnext"], 9 | "module": "esnext", 10 | "moduleResolution": "node", 11 | "noEmit": true, 12 | "resolveJsonModule": true, 13 | "skipLibCheck": true, 14 | "strict": false, 15 | "target": "es5" 16 | }, 17 | "exclude": [ 18 | "node_modules", 19 | "./.storybook/**/*", 20 | "./cypress/**/*", 21 | "./static/scripts/**/*" 22 | ], 23 | "include": ["src/**/*.ts", "src/**/*.tsx", "global.d.ts"] 24 | } 25 | -------------------------------------------------------------------------------- /gatsby-theme-code-notes/.gitignore: -------------------------------------------------------------------------------- 1 | .env 2 | -------------------------------------------------------------------------------- /gatsby-theme-code-notes/.release-it.json: -------------------------------------------------------------------------------- 1 | { 2 | "git": { 3 | "commitMessage": "chore: release v${version}" 4 | }, 5 | "github": { 6 | "release": true 7 | }, 8 | "npm": { 9 | "publish": true 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /gatsby-theme-code-notes/README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 |

5 | 6 | gatsby-theme-code-notes is released under the MIT license. 7 | 8 | 9 | Current npm package version. 10 | 11 | 12 | Downloads per month on npm. 13 | 14 | 15 | Total downloads on npm. 16 | 17 | PRs welcome! 18 | 19 | Follow @MrMartineau 20 | 21 |

22 | 23 |

24 | Features • 25 | Installation • 26 | Usage • 27 | Demo 28 |

29 | 30 | Code Notes - A Gatsby theme for publishing code-related notes online | Product Hunt Embed 31 | 32 |
33 | 34 | ## Features 35 | 36 | - Notes can: 37 | - be written using Markdown (`.md`) or [MDX](https://mdxjs.com/) (`.mdx`) 38 | - have zero, one or many tags. See an example [here](https://code-notes-example.netlify.app/syntax-highlighting) 39 | - have associated emojis 👏 40 | - be nested in subfolders so you can organise them how you like 41 | - sketchy annotations (highlights, strike-thoughs etc). Find out more [here](https://code-notes-example.netlify.app/annotations) 42 | - Extra markdown features have also been added. Find out more [here](https://code-notes-example.netlify.app/markdown-features) 43 | - Note search powered by the super-fast [Flexsearch](https://github.com/nextapps-de/flexsearch) 44 | 45 | ## Installation 46 | 47 | ```sh 48 | mkdir my-site 49 | cd my-site 50 | yarn init 51 | 52 | # install gatsby-theme-code-notes and it's dependencies 53 | yarn add gatsby-theme-code-notes gatsby react react-dom 54 | 55 | # or 56 | 57 | npm install gatsby-theme-code-notes gatsby react react-dom 58 | ``` 59 | 60 | ### Using the Gatsby starter 61 | 62 | #### Step 1: Starter installation 63 | 64 | Source code for the starter can be found at: https://github.com/MrMartineau/gatsby-starter-code-notes 65 | 66 | ##### With `gatsby-cli`: 67 | 68 | ```sh 69 | gatsby new code-notes https://github.com/MrMartineau/gatsby-starter-code-notes 70 | ``` 71 | 72 | ##### With `git clone`: 73 | 74 | ```sh 75 | git clone git@github.com:MrMartineau/gatsby-starter-code-notes.git 76 | 77 | cd code-notes 78 | 79 | yarn 80 | ``` 81 | 82 | #### Step 2: Develop & Build 83 | 84 | Once installed or cloned locally and all packages are installed you can begin developing your site. 85 | 86 | ```sh 87 | # Run localhost 88 | yarn dev 89 | 90 | # Build your Gatsby site 91 | yarn build 92 | ``` 93 | 94 | ## Usage 95 | 96 | ### Theme Options 97 | 98 | | Key | Default value | Description | 99 | | -------------------------- | --------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | 100 | | `basePath` | `/` | Root url for all notes pages | 101 | | `contentPath` | `/content/notes` | Location of notes content | 102 | | `logo` | `''` (empty string) | Path to your site's logo. Will be used as the `src` attribute for an image | 103 | | `showDescriptionInSidebar` | `true` | Show `config.site.description` in the sidebar | 104 | | `showDate` | `false` | Show the note's modified date | 105 | | `gitRepoContentPath` | `''` | Set the location for your notes if they're hosted online, e.g. your git repo. This will show a "Edit this page" link underneath each note | 106 | | `showThemeInfo` | `true` | Show info about this Gatsby theme | 107 | | `mdxOtherwiseConfigured` | `true` | Configure `gatsby-plugin-mdx`. Note that most sites will not need to use this flag. If your site has already configured `gatsby-plugin-mdx` separately, set this flag `false`. | 108 | | `flexSearchEngineOptions` | `{ encode: 'icase', tokenize: 'forward', resolution: 9 }` | Configure FlexSearch's index method. The default value uses FlexSearch's `default` preset. Find out your other options [here](https://github.com/nextapps-de/flexsearch#presets). | 109 | | `openSearch` | `{ }` | Configure the `opensearch.xml` file contents. This file is generated during the build process. If you want to add opensearch support, ensure you set a `siteUrl` in the config. See [below](#example-usage) for more information. | 110 | 111 | ### Example usage 112 | 113 | This example overrides some of the theme defaults and shows the various options for the opensearch config. 114 | 115 | ```js 116 | // gatsby-config.js 117 | module.exports = { 118 | plugins: [ 119 | { 120 | resolve: `gatsby-theme-code-notes`, 121 | options: { 122 | basePath: '/', 123 | contentPath: '/content/notes', 124 | gitRepoContentPath: 125 | 'https://github.com/mrmartineau/gatsby-theme-code-notes/tree/master/example/code-notes/', 126 | showDescriptionInSidebar: true, 127 | showThemeInfo: false, 128 | logo: 'https://brand.zander.wtf/Avatar.png', 129 | showDate: true, 130 | 131 | // Opensearch is used to enhance the search on your site. 132 | // If you want to add it, ensure you set a `siteUrl` 133 | openSearch: { 134 | siteUrl: 'https://code-notes-example.netlify.app', // required if you want opensearch 135 | siteShortName: 'Gatsby Theme Code Notes Example', // override the default value of 'Search` 136 | siteTags: 'front-end', // optional 137 | siteContact: 'https://twitter.com/MrMartineau', // optional 138 | siteDescription: 'A Gatsby theme for storing your code-related notes', // optional 139 | }, 140 | }, 141 | }, 142 | ], 143 | } 144 | ``` 145 | 146 | Add notes to your site by creating `md` or `mdx` files inside `/content/notes`. 147 | 148 | > Note that if you've changed the default `contentPath` in the configuration, you'll want to add your markdown files in the directory specified by that path. 149 | 150 | ### Note frontmatter 151 | 152 | Frontmatter information (written in YAML) can be used to add metadata and extra information for your notes 153 | 154 | Only the `title` field is required, the rest are optional. 155 | 156 | ```yaml 157 | --- 158 | title: Note metadata 159 | emoji: 😃 160 | tags: 161 | - metadata 162 | - info 163 | link: https://zander.wtf 164 | --- 165 | 166 | ``` 167 | 168 | #### Link 169 | 170 | The `link` item is used to display a link that is related to the note itself. It will appear below the title if. 171 | 172 | #### Emoji 173 | 174 | The `emoji` frontmatter item will add an emoji beside the title on listing views and above the title on individual note pages 175 | 176 | #### Tags 177 | 178 | The `tags` array frontmatter item allows you to add as many tags to a note as you'd like. 179 | 180 | #### Dates 181 | 182 | The `modified` frontmatter item allows you set a date for your note. This means they can then be sorted (ascending & descending) when viewed in the note list pages. This was introduced in v2.0.0. 183 | 184 | The `created` frontmatter item works in a similar way, but it is not being used at the moment so it can be ommitted. 185 | 186 | ##### 1. Add new `modified` key to your YAML frontmatter 187 | 188 | This will mean that you have to update all your notes with a timestamp. 189 | 190 | ```yaml 191 | --- 192 | title: Storybook 193 | tags: 194 | - testing 195 | emoji: 📖 196 | link: 'https://storybook.js.org' 197 | created: 2020-02-27T23:02:00.000Z # this is not used by the theme at the moment 198 | modified: 2021-01-16T10:31:32.000Z 199 | 200 | # any valid ISO timestamp should work, like this: 201 | # modified: 2021-01-16 202 | --- 203 | 204 | ``` 205 | 206 | If you have many notes and want to speed up adding all those timestamps, I created an npm package ([`frontmatter-date-setter`](https://github.com/mrmartineau/frontmatter-date-setter)) to automate it based on your last git or file modification dates. 207 | 208 | Use the `frontmatter-date-setter` (or `fds`) CLI like so: (where `notes` is the directory of all your notes) 209 | 210 | ```sh 211 | fds --directory=notes --debug 212 | ``` 213 | 214 | The package does have a few issues that I'd like to improve. For example, it will convert most emojis to unicode strings, and will format other parts of your frontmatter. So take care when you run it. 215 | 216 | ##### 2. Set `showDate: true` in `gatsby-config.js` 217 | 218 | Setting this value in this plugin's config renders the interface to switch to date sorting as well as showing the date in other parts of the interface. 219 | 220 | ### Advanced usage 221 | 222 | #### PWA 223 | 224 | Turn your code notes into a PWA using [this extra config](https://github.com/mrmartineau/notes.zander.wtf/blob/master/gatsby-config.js#L20-L38). This requires `gatsby-plugin-manifest` and `gatsby-plugin-offline`. 225 | 226 | ```js 227 | // gatsby-config.js 228 | { 229 | resolve: `gatsby-plugin-manifest`, 230 | options: { 231 | name: `Zander's Code Notes`, 232 | short_name: `CodeNotes`, 233 | description: `Notes on code. My memory bank.`, 234 | start_url: `/`, 235 | background_color: `hsl(210, 38%, 95%)`, 236 | theme_color: `hsl(345, 100%, 69%)`, 237 | display: `standalone`, 238 | icon: `static/logo.png`, 239 | showDate: true, 240 | }, 241 | }, 242 | { 243 | resolve: `gatsby-plugin-offline`, 244 | options: { 245 | precachePages: [`/*`, `/tag/*`], 246 | }, 247 | }, 248 | ``` 249 | 250 | --- 251 | 252 | ## License 253 | 254 | [MIT](https://choosealicense.com/licenses/mit/) © [Zander Martineau](https://zander.wtf) 255 | 256 | > Made by Zander • [zander.wtf](https://zander.wtf) • [GitHub](https://github.com/mrmartineau/) • [Twitter](https://twitter.com/mrmartineau/) 257 | -------------------------------------------------------------------------------- /gatsby-theme-code-notes/gatsby-browser.js: -------------------------------------------------------------------------------- 1 | import { wrapRootElementComponent } from './src/gatsby/wrapRootElement' 2 | 3 | export const wrapRootElement = wrapRootElementComponent 4 | -------------------------------------------------------------------------------- /gatsby-theme-code-notes/gatsby-config.js: -------------------------------------------------------------------------------- 1 | const remarkSlug = require('remark-slug') 2 | const remarkEmoji = require('remark-emoji') 3 | const squeezeParagraphs = require('remark-squeeze-paragraphs') 4 | const remarkTruncateLinks = require('remark-truncate-links').remarkTruncateLinks 5 | const TextCleaner = require('text-cleaner') 6 | const unwrapImages = require('remark-unwrap-images') 7 | 8 | const clean = (string) => { 9 | return TextCleaner(string) 10 | .removeChars({ exclude: '/', replaceWith: ' ' }) 11 | .removeStopWords() 12 | .stripHtml() 13 | .condense() 14 | .toLowerCase() 15 | .valueOf() 16 | } 17 | 18 | module.exports = (options) => { 19 | const { 20 | mdxOtherwiseConfigured = true, 21 | gitRepoContentPath = '', 22 | showThemeInfo = true, 23 | showDescriptionInSidebar = true, 24 | logo = '', 25 | flexSearchEngineOptions = { 26 | encode: 'icase', 27 | tokenize: 'forward', 28 | resolution: 9, 29 | }, 30 | openSearch = {}, 31 | showDate = false, 32 | basePath = '/', 33 | } = options 34 | 35 | return { 36 | siteMetadata: { 37 | title: `Code Notes`, 38 | description: `A Gatsby theme for your code-related notes`, 39 | gitRepoContentPath, 40 | showThemeInfo, 41 | showDescriptionInSidebar, 42 | logo, 43 | openSearch, 44 | showDate, 45 | basePath, 46 | }, 47 | plugins: [ 48 | 'gatsby-plugin-typescript', 49 | `gatsby-plugin-sharp`, 50 | `gatsby-transformer-sharp`, 51 | `gatsby-plugin-catch-links`, 52 | { 53 | resolve: `gatsby-source-filesystem`, 54 | options: { 55 | path: options.contentPath || `content/notes`, 56 | name: options.contentPath || `content/notes`, 57 | }, 58 | }, 59 | mdxOtherwiseConfigured && { 60 | resolve: `gatsby-plugin-mdx`, 61 | options: { 62 | extensions: [`.md`, `.mdx`], 63 | gatsbyRemarkPlugins: [ 64 | { 65 | resolve: 'gatsby-remark-images', 66 | options: { 67 | backgroundColor: 'none', 68 | maxWidth: 900, 69 | linkImagesToOriginal: false, 70 | disableBgImage: true, 71 | wrapperStyle: `margin: 1.5rem 0;`, 72 | }, 73 | }, 74 | { 75 | resolve: 'gatsby-remark-double-brackets-link', 76 | options: { 77 | titleToURLPath: `${__dirname}/src/utils/resolve-url.js`, 78 | stripBrackets: true, 79 | }, 80 | }, 81 | ], 82 | remarkPlugins: [ 83 | remarkSlug, 84 | remarkEmoji, 85 | squeezeParagraphs, 86 | [remarkTruncateLinks, { style: 'smart' }], 87 | unwrapImages, 88 | ], 89 | }, 90 | }, 91 | `gatsby-plugin-redirects`, 92 | `gatsby-plugin-react-helmet`, 93 | `gatsby-plugin-theme-ui`, 94 | { 95 | resolve: 'gatsby-plugin-local-search', 96 | options: { 97 | name: 'notes', 98 | engine: 'flexsearch', 99 | engineOptions: flexSearchEngineOptions, 100 | query: `{ 101 | allNotes: allMdx { 102 | edges { 103 | node { 104 | id 105 | frontmatter { 106 | title 107 | emoji 108 | tags 109 | } 110 | fields { 111 | slug 112 | } 113 | rawBody 114 | } 115 | } 116 | } 117 | }`, 118 | ref: 'id', 119 | index: ['title', 'body', 'tagsJoint'], 120 | store: ['id', 'slug', 'title', 'body', 'tags', 'emoji'], 121 | normalizer: ({ data }) => 122 | data.allNotes.edges.map(({ node }) => { 123 | return { 124 | id: node.id, 125 | slug: node.fields.slug, 126 | title: node.frontmatter.title, 127 | body: clean(node.rawBody), 128 | emoji: node.frontmatter.emoji, 129 | tags: node.frontmatter.tags, 130 | tagsJoint: 131 | node.frontmatter.tags && 132 | node.frontmatter.tags.join().replace(/,/gi, ' '), 133 | } 134 | }), 135 | }, 136 | }, 137 | { 138 | resolve: `gatsby-transformer-markdown-references`, 139 | options: { 140 | types: ['Mdx'], 141 | }, 142 | }, 143 | ].filter(Boolean), 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /gatsby-theme-code-notes/gatsby-node.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const fs = require('fs') 3 | const mkdirp = require('mkdirp') 4 | const slugify = require('@alexcarpenter/slugify') 5 | const { createFilePath } = require(`gatsby-source-filesystem`) 6 | const createOpenSearchFile = require('./src/utils/createOpenSearch') 7 | 8 | const DEFAULT_BASE_PATH = '/' 9 | 10 | const getBasePath = (bp = DEFAULT_BASE_PATH) => { 11 | if (bp === '' || bp === '.' || bp === './') { 12 | return DEFAULT_BASE_PATH 13 | } else { 14 | return bp 15 | } 16 | } 17 | 18 | exports.onPreBootstrap = ({ store, reporter }, themeOptions) => { 19 | const { program } = store.getState() 20 | const contentPath = themeOptions.contentPath || `content/notes` 21 | const dirs = [path.join(program.directory, contentPath)] 22 | dirs.forEach((dir) => { 23 | if (!fs.existsSync(dir)) { 24 | reporter.log(`Initializing ${dir} directory`) 25 | mkdirp.sync(dir) 26 | } 27 | }) 28 | } 29 | 30 | exports.onPreExtractQueries = ({ reporter }, themeOptions) => { 31 | if (themeOptions.openSearch && themeOptions.openSearch.siteUrl) { 32 | const filePath = path.join('public', 'opensearch.xml') 33 | fs.writeFile( 34 | filePath, 35 | createOpenSearchFile(themeOptions.openSearch), 36 | (err) => { 37 | if (err) throw err 38 | reporter.log('The opensearch.xml file has been created') 39 | } 40 | ) 41 | } 42 | } 43 | 44 | exports.createPages = async ({ graphql, actions }, options) => { 45 | const { createPage } = actions 46 | const basePath = getBasePath(options.basePath) 47 | 48 | const mdxDocs = await graphql( 49 | ` 50 | { 51 | allNotes: allMdx { 52 | edges { 53 | node { 54 | id 55 | frontmatter { 56 | title 57 | tags 58 | } 59 | fields { 60 | slug 61 | } 62 | } 63 | } 64 | 65 | tags: group(field: frontmatter___tags) { 66 | tag: fieldValue 67 | totalCount 68 | } 69 | } 70 | 71 | untagged: allMdx(filter: { frontmatter: { tags: { eq: null } } }) { 72 | edges { 73 | node { 74 | id 75 | } 76 | } 77 | } 78 | } 79 | ` 80 | ) 81 | 82 | if (mdxDocs.errors) { 83 | throw mdxDocs.errors 84 | } 85 | 86 | const { allNotes, untagged } = mdxDocs.data 87 | 88 | const globalTagsList = allNotes.tags 89 | const notesData = allNotes.edges 90 | const hasUntagged = !!untagged.edges.length 91 | const slugifiedTags = globalTagsList.map((item) => { 92 | return { 93 | ...item, 94 | slug: `/${slugify(item.tag)}`, 95 | path: path.join(basePath, 'tag', slugify(item.tag)), 96 | } 97 | }) 98 | 99 | // Create notes pages 100 | notesData.forEach((note, index) => { 101 | const previous = 102 | index === notesData.length - 1 ? null : notesData[index + 1].node 103 | const next = index === 0 ? null : notesData[index - 1].node 104 | const slug = slugify(note.node.fields.slug) 105 | createPage({ 106 | path: slug, 107 | component: path.join(__dirname, './src/templates', 'Note.js'), 108 | context: { 109 | id: note.node.id, 110 | previous, 111 | next, 112 | hasUntagged, 113 | basePath, 114 | tags: slugifiedTags, 115 | }, 116 | }) 117 | }) 118 | 119 | // Create the notes landing page 120 | createPage({ 121 | path: basePath, 122 | component: path.join(__dirname, './src/templates', 'Notes.js'), 123 | context: { 124 | tags: slugifiedTags, 125 | basePath, 126 | hasUntagged, 127 | }, 128 | }) 129 | 130 | // Create tag pages 131 | slugifiedTags.forEach((item) => { 132 | createPage({ 133 | path: item.path, 134 | component: path.join(__dirname, './src/templates', 'TagPage.js'), 135 | context: { 136 | tag: item.tag, 137 | tags: slugifiedTags, 138 | hasUntagged, 139 | basePath, 140 | }, 141 | }) 142 | }) 143 | 144 | if (hasUntagged) { 145 | createPage({ 146 | path: path.join(basePath, 'tag', 'untagged'), 147 | component: path.join(__dirname, './src/templates', 'UntaggedTagPage.js'), 148 | context: { 149 | tag: 'untagged', 150 | tags: slugifiedTags, 151 | hasUntagged, 152 | basePath, 153 | }, 154 | }) 155 | } 156 | 157 | createPage({ 158 | path: path.join('404'), 159 | component: path.join(__dirname, './src/templates', '404.js'), 160 | context: { 161 | tags: slugifiedTags, 162 | hasUntagged, 163 | basePath, 164 | }, 165 | }) 166 | } 167 | 168 | exports.onCreateNode = async ({ node, actions, getNode }, options) => { 169 | const { createNodeField } = actions 170 | 171 | if (node.internal.type === `Mdx`) { 172 | const slug = createFilePath({ 173 | node, 174 | getNode, 175 | trailingSlash: false, 176 | }) 177 | const pathSlug = path.join(getBasePath(options.basePath), slug) 178 | createNodeField({ 179 | name: `slug`, 180 | node, 181 | value: pathSlug, 182 | }) 183 | } 184 | } 185 | 186 | exports.createSchemaCustomization = ({ actions }) => { 187 | const { createTypes } = actions 188 | const typeDefs = ` 189 | type OpenSearch { 190 | siteShortName: String 191 | siteUrl: String 192 | siteTags: String 193 | siteContact: String 194 | siteDescription: String 195 | } 196 | type SiteSiteMetadata { 197 | title: String! 198 | description: String! 199 | gitRepoContentPath: String 200 | showThemeInfo: Boolean 201 | showDescriptionInSidebar: Boolean 202 | logo: String 203 | openSearch: OpenSearch 204 | showDate: Boolean 205 | } 206 | ` 207 | createTypes(typeDefs) 208 | } 209 | -------------------------------------------------------------------------------- /gatsby-theme-code-notes/gatsby-ssr.js: -------------------------------------------------------------------------------- 1 | import { wrapRootElementComponent } from './src/gatsby/wrapRootElement' 2 | 3 | export const wrapRootElement = wrapRootElementComponent 4 | -------------------------------------------------------------------------------- /gatsby-theme-code-notes/index.js: -------------------------------------------------------------------------------- 1 | // noop 2 | -------------------------------------------------------------------------------- /gatsby-theme-code-notes/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gatsby-theme-code-notes", 3 | "description": "A Gatsby theme for publishing code-related notes and snippets", 4 | "version": "2.3.0", 5 | "main": "index.js", 6 | "author": { 7 | "name": "Zander Martineau", 8 | "url": "https://zander.wtf" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/mrmartineau/gatsby-theme-code-notes" 13 | }, 14 | "bugs": { 15 | "url": "https://github.com/mrmartineau/gatsby-theme-code-notes/issues" 16 | }, 17 | "scripts": { 18 | "build": "gatsby build", 19 | "clean": "gatsby clean", 20 | "dev": "gatsby develop", 21 | "release": "dotenv release-it" 22 | }, 23 | "license": "MIT", 24 | "keywords": [ 25 | "gatsby", 26 | "gatsby-plugin", 27 | "gatsby-theme", 28 | "markdown", 29 | "mdx" 30 | ], 31 | "peerDependencies": { 32 | "gatsby": "^2.28.0 || ^3.0.0", 33 | "react": "^16.13.0 || ^17.0.0", 34 | "react-dom": "^16.13.0 || ^17.0.0" 35 | }, 36 | "files": [ 37 | "src", 38 | "gatsby-*.js", 39 | "tsconfig.json" 40 | ], 41 | "dependencies": { 42 | "@alexcarpenter/slugify": "^1.0.6", 43 | "@hookit/local-storage": "^0.1.0", 44 | "@mdx-js/mdx": "^1.6.5", 45 | "@mdx-js/react": "^1.6.5", 46 | "@theme-ui/presets": "0.7.3", 47 | "@theme-ui/prism": "0.7.3", 48 | "color-hash": "^2.0.0", 49 | "copee": "^1.0.6", 50 | "gatsby-core-utils": "^2.3.0", 51 | "gatsby-plugin-catch-links": "^3.3.0", 52 | "gatsby-plugin-local-search": "^2.0.1", 53 | "gatsby-plugin-mdx": "^2.3.0", 54 | "gatsby-plugin-meta-redirect": "^1.1.1", 55 | "gatsby-plugin-og-image": "^0.0.1", 56 | "gatsby-plugin-react-helmet": "^4.3.0", 57 | "gatsby-plugin-redirects": "^1.0.0", 58 | "gatsby-plugin-sharp": "^3.3.1", 59 | "gatsby-plugin-theme-ui": "0.7.3", 60 | "gatsby-plugin-typescript": "^3.3.0", 61 | "gatsby-remark-double-brackets-link": "^0.1.8", 62 | "gatsby-remark-images": "^5.0.0", 63 | "gatsby-source-filesystem": "^3.3.0", 64 | "gatsby-transformer-markdown-references": "^0.1.5", 65 | "gatsby-transformer-sharp": "^3.3.0", 66 | "lodash": "^4.17.21", 67 | "mkdirp": "^1.0.3", 68 | "querystring": "^0.2.1", 69 | "react-helmet": "^6.1.0", 70 | "react-icons": "^4.2.0", 71 | "react-rough-notation": "^1.0.0", 72 | "react-use-flexsearch": "^0.1.1", 73 | "remark-emoji": "^2.2.0", 74 | "remark-github": "^10.0.1", 75 | "remark-slug": "^6.0.0", 76 | "remark-squeeze-paragraphs": "^4.0.0", 77 | "remark-truncate-links": "^1.0.3", 78 | "remark-unwrap-images": "^2.0.0", 79 | "text-cleaner": "^1.2.1", 80 | "theme-ui": "0.7.3", 81 | "typescript": "^4.2.4" 82 | }, 83 | "devDependencies": { 84 | "@types/node": "^14.14.41", 85 | "@types/react": "^17.0.3", 86 | "@types/react-dom": "^17.0.3", 87 | "@types/react-helmet": "^6.1.1", 88 | "dotenv-cli": "^4.0.0", 89 | "eslint-plugin-react-hooks": "^4.2.0", 90 | "gatsby": "^3.3.1", 91 | "husky": "^6.0.0", 92 | "lint-staged": "^10.5.4", 93 | "prettier": "^2.2.1", 94 | "react": "^17.0.2", 95 | "react-dom": "^17.0.2", 96 | "release-it": "^14.6.1" 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /gatsby-theme-code-notes/src/components/BackLinks/BackLinks.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Box, Link } from 'theme-ui' 3 | 4 | interface IProps { 5 | references: { 6 | frontmatter: { 7 | title: string 8 | } 9 | slug: string 10 | }[] 11 | } 12 | 13 | export const BackLinks: React.FC = ({ references }) => ( 14 | 15 | 19 | Back Links ({references.length}) 20 | 21 | 22 | {references.map((ref) => { 23 | return ( 24 |
  • 25 | 26 | {ref.frontmatter.title} 27 | 28 |
  • 29 | ) 30 | })} 31 |
    32 |
    33 | ) 34 | -------------------------------------------------------------------------------- /gatsby-theme-code-notes/src/components/BackLinks/index.ts: -------------------------------------------------------------------------------- 1 | export { BackLinks } from './BackLinks' 2 | -------------------------------------------------------------------------------- /gatsby-theme-code-notes/src/components/Contents/Contents.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Link, Box } from 'theme-ui' 3 | 4 | const ContentsList = ({ items }) => { 5 | return ( 6 | 7 | {items.map((item) => { 8 | return 9 | })} 10 | 11 | ) 12 | } 13 | 14 | const ContentsItem = ({ item }) => ( 15 |
  • 16 | {item.title} 17 | {item.items && item.items.length && ( 18 | 19 | )} 20 |
  • 21 | ) 22 | 23 | export const Contents = ({ toc }) => { 24 | if (!toc.items) { 25 | return null 26 | } 27 | 28 | return ( 29 | 30 | 34 | On this page 35 | 36 | 37 | 38 | ) 39 | } 40 | -------------------------------------------------------------------------------- /gatsby-theme-code-notes/src/components/Contents/index.ts: -------------------------------------------------------------------------------- 1 | export { Contents } from './Contents' 2 | -------------------------------------------------------------------------------- /gatsby-theme-code-notes/src/components/Layout/Layout.tsx: -------------------------------------------------------------------------------- 1 | /** @jsx jsx */ 2 | import { Fragment, FunctionComponent, useState, useContext } from 'react' 3 | import { Global } from '@emotion/react' 4 | import { 5 | jsx, 6 | css, 7 | Box, 8 | useThemeUI, 9 | MenuButton, 10 | Link, 11 | Text, 12 | Image, 13 | useColorMode, 14 | IconButton, 15 | Container, 16 | Flex, 17 | } from 'theme-ui' 18 | import { Helmet } from 'react-helmet' 19 | import { TagNav } from '../TagNav' 20 | import { useSiteMetadata } from '../../use-site-metadata' 21 | import { SearchContext, SearchInput, SearchResults } from '../Search' 22 | 23 | interface LayoutProps { 24 | activeTag?: string 25 | path?: string 26 | basePath?: string 27 | title?: string 28 | hasUntagged?: boolean 29 | tags: any 30 | } 31 | 32 | export const Layout: FunctionComponent = ({ 33 | activeTag, 34 | path, 35 | basePath, 36 | hasUntagged, 37 | title, 38 | tags, 39 | children, 40 | }) => { 41 | const { theme } = useThemeUI() 42 | const { 43 | showThemeInfo, 44 | showDescriptionInSidebar, 45 | description, 46 | logo, 47 | openSearch, 48 | } = useSiteMetadata() 49 | const [navOpen, setNavOpen] = useState(false) 50 | const [colorMode, setColorMode] = useColorMode() 51 | const { query } = useContext(SearchContext) 52 | 53 | return ( 54 | 55 | 60 | 61 | 62 | {title} 63 | 64 | {openSearch && openSearch.siteUrl && ( 65 | 71 | )} 72 | 73 | 101 | 102 | 125 | 132 | 133 | setNavOpen(!navOpen)} 136 | sx={{ 137 | display: ['block', 'none'], 138 | mr: 3, 139 | }} 140 | /> 141 | 142 | 147 | 148 | 149 | 150 | 151 | {query ? : children} 152 | 153 | {showThemeInfo && ( 154 | 155 | Want to make your own site like this? 156 |
    157 | Try{' '} 158 | 159 | gatsby-theme-code-notes 160 | {' '} 161 | by Zander Martineau. 162 |
    163 | )} 164 |
    165 |
    166 | 167 | 181 | 182 | {!!logo && ( 183 | 184 | logo 185 | 186 | )} 187 | {showDescriptionInSidebar && description && ( 188 | 189 | 196 | {description} 197 | 198 | 199 | )} 200 | 201 | 202 | 209 | 219 | { 221 | setColorMode(colorMode === 'light' ? 'dark' : 'light') 222 | }} 223 | aria-label="Toggle dark mode" 224 | sx={{ 225 | color: 'text', 226 | zIndex: 11, 227 | p: 0, 228 | display: 'block', 229 | mx: 'auto', 230 | transition: 'all 200ms ease-in-out ', 231 | cursor: 'pointer', 232 | '&:hover': { 233 | color: 'primary', 234 | }, 235 | }} 236 | > 237 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 |
    252 | ) 253 | } 254 | -------------------------------------------------------------------------------- /gatsby-theme-code-notes/src/components/Layout/index.ts: -------------------------------------------------------------------------------- 1 | export { Layout } from './Layout' 2 | -------------------------------------------------------------------------------- /gatsby-theme-code-notes/src/components/NavItem/NavItem.tsx: -------------------------------------------------------------------------------- 1 | import React, { FunctionComponent } from 'react' 2 | import { NavLink, Link } from 'theme-ui' 3 | import { Link as GatsbyLink } from 'gatsby' 4 | 5 | interface NavItemProps { 6 | active?: boolean 7 | to: string 8 | } 9 | 10 | export const NavItem: FunctionComponent = ({ 11 | active, 12 | to, 13 | children, 14 | }) => ( 15 | 23 | {children} 24 | 25 | ) 26 | -------------------------------------------------------------------------------- /gatsby-theme-code-notes/src/components/NavItem/index.ts: -------------------------------------------------------------------------------- 1 | export { NavItem } from './NavItem' 2 | -------------------------------------------------------------------------------- /gatsby-theme-code-notes/src/components/NoteList/NoteList.tsx: -------------------------------------------------------------------------------- 1 | import React, { Fragment, FunctionComponent } from 'react' 2 | import { Flex } from 'theme-ui' 3 | import { NoteListItem } from '../NoteListItem' 4 | import { SortButton } from './SortButton' 5 | import { useSortableData } from './useSortableData' 6 | import slugify from '@alexcarpenter/slugify' 7 | 8 | interface NoteListProps { 9 | notes: any[] 10 | } 11 | 12 | export const NoteList: FunctionComponent = ({ notes }) => { 13 | const { items, requestSort, sortConfig } = useSortableData(notes, { 14 | key: 'title', 15 | direction: 'ascending', 16 | }) 17 | 18 | return ( 19 | 20 | 27 | 32 | A-Z 33 | 34 | 39 | Date 40 | 41 | 42 | 43 | {items.map(({ node }) => { 44 | const { 45 | title, 46 | tags, 47 | emoji, 48 | modified, 49 | modifiedTimestamp, 50 | } = node.frontmatter 51 | const slug = `/${slugify(node.fields.slug)}` 52 | return ( 53 | 62 | ) 63 | })} 64 | 65 | ) 66 | } 67 | -------------------------------------------------------------------------------- /gatsby-theme-code-notes/src/components/NoteList/SortButton.tsx: -------------------------------------------------------------------------------- 1 | import React, { Fragment } from 'react' 2 | import { Button } from 'theme-ui' 3 | import { BiSortDown, BiSortUp } from 'react-icons/bi' 4 | 5 | const getClassNamesFor = (name, sortConfig) => { 6 | if (!sortConfig) { 7 | return 8 | } 9 | return sortConfig.key === name ? `active ${sortConfig.direction}` : undefined 10 | } 11 | 12 | export const SortButton = ({ requestSort, sortKey, sortConfig, children }) => ( 13 | 42 | ) 43 | -------------------------------------------------------------------------------- /gatsby-theme-code-notes/src/components/NoteList/index.ts: -------------------------------------------------------------------------------- 1 | export { NoteList } from './NoteList' 2 | -------------------------------------------------------------------------------- /gatsby-theme-code-notes/src/components/NoteList/useSortableData.ts: -------------------------------------------------------------------------------- 1 | import { useLocalStorage } from '@hookit/local-storage' 2 | import { useMemo } from 'react' 3 | 4 | export const useSortableData = (items, config = null) => { 5 | const [sortConfig, setSortConfig] = useLocalStorage( 6 | 'codeNotesSortConfig', 7 | config 8 | ) 9 | 10 | const sortedItems = useMemo(() => { 11 | let sortableItems = [...items] 12 | if (sortConfig !== null) { 13 | sortableItems.sort((a, b) => { 14 | if ( 15 | a.node.frontmatter[sortConfig.key]?.toLowerCase() < 16 | b.node.frontmatter[sortConfig.key]?.toLowerCase() 17 | ) { 18 | return sortConfig.direction === 'ascending' ? -1 : 1 19 | } 20 | if ( 21 | a.node.frontmatter[sortConfig.key]?.toLowerCase() > 22 | b.node.frontmatter[sortConfig.key]?.toLowerCase() 23 | ) { 24 | return sortConfig.direction === 'ascending' ? 1 : -1 25 | } 26 | return 0 27 | }) 28 | } 29 | return sortableItems 30 | }, [items, sortConfig]) 31 | 32 | const requestSort = (key) => { 33 | let direction = 'ascending' 34 | if ( 35 | sortConfig && 36 | sortConfig.key === key && 37 | sortConfig.direction === 'ascending' 38 | ) { 39 | direction = 'descending' 40 | } 41 | setSortConfig({ key, direction }) 42 | } 43 | 44 | return { items: sortedItems, requestSort, sortConfig } 45 | } 46 | -------------------------------------------------------------------------------- /gatsby-theme-code-notes/src/components/NoteListItem/NoteListItem.tsx: -------------------------------------------------------------------------------- 1 | /** @jsx jsx */ 2 | import { FunctionComponent } from 'react' 3 | import { TagList } from '../TagList' 4 | import { Link as GatsbyLink } from 'gatsby' 5 | import { jsx, Heading, Flex, Box, Link, Text } from 'theme-ui' 6 | import { useSiteMetadata } from '../../use-site-metadata' 7 | 8 | interface NoteListItemProps { 9 | slug: string 10 | title: string 11 | emoji?: string 12 | tags: string[] 13 | onClick?: () => void 14 | dateModified: string 15 | modifiedTimestamp: string 16 | } 17 | 18 | export const NoteListItem: FunctionComponent = ({ 19 | slug, 20 | title, 21 | tags, 22 | emoji, 23 | onClick, 24 | dateModified, 25 | modifiedTimestamp, 26 | }) => { 27 | const { showDate } = useSiteMetadata() 28 | const noteEmoji = emoji ?? '🗒' 29 | return ( 30 | 31 | 42 | 47 | 48 | {noteEmoji} 49 | 50 | {title} 51 | 52 | 57 | {tags && ( 58 | 67 | )} 68 | {showDate && dateModified && modifiedTimestamp && ( 69 | 80 | {dateModified} 81 | 82 | )} 83 | 84 | 85 | 86 | ) 87 | } 88 | -------------------------------------------------------------------------------- /gatsby-theme-code-notes/src/components/NoteListItem/index.ts: -------------------------------------------------------------------------------- 1 | export { NoteListItem } from './NoteListItem' 2 | -------------------------------------------------------------------------------- /gatsby-theme-code-notes/src/components/NotePage/NotePage.tsx: -------------------------------------------------------------------------------- 1 | /** @jsx jsx */ 2 | import { FunctionComponent, Fragment, useEffect, useState } from 'react' 3 | import { jsx, Box, Flex, Heading, Link, Text } from 'theme-ui' 4 | import { MDXRenderer } from 'gatsby-plugin-mdx' 5 | import { GoCalendar, GoLink, GoTag } from 'react-icons/go' 6 | import { useSiteMetadata } from '../../use-site-metadata' 7 | import { Contents } from '../Contents' 8 | import { Layout } from '../Layout' 9 | import { TagList } from '../TagList' 10 | import { BackLinks } from '../BackLinks' 11 | 12 | interface NotePageProps { 13 | data: { 14 | mdx: { 15 | frontmatter: { 16 | tags: string[] 17 | title: string 18 | emoji: string 19 | link: string 20 | modified: string 21 | modifiedTimestamp: string 22 | } 23 | references: { 24 | frontmatter: { 25 | title: string 26 | } 27 | slug: string 28 | }[] 29 | body: any 30 | parent: { 31 | relativePath: string 32 | } 33 | tableOfContents: any 34 | } 35 | } 36 | pageContext: { 37 | id: string 38 | previous: boolean 39 | next: boolean 40 | hasUntagged: boolean 41 | basePath?: string 42 | tags: any 43 | } 44 | location: { 45 | pathname: string 46 | } 47 | } 48 | 49 | export const NotePage: FunctionComponent = ({ 50 | data, 51 | pageContext, 52 | location, 53 | }) => { 54 | if (!data) { 55 | return null 56 | } 57 | const { showDate } = useSiteMetadata() 58 | const { 59 | frontmatter: { title, tags, emoji, link, modified, modifiedTimestamp }, 60 | body, 61 | parent: { relativePath }, 62 | tableOfContents, 63 | } = data.mdx 64 | 65 | const { gitRepoContentPath } = useSiteMetadata() 66 | const showMetadata = !!(link || showDate) 67 | const [shortenedLink, setShortenedLink] = useState(link) 68 | 69 | useEffect(() => { 70 | if (link) { 71 | if ('URL' in window) { 72 | const { hostname, pathname } = new URL(link) 73 | setShortenedLink(`${hostname}${pathname}`) 74 | } 75 | } 76 | }, [link]) 77 | 78 | return ( 79 | 86 |
    87 | 96 | {emoji && ( 97 | 104 | {emoji} 105 | 106 | )} 107 | 108 | 109 | {title} 110 | 111 | 112 | {showMetadata && ( 113 | 119 | {link && ( 120 | 121 | 129 | 139 | {shortenedLink} 140 | 141 | 142 | )} 143 | {modifiedTimestamp && modified && ( 144 | 158 | )} 159 | 160 | )} 161 | 162 | {tags && ( 163 | 164 | 172 | 173 | 174 | )} 175 | 176 | 177 | {!!data.mdx.references.length && ( 178 | 179 | )} 180 | 181 | 182 | 183 | {body} 184 | 185 | 193 | {gitRepoContentPath && ( 194 | 198 | Edit this page 199 | 200 | )} 201 | 202 |
    203 |
    204 | ) 205 | } 206 | -------------------------------------------------------------------------------- /gatsby-theme-code-notes/src/components/NotePage/index.ts: -------------------------------------------------------------------------------- 1 | export { NotePage } from './NotePage' 2 | -------------------------------------------------------------------------------- /gatsby-theme-code-notes/src/components/NotesPage/NotesPage.tsx: -------------------------------------------------------------------------------- 1 | /** @jsx jsx */ 2 | import { jsx, Heading } from 'theme-ui' 3 | import { Fragment } from 'react' 4 | import { NoteList } from '../NoteList' 5 | import { Layout } from '../Layout' 6 | import { useSiteMetadata } from '../../use-site-metadata' 7 | import { TagDot } from '../TagDot' 8 | 9 | export const NotesPage = ({ data, pageContext, location }) => { 10 | const notes = data.allMdx.edges 11 | const { title } = useSiteMetadata() 12 | const siteTitle = title 13 | 14 | return ( 15 | 23 | {pageContext.tag && ( 24 | 25 | {pageContext.tag !== 'untagged' ? ( 26 | 27 | {pageContext.tag} 28 | 29 | ) : ( 30 | Untagged Notes 31 | )} 32 | 33 | )} 34 | 35 | 36 | ) 37 | } 38 | -------------------------------------------------------------------------------- /gatsby-theme-code-notes/src/components/NotesPage/index.ts: -------------------------------------------------------------------------------- 1 | export { NotesPage } from './NotesPage' 2 | -------------------------------------------------------------------------------- /gatsby-theme-code-notes/src/components/Rough/Rough.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { RoughNotation } from 'react-rough-notation' 3 | import { useThemeUI } from 'theme-ui' 4 | 5 | export const Underline = (props) => ( 6 | 7 | ) 8 | export const Box = (props) => { 9 | const { theme } = useThemeUI() 10 | return ( 11 | 18 | ) 19 | } 20 | export const Circle = (props) => { 21 | const { theme } = useThemeUI() 22 | return ( 23 | 30 | ) 31 | } 32 | export const Highlight = (props) => { 33 | const { theme } = useThemeUI() 34 | return ( 35 | 42 | ) 43 | } 44 | export const StrikeThrough = (props) => { 45 | const { theme } = useThemeUI() 46 | return ( 47 | 54 | ) 55 | } 56 | export const CrossedOff = (props) => { 57 | const { theme } = useThemeUI() 58 | return ( 59 | 66 | ) 67 | } 68 | -------------------------------------------------------------------------------- /gatsby-theme-code-notes/src/components/Rough/index.ts: -------------------------------------------------------------------------------- 1 | export { 2 | Underline, 3 | Box, 4 | Circle, 5 | Highlight, 6 | StrikeThrough, 7 | CrossedOff, 8 | } from './Rough' 9 | -------------------------------------------------------------------------------- /gatsby-theme-code-notes/src/components/Search/SearchInput.tsx: -------------------------------------------------------------------------------- 1 | /** @jsx jsx */ 2 | import { useContext, useEffect, useRef } from 'react' 3 | import { jsx, Input, Box } from 'theme-ui' 4 | import querystring from 'querystring' 5 | import { SearchContext } from './SearchProvider' 6 | import { GoSearch } from 'react-icons/go' 7 | 8 | export const SearchInput = () => { 9 | const { query, setQuery } = useContext(SearchContext) 10 | const inputEl = useRef(null) 11 | 12 | useEffect(() => { 13 | const searchParts = location.search.split('?') 14 | const searchParams = querystring.parse(searchParts[1]) 15 | if (searchParams.search) { 16 | setQuery(searchParams.search) 17 | } 18 | }, []) 19 | 20 | useEffect(() => { 21 | window.addEventListener('keydown', (event) => { 22 | if (event.isComposing || event.keyCode === 191) { 23 | if (inputEl && inputEl.current) { 24 | event.preventDefault() 25 | inputEl.current.focus() 26 | } 27 | } 28 | }) 29 | 30 | return () => { 31 | window.removeEventListener('keydown', () => {}) 32 | } 33 | }, []) 34 | 35 | return ( 36 | 41 | 51 | { 54 | setQuery(event.target.value) 55 | window.history.pushState({}, '', `?search=${event.target.value}`) 56 | }} 57 | aria-label="Search" 58 | placeholder="Search notes" 59 | ref={inputEl} 60 | /> 61 | 82 | / 83 | 84 | 85 | ) 86 | } 87 | -------------------------------------------------------------------------------- /gatsby-theme-code-notes/src/components/Search/SearchProvider.tsx: -------------------------------------------------------------------------------- 1 | import React, { createContext, useState } from 'react' 2 | 3 | interface SearchContextInterface { 4 | query: string 5 | setQuery: (value: string) => void 6 | } 7 | 8 | export const SearchContext = createContext( 9 | {} as SearchContextInterface 10 | ) 11 | 12 | export const SearchProvider = ({ children }) => { 13 | const [query, setQuery] = useState('') 14 | return ( 15 | 16 | {children} 17 | 18 | ) 19 | } 20 | -------------------------------------------------------------------------------- /gatsby-theme-code-notes/src/components/Search/SearchResults.tsx: -------------------------------------------------------------------------------- 1 | import React, { useContext } from 'react' 2 | import { useStaticQuery, graphql } from 'gatsby' 3 | import { useFlexSearch } from 'react-use-flexsearch' 4 | import { Box } from 'theme-ui' 5 | import { NoteListItem } from '../NoteListItem' 6 | import { SearchContext } from './SearchProvider' 7 | 8 | export const SearchResults = () => { 9 | const { query, setQuery } = useContext(SearchContext) 10 | const { localSearchNotes } = useStaticQuery(graphql` 11 | query { 12 | localSearchNotes { 13 | index 14 | store 15 | } 16 | } 17 | `) 18 | 19 | const results = useFlexSearch( 20 | query, 21 | localSearchNotes.index, 22 | localSearchNotes.store 23 | ) 24 | 25 | return ( 26 | 27 | {results.length > 0 ? ( 28 | results.map((result) => { 29 | return ( 30 | setQuery('')} 37 | dateModified={result.modified} 38 | /> 39 | ) 40 | }) 41 | ) : ( 42 | 43 | Zero search results 44 | 45 | )} 46 | 47 | ) 48 | } 49 | -------------------------------------------------------------------------------- /gatsby-theme-code-notes/src/components/Search/index.ts: -------------------------------------------------------------------------------- 1 | export { SearchInput } from './SearchInput' 2 | export { SearchResults } from './SearchResults' 3 | export { SearchProvider, SearchContext } from './SearchProvider' 4 | -------------------------------------------------------------------------------- /gatsby-theme-code-notes/src/components/Sidebar/Sidebar.tsx: -------------------------------------------------------------------------------- 1 | import React, { FunctionComponent } from 'react' 2 | import { Box, useThemeUI } from 'theme-ui' 3 | 4 | export const Sidebar: FunctionComponent = ({ children }) => { 5 | const { theme } = useThemeUI() 6 | return ( 7 | 21 | {children} 22 | 23 | ) 24 | } 25 | -------------------------------------------------------------------------------- /gatsby-theme-code-notes/src/components/Sidebar/index.ts: -------------------------------------------------------------------------------- 1 | export { Sidebar } from './Sidebar' 2 | -------------------------------------------------------------------------------- /gatsby-theme-code-notes/src/components/TagDot/TagDot.tsx: -------------------------------------------------------------------------------- 1 | import React, { FunctionComponent } from 'react' 2 | import { Box } from 'theme-ui' 3 | import slugify from '@alexcarpenter/slugify' 4 | import { getColourFromString } from '../../utils/getColourFromString' 5 | 6 | export const TagDot: FunctionComponent<{ tag?: string; size?: string }> = ({ 7 | tag, 8 | size = '10px', 9 | }) => { 10 | let tagColor 11 | if (tag) { 12 | const tagName = slugify(tag) 13 | tagColor = getColourFromString(tagName) 14 | } else { 15 | tagColor = '#aaa' 16 | } 17 | return ( 18 | 27 | ) 28 | } 29 | -------------------------------------------------------------------------------- /gatsby-theme-code-notes/src/components/TagDot/index.ts: -------------------------------------------------------------------------------- 1 | export { TagDot } from './TagDot' 2 | -------------------------------------------------------------------------------- /gatsby-theme-code-notes/src/components/TagList/TagList.tsx: -------------------------------------------------------------------------------- 1 | /** @jsx jsx */ 2 | import { FunctionComponent } from 'react' 3 | import { Link as GatsbyLink } from 'gatsby' 4 | import { jsx, Box, Badge, Link, SxStyleProp } from 'theme-ui' 5 | import slugify from '@alexcarpenter/slugify' 6 | import { getColourFromString } from '../../utils/getColourFromString' 7 | import { useSiteMetadata } from '../../use-site-metadata' 8 | 9 | interface TagListProps { 10 | tags: string[] 11 | asLinks?: boolean 12 | sx?: SxStyleProp 13 | } 14 | 15 | export const TagList: FunctionComponent = ({ 16 | tags, 17 | asLinks = true, 18 | sx, 19 | }) => { 20 | const { basePath } = useSiteMetadata() 21 | if (!tags) { 22 | return null 23 | } 24 | 25 | return ( 26 | 34 | {tags.map((item, index) => { 35 | if (item === 'untagged') { 36 | return null 37 | } 38 | const tagName = slugify(item) 39 | const tagColor = getColourFromString(tagName) 40 | const tagLink = 41 | basePath === '/' ? `/tag/${tagName}` : `${basePath}/tag/${tagName}` 42 | if (asLinks) { 43 | return ( 44 | 55 | {item} 56 | 57 | ) 58 | } 59 | 60 | return ( 61 | 62 | {item} 63 | 64 | ) 65 | })} 66 | 67 | ) 68 | } 69 | -------------------------------------------------------------------------------- /gatsby-theme-code-notes/src/components/TagList/index.ts: -------------------------------------------------------------------------------- 1 | export { TagList } from './TagList' 2 | -------------------------------------------------------------------------------- /gatsby-theme-code-notes/src/components/TagNav/TagNav.tsx: -------------------------------------------------------------------------------- 1 | /** @jsx jsx */ 2 | import { FunctionComponent, useContext, Fragment } from 'react' 3 | import { Link as GatsbyLink } from 'gatsby' 4 | import { jsx, Box, NavLink } from 'theme-ui' 5 | import { SearchContext } from '../Search' 6 | import { TagDot } from '../TagDot' 7 | 8 | export interface TagItemInterface { 9 | tag: string 10 | totalCount: number 11 | slug: string 12 | path: string 13 | } 14 | 15 | interface TagNavProps { 16 | tags: TagItemInterface[] 17 | activeTag?: string 18 | rootPath?: boolean 19 | basePath: string 20 | hasUntagged?: boolean 21 | } 22 | 23 | export const TagNav: FunctionComponent = ({ 24 | tags, 25 | activeTag, 26 | rootPath, 27 | basePath, 28 | hasUntagged, 29 | }) => { 30 | const { setQuery } = useContext(SearchContext) 31 | return ( 32 | 33 | setQuery('')} 43 | > 44 | All Notes 45 | 46 | {tags.length > 0 && ( 47 | 48 | 59 | Tags 60 | 61 | 62 | {tags 63 | .sort((one, two) => one.tag.localeCompare(two.tag)) 64 | .map((item, index) => { 65 | return ( 66 | setQuery('')} 74 | > 75 | 76 | {item.tag} 77 | 78 | ) 79 | })} 80 | 81 | )} 82 | {hasUntagged && ( 83 | setQuery('')} 91 | > 92 | 93 | Untagged Notes 94 | 95 | )} 96 | 97 | ) 98 | } 99 | -------------------------------------------------------------------------------- /gatsby-theme-code-notes/src/components/TagNav/index.ts: -------------------------------------------------------------------------------- 1 | export { TagNav } from './TagNav' 2 | -------------------------------------------------------------------------------- /gatsby-theme-code-notes/src/gatsby-plugin-theme-ui/badges.js: -------------------------------------------------------------------------------- 1 | export const badgePrimary = { 2 | borderRadius: '50px', 3 | color: 'textStrong', 4 | bg: 'badgeBg', 5 | textDecoration: 'none', 6 | px: 2, 7 | fontWeight: 'normal', 8 | fontSize: 0, 9 | } 10 | export const badges = { 11 | primary: badgePrimary, 12 | } 13 | -------------------------------------------------------------------------------- /gatsby-theme-code-notes/src/gatsby-plugin-theme-ui/borderWidths.js: -------------------------------------------------------------------------------- 1 | export const borderWidths = { 2 | px: '1px', 3 | '0': '0', 4 | '2': '2px', 5 | '4': '4px', 6 | '8': '8px', 7 | } 8 | -------------------------------------------------------------------------------- /gatsby-theme-code-notes/src/gatsby-plugin-theme-ui/breakpoints.js: -------------------------------------------------------------------------------- 1 | export const breakpoints = ['800px', '1200px'] 2 | -------------------------------------------------------------------------------- /gatsby-theme-code-notes/src/gatsby-plugin-theme-ui/buttons.js: -------------------------------------------------------------------------------- 1 | export const buttons = { 2 | copyCode: { 3 | px: 2, 4 | py: 1, 5 | fontSize: 0, 6 | bg: 'badgeBg', 7 | userSelect: 'none', 8 | color: 'text', 9 | border: (theme) => `1px solid ${theme.colors.badgeBorder}`, 10 | borderRadius: 'large', 11 | ml: 'auto', 12 | fontFamily: 'inherit', 13 | textTransform: 'uppercase', 14 | cursor: 'pointer', 15 | '&:hover': { 16 | bg: 'badgeBgHover', 17 | }, 18 | variant: 'utils.focusVisibleOutset', 19 | }, 20 | 21 | icon: { 22 | cursor: 'pointer', 23 | borderRadius: '50%', 24 | variant: 'utils.focusVisibleOutset', 25 | }, 26 | 27 | menu: { 28 | cursor: 'pointer', 29 | borderRadius: '50%', 30 | variant: 'utils.focusVisibleOutset', 31 | }, 32 | 33 | sort: { 34 | cursor: 'pointer', 35 | variant: 'utils.focusVisibleOutset', 36 | display: 'inline-flex', 37 | alignItems: 'center', 38 | px: 2, 39 | py: 0, 40 | fontSize: 0, 41 | bg: 'badgeBg', 42 | userSelect: 'none', 43 | color: 'text', 44 | border: '1px solid', 45 | borderColor: 'badgeBorder', 46 | borderRadius: 0, 47 | fontFamily: 'inherit', 48 | textTransform: 'uppercase', 49 | '&:hover': { 50 | bg: 'badgeBgHover', 51 | }, 52 | '&.active': { 53 | bg: 'badgeBgHover', 54 | color: 'textStrong', 55 | }, 56 | svg: { 57 | ml: 1, 58 | }, 59 | '&:first-child': { 60 | borderTopLeftRadius: '0.25rem', 61 | borderBottomLeftRadius: '0.25rem', 62 | borderRight: 0, 63 | }, 64 | '&:last-child': { 65 | borderTopRightRadius: '0.25rem', 66 | borderBottomRightRadius: '0.25rem', 67 | borderRight: '1px solid', 68 | borderColor: 'badgeBorder', 69 | }, 70 | }, 71 | } 72 | -------------------------------------------------------------------------------- /gatsby-theme-code-notes/src/gatsby-plugin-theme-ui/colors.js: -------------------------------------------------------------------------------- 1 | export const baseColors = { 2 | grayDark: '#2d3748', 3 | white: '#fff', 4 | light: '#FBFDFE', 5 | dark: '#1a202c', 6 | } 7 | 8 | const lightTheme = { 9 | text: '#4a5568', 10 | textStrong: baseColors.dark, 11 | background: 'hsl(210, 38%, 95%)', 12 | scrollbar: 'hsl(210, 20%, 85%)', 13 | backgroundTransparent: 'hsla(210, 38%, 95%, 0.72)', 14 | contentBg: baseColors.light, 15 | primary: 'hsl(334, 86%, 48%)', 16 | primarySemiTransparent: 'hsla(334, 86%, 48%, 0.3)', 17 | secondary: '#718096', 18 | muted: '#e2e8f0', 19 | success: '#9ae6b4', 20 | info: '#63b3ed', 21 | warning: '#faf089', 22 | danger: '#feb2b2', 23 | navHover: '#cbd5e0', 24 | codeBackground: 'hsl(210,38%,95%)', 25 | codeHighlight: 'hsl(210,38%,90%)', 26 | codeHighlightBorder: 'hsl(210,38%,85%)', 27 | badgeBg: 'hsl(210, 25%, 97%)', 28 | badgeBgHover: 'hsl(210, 25%, 89%)', 29 | badgeBorder: 'hsl(207, 24%, 83%)', 30 | input: '#92A2B9', 31 | code1: '#d03592', 32 | code2: '#fc9867', 33 | code3: '#f66a0a', 34 | code4: '#0366d6', 35 | code5: '#6f42c1', 36 | code6: '#0366d6', 37 | code7: '#999988', 38 | highlight: '#fff176', 39 | around: '#1aa6e9', 40 | crossed: '#e9522c', 41 | } 42 | 43 | export const colors = { 44 | ...baseColors, 45 | ...lightTheme, 46 | modes: { 47 | dark: { 48 | text: 'hsl(210, 17%, 85%)', 49 | textStrong: 'hsl(210, 38%, 98%)', 50 | background: 'hsl(285, 5%, 17%)', 51 | scrollbar: 'hsl(285, 5%, 12%)', 52 | backgroundTransparent: 'hsla(285, 5%, 17%, 0.72)', 53 | contentBg: '#383539', 54 | primary: 'hsl(345, 100%, 69%)', 55 | primarySemiTransparent: 'hsl(345, 100%, 79%, 0.3)', 56 | secondary: '#718096', 57 | muted: 'hsl(210, 5%, 40%)', 58 | success: '#9ae6b4', 59 | info: '#63b3ed', 60 | warning: '#faf089', 61 | danger: '#feb2b2', 62 | navHover: 'hsl(285, 5%, 13%)', 63 | codeBackground: 'hsl(285,5%,17%)', 64 | codeHighlight: 'hsl(285,5%,25%)', 65 | codeHighlightBorder: 'hsl(285,5%,20%)', 66 | badgeBg: 'hsl(285, 5%, 17%)', 67 | badgeBgHover: 'hsl(285, 5%, 14%)', 68 | badgeBorder: 'hsl(285, 5%, 12%)', 69 | input: 'hsl(215, 5%, 65%)', 70 | code1: 'hsl(345, 100%, 69%)', 71 | code2: '#fc9867', 72 | code3: '#ffd866', 73 | code4: '#a9dc76', 74 | code5: '#78dce8', 75 | code6: '#ab9df2', 76 | code7: '#999988', 77 | 78 | highlight: 'hsl(50, 80%, 35%)', 79 | around: '#1aa6e9', 80 | crossed: '#e9522c', 81 | }, 82 | light: lightTheme, 83 | }, 84 | } 85 | -------------------------------------------------------------------------------- /gatsby-theme-code-notes/src/gatsby-plugin-theme-ui/components.js: -------------------------------------------------------------------------------- 1 | /** @jsx jsx */ 2 | import { jsx, Box as TUIBox, Button } from 'theme-ui' 3 | import Prism from '@theme-ui/prism' 4 | import { FaAnchor } from 'react-icons/fa' 5 | import { toClipboard } from 'copee' 6 | import { useState } from 'react' 7 | import { getColourFromString } from '../utils/getColourFromString' 8 | import { 9 | Underline, 10 | Box, 11 | Circle, 12 | Highlight, 13 | StrikeThrough, 14 | CrossedOff, 15 | } from '../components/Rough' 16 | 17 | const heading = (Tag) => (props) => { 18 | if (!props.id) return 19 | 20 | return ( 21 | 29 | {props.children} 30 | 52 | 53 | 54 | 55 | ) 56 | } 57 | 58 | const ResponsiveTable = ({ children }) => { 59 | return ( 60 | 70 | {children}
    71 |
    72 | ) 73 | } 74 | 75 | const CopyCode = ({ code }) => { 76 | const [notificationActive, setNotificationActive] = useState(false) 77 | 78 | const handleCopy = () => { 79 | toClipboard(code) 80 | setNotificationActive(true) 81 | 82 | setTimeout(() => { 83 | setNotificationActive(false) 84 | }, 3000) 85 | } 86 | 87 | return ( 88 | 91 | ) 92 | } 93 | 94 | const CodeLabel = ({ label }) => { 95 | if (!label.includes('language')) { 96 | return null 97 | } 98 | 99 | const classes = label.split(' ') 100 | const langIndex = classes.findIndex((item) => { 101 | return item.includes('language') 102 | }) 103 | const language = classes[langIndex].replace('language-', '') 104 | 105 | return ( 106 | 123 | {language} 124 | 125 | ) 126 | } 127 | 128 | const Code = ({ children, classes, code }) => { 129 | return ( 130 | 131 | 142 | 143 | 144 | 145 | {children} 146 | 147 | ) 148 | } 149 | 150 | const components = { 151 | h1: heading('h1'), 152 | h2: heading('h2'), 153 | h3: heading('h3'), 154 | h4: heading('h4'), 155 | h5: heading('h5'), 156 | h6: heading('h6'), 157 | pre: (props) => props.children, 158 | code: (props) => ( 159 | 160 | 161 | 162 | ), 163 | table: (props) => {props.children}, 164 | Underline, 165 | Box, 166 | Circle, 167 | Highlight, 168 | StrikeThrough, 169 | CrossedOff, 170 | } 171 | 172 | export default components 173 | -------------------------------------------------------------------------------- /gatsby-theme-code-notes/src/gatsby-plugin-theme-ui/fonts.js: -------------------------------------------------------------------------------- 1 | export const baseFonts = { 2 | sans: 3 | 'Inter,-apple-system, BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji"', 4 | serif: 'Georgia, Cambria, "Times New Roman", Times, serif', 5 | mono: 6 | '"JetBrains Mono", "IBM Plex Mono", "Fira Code", "Input Mono", Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace', 7 | } 8 | 9 | export const fonts = { 10 | ...baseFonts, 11 | body: baseFonts.sans, 12 | heading: 'inherit', 13 | monospace: baseFonts.mono, 14 | } 15 | 16 | // https://www.modularscale.com/?1&em&1.125 17 | export const fontSizes = [ 18 | '0.7rem', 19 | '0.875rem', 20 | '1rem', 21 | '1.125rem', 22 | '1.266em', 23 | '1.424em', 24 | '1.602em', 25 | '1.802em', 26 | '2.027em', 27 | '2.281em', 28 | '3rem', 29 | '4rem', 30 | '4.5rem', 31 | ] 32 | 33 | export const baseFontWeights = { 34 | hairline: '100', 35 | thin: '200', 36 | light: '300', 37 | normal: '400', 38 | medium: '500', 39 | semibold: '600', 40 | bold: '700', 41 | extrabold: '800', 42 | black: '900', 43 | } 44 | 45 | export const fontWeights = { 46 | ...baseFontWeights, 47 | body: baseFontWeights.normal, 48 | heading: baseFontWeights.bold, 49 | } 50 | 51 | export const letterSpacings = { 52 | tighter: '-0.05em', 53 | tight: '-0.025em', 54 | normal: '0', 55 | wide: '0.025em', 56 | wider: '0.05em', 57 | widest: '0.1em', 58 | } 59 | 60 | export const baseLineHeights = { 61 | none: '1', 62 | tight: '1.25', 63 | snug: '1.375', 64 | normal: '1.5', 65 | relaxed: '1.625', 66 | loose: '2', 67 | } 68 | 69 | export const lineHeights = { 70 | ...baseLineHeights, 71 | body: baseLineHeights.relaxed, 72 | heading: baseLineHeights.tight, 73 | } 74 | -------------------------------------------------------------------------------- /gatsby-theme-code-notes/src/gatsby-plugin-theme-ui/forms.js: -------------------------------------------------------------------------------- 1 | export const forms = { 2 | input: { 3 | border: 0, 4 | borderBottom: (theme) => `1px solid ${theme.colors.input}`, 5 | pl: 5, 6 | borderRadius: 0, 7 | fontFamily: 'sans', 8 | transition: 'all 200ms ease-in-out ', 9 | lineHeight: '1.4rem', 10 | '&:focus': { 11 | boxShadow: (theme) => `0 2px 0 ${theme.colors.primary}`, 12 | outline: 'none', 13 | borderColor: 'primary', 14 | }, 15 | }, 16 | 17 | radio: { 18 | width: 18, 19 | height: 18, 20 | mr: 1, 21 | 'input:focus ~ &': { 22 | bg: 'primarySemiTransparent', 23 | }, 24 | }, 25 | 26 | label: { 27 | width: 'auto', 28 | ml: 2, 29 | fontSize: 0, 30 | textTransform: 'uppercase', 31 | display: 'inline-flex', 32 | alignItems: 'center', 33 | }, 34 | } 35 | -------------------------------------------------------------------------------- /gatsby-theme-code-notes/src/gatsby-plugin-theme-ui/images.js: -------------------------------------------------------------------------------- 1 | export const images = { 2 | logo: { 3 | maxWidth: '100%', 4 | display: 'block', 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /gatsby-theme-code-notes/src/gatsby-plugin-theme-ui/index.js: -------------------------------------------------------------------------------- 1 | import { buttons } from './buttons' 2 | import { colors } from './colors' 3 | import { shadows } from './shadows' 4 | import { space } from './space' 5 | import { radii } from './radii' 6 | import { 7 | letterSpacings, 8 | lineHeights, 9 | fonts, 10 | fontSizes, 11 | fontWeights, 12 | } from './fonts' 13 | import { styles } from './styles' 14 | import { text } from './text' 15 | import { links } from './links' 16 | import { sizes } from './sizes' 17 | import { forms } from './forms' 18 | import { zIndices } from './zIndices' 19 | import { images } from './images' 20 | import { badges } from './badges' 21 | import { borderWidths } from './borderWidths' 22 | import { utils } from './utils' 23 | import { breakpoints } from './breakpoints' 24 | 25 | export default { 26 | useColorSchemeMediaQuery: true, 27 | buttons, 28 | colors, 29 | shadows, 30 | space, 31 | radii, 32 | letterSpacings, 33 | lineHeights, 34 | fonts, 35 | fontSizes, 36 | fontWeights, 37 | borderWidths, 38 | breakpoints, 39 | styles, 40 | text, 41 | links, 42 | sizes, 43 | forms, 44 | zIndices, 45 | images, 46 | badges, 47 | utils, 48 | } 49 | -------------------------------------------------------------------------------- /gatsby-theme-code-notes/src/gatsby-plugin-theme-ui/links.js: -------------------------------------------------------------------------------- 1 | import { badgePrimary } from './badges' 2 | 3 | export const links = { 4 | noteListItem: { 5 | mx: (theme) => `-${theme.space[2]}`, 6 | p: 2, 7 | borderRadius: 'default', 8 | display: 'block', 9 | transition: 'all 200ms ease-in-out ', 10 | color: 'text', 11 | variant: 'utils.focusVisibleOutset', 12 | '&:link, &:visited': { 13 | color: 'primary', 14 | textDecoration: 'none', 15 | }, 16 | '&:hover': { 17 | bg: 'codeBackground', 18 | }, 19 | }, 20 | 21 | nav: { 22 | px: 3, 23 | py: 1, 24 | display: 'flex', 25 | alignItems: 'center', 26 | fontWeight: 'normal', 27 | fontSize: '14px', 28 | transition: 'all 200ms ease-in-out ', 29 | color: 'textStrong', 30 | '&:hover': { 31 | bg: 'navHover', 32 | color: 'textStrong', 33 | }, 34 | variant: 'utils.focusVisibleOutset', 35 | }, 36 | 37 | badge: { 38 | ...badgePrimary, 39 | transition: 'all 200ms ease-in-out ', 40 | flexShrink: 0, 41 | '&:hover': { 42 | textDecoration: 'none', 43 | opacity: '0.7', 44 | }, 45 | variant: 'utils.focusVisibleOutset', 46 | }, 47 | } 48 | -------------------------------------------------------------------------------- /gatsby-theme-code-notes/src/gatsby-plugin-theme-ui/radii.js: -------------------------------------------------------------------------------- 1 | export const radii = { 2 | none: '0', 3 | sm: '0.125rem', 4 | default: '0.25rem', 5 | lg: '0.5rem', 6 | full: '9999px', 7 | } 8 | -------------------------------------------------------------------------------- /gatsby-theme-code-notes/src/gatsby-plugin-theme-ui/shadows.js: -------------------------------------------------------------------------------- 1 | export const shadows = { 2 | default: '0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06)', 3 | md: '0 0 6px -1px rgba(0, 0, 0, 0.1), 0 0 4px -1px rgba(0, 0, 0, 0.06)', 4 | lg: '0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05)', 5 | xl: 6 | '0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04)', 7 | '2xl': '0 25px 50px -12px rgba(0, 0, 0, 0.25)', 8 | inner: 'inset 0 2px 4px 0 rgba(0, 0, 0, 0.06)', 9 | outline: '0 0 0 3px rgba(66, 153, 225, 0.5)', 10 | none: 'none', 11 | } 12 | -------------------------------------------------------------------------------- /gatsby-theme-code-notes/src/gatsby-plugin-theme-ui/sizes.js: -------------------------------------------------------------------------------- 1 | export const sizes = { 2 | container: 900, 3 | sidebar: '250px', 4 | sidebarSkinny: '50%', 5 | contentMaxWidth: '900px', 6 | } 7 | -------------------------------------------------------------------------------- /gatsby-theme-code-notes/src/gatsby-plugin-theme-ui/space.js: -------------------------------------------------------------------------------- 1 | export const space = [ 2 | 0, 3 | '0.25rem', 4 | '0.5rem', 5 | '1rem', 6 | '1.5rem', 7 | '2rem', 8 | '2.5rem', 9 | '3rem', 10 | '3.5rem', 11 | '4rem', 12 | '8rem', 13 | '16rem', 14 | '32rem', 15 | ] 16 | -------------------------------------------------------------------------------- /gatsby-theme-code-notes/src/gatsby-plugin-theme-ui/styles.js: -------------------------------------------------------------------------------- 1 | export const heading = { 2 | fontFamily: 'heading', 3 | fontWeight: 'heading', 4 | lineHeight: 'heading', 5 | mt: 5, 6 | mb: 3, 7 | color: 'textStrong', 8 | } 9 | 10 | export const styles = { 11 | root: { 12 | fontFamily: 'body', 13 | lineHeight: 'body', 14 | fontWeight: 'body', 15 | fontSize: 2, 16 | }, 17 | a: { 18 | transition: 'all 200ms ease-in-out ', 19 | '&:link, &:visited': { 20 | fontWeight: 'semibold', 21 | color: 'textStrong', 22 | textDecorationStyle: 'solid', 23 | textDecorationSkipInk: 'auto', 24 | textDecorationThickness: 'from-font', 25 | }, 26 | '&:hover': { 27 | color: 'primary', 28 | // textDecorationSkipInk: 'none', 29 | // textDecorationStyle: 'dashed', 30 | }, 31 | variant: 'utils.focusVisibleOutset', 32 | }, 33 | p: { 34 | fontFamily: 'body', 35 | lineHeight: 'body', 36 | fontWeight: 'body', 37 | color: 'text', 38 | mt: 0, 39 | mb: '1.25rem', 40 | }, 41 | h1: { 42 | ...heading, 43 | fontSize: 7, 44 | }, 45 | h2: { 46 | ...heading, 47 | fontSize: 6, 48 | }, 49 | h3: { 50 | ...heading, 51 | fontSize: 5, 52 | mb: 1, 53 | }, 54 | h4: { 55 | ...heading, 56 | fontSize: 4, 57 | mb: 1, 58 | }, 59 | h5: { 60 | ...heading, 61 | fontSize: 3, 62 | mb: 1, 63 | }, 64 | h6: { 65 | ...heading, 66 | fontSize: 2, 67 | mb: 1, 68 | }, 69 | inlineCode: { 70 | // backgroundColor: 'background', 71 | // p: '0.1em 0.2em', 72 | // borderRadius: 'default', 73 | fontSize: '80%', 74 | fontFamily: 'mono', 75 | color: 'textStrong', 76 | fontWeight: 'semibold', 77 | '&::before, &::after': { 78 | content: "'`'", 79 | }, 80 | }, 81 | pre: { 82 | py: 2, 83 | mt: 0, 84 | fontSize: 1, 85 | color: 'textStrong', 86 | bg: 'background', 87 | overflowX: 'auto', 88 | borderRadius: (theme) => 89 | `0 0 ${theme.radii.default} ${theme.radii.default}`, 90 | fontFamily: 'mono', 91 | '.comment,.prolog,.doctype,.cdata': { 92 | color: 'code7', 93 | fontStyle: 'italic', 94 | }, 95 | '.namespace': { 96 | opacity: 0.7, 97 | }, 98 | '.string,.attr-value,.punctuation,.tag.script-punctuation,.tag.attr-value.punctuation': { 99 | color: 'code3', 100 | }, 101 | '.entity,.url,.symbol,.number,.boolean,.constant,.property,.regex,.inserted,.attr-value,.tag.attr-value': { 102 | color: 'text', 103 | }, 104 | '.function,.tag.function,.deleted,.variable,.unit': { 105 | color: 'code3', 106 | }, 107 | '.function-variable': { 108 | color: 'code6', 109 | }, 110 | '.tag,.keyword,.selector,.attr-name,.tag.attr-name': { 111 | color: 'code5', 112 | }, 113 | '.symbol,.tag.punctuation': { 114 | color: 'code7', 115 | }, 116 | '.property,.number': { 117 | color: 'code6', 118 | }, 119 | '.rule,.class-name,.keyword.module,.operator,.tag': { 120 | color: 'code1', 121 | }, 122 | '.function,.tag.function': { 123 | color: 'code4', 124 | }, 125 | '&::-webkit-scrollbar': { 126 | width: '0.5rem', 127 | height: '0.5rem', 128 | '&:hover': { 129 | width: '1rem', 130 | height: '1rem', 131 | }, 132 | }, 133 | '&::-webkit-scrollbar-track': { 134 | backgroundColor: 'scrollbar', 135 | }, 136 | '&::-webkit-scrollbar-thumb': { 137 | backgroundColor: 'muted', 138 | }, 139 | '.highlight': { 140 | bg: 'codeHighlight', 141 | position: 'relative', 142 | '&::before': { 143 | content: "''", 144 | position: 'absolute', 145 | top: 0, 146 | left: 0, 147 | bottom: 0, 148 | display: 'block', 149 | width: '4px', 150 | bg: 'codeHighlightBorder', 151 | }, 152 | }, 153 | '.token-line': { 154 | px: 3, 155 | }, 156 | }, 157 | blockquote: { 158 | borderLeft: '4px solid', 159 | borderColor: 'muted', 160 | fontFamily: 'mono', 161 | textStyle: 'italic', 162 | pl: 3, 163 | my: 4, 164 | mx: 0, 165 | }, 166 | hr: { 167 | bg: 'muted', 168 | border: 0, 169 | height: '1px', 170 | m: 3, 171 | }, 172 | table: { 173 | width: '100%', 174 | borderCollapse: 'separate', 175 | borderSpacing: 0, 176 | tableLayout: 'fixed', 177 | my: 2, 178 | }, 179 | th: { 180 | textAlign: 'left', 181 | borderBottomStyle: 'solid', 182 | borderBottomColor: 'background', 183 | p: 2, 184 | fontWeight: 'bold', 185 | fontSize: 3, 186 | lineHeight: 1, 187 | }, 188 | td: { 189 | textAlign: 'left', 190 | borderBottomStyle: 'solid', 191 | borderBottomColor: 'background', 192 | p: 2, 193 | }, 194 | img: { 195 | my: 4, 196 | p: 3, 197 | border: (theme) => `1px solid ${theme.colors.muted}`, 198 | display: 'block', 199 | borderRadius: 'default', 200 | maxWidth: '100%', 201 | }, 202 | summary: { 203 | color: 'textStrong', 204 | }, 205 | } 206 | -------------------------------------------------------------------------------- /gatsby-theme-code-notes/src/gatsby-plugin-theme-ui/text.js: -------------------------------------------------------------------------------- 1 | export const text = { 2 | noteTitle: { 3 | fontWeight: 'extrabold', 4 | fontSize: 9, 5 | mb: 3, 6 | lineHeight: 'none', 7 | display: 'flex', 8 | alignItems: 'center', 9 | color: 'textStrong', 10 | }, 11 | 12 | noteListItem: { 13 | position: 'relative', 14 | fontSize: 2, 15 | m: 0, 16 | fontFamily: 'body', 17 | fontWeight: 'bold', 18 | color: 'textStrong', 19 | pl: 5, 20 | }, 21 | 22 | dateModified: { 23 | fontSize: 0, 24 | color: 'text', 25 | whiteSpace: 'nowrap', 26 | overflow: 'hidden', 27 | textOverflow: 'ellipsis', 28 | }, 29 | } 30 | -------------------------------------------------------------------------------- /gatsby-theme-code-notes/src/gatsby-plugin-theme-ui/utils.js: -------------------------------------------------------------------------------- 1 | export const utils = { 2 | focusVisibleOutset: { 3 | '&:focus': { 4 | outline: 0, 5 | boxShadow: (theme) => 6 | `0px 0px 0px 3px ${theme.colors.primarySemiTransparent}`, 7 | }, 8 | '&:focus-visible': { 9 | boxShadow: (theme) => 10 | `0px 0px 0px 3px ${theme.colors.primarySemiTransparent}`, 11 | }, 12 | '&:focus:not(:focus-visible)': { 13 | boxShadow: 'none', 14 | }, 15 | }, 16 | 17 | focusVisibleInset: { 18 | '&:focus': { 19 | outline: 0, 20 | boxShadow: (theme) => 21 | `inset 0px 0px 0px 3px ${theme.colors.primarySemiTransparent}`, 22 | }, 23 | '&:focus-visible': { 24 | outline: 0, 25 | boxShadow: (theme) => 26 | `inset 0px 0px 0px 3px ${theme.colors.primarySemiTransparent}`, 27 | }, 28 | '&:focus:not(:focus-visible)': { 29 | boxShadow: 'none', 30 | }, 31 | }, 32 | } 33 | -------------------------------------------------------------------------------- /gatsby-theme-code-notes/src/gatsby-plugin-theme-ui/zIndices.js: -------------------------------------------------------------------------------- 1 | export const zIndices = { 2 | low: 10, 3 | mid: 20, 4 | high: 30, 5 | } 6 | -------------------------------------------------------------------------------- /gatsby-theme-code-notes/src/gatsby/wrapRootElement.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { SearchProvider } from '../components/Search' 3 | 4 | export const wrapRootElementComponent = ({ element }) => { 5 | return {element} 6 | } 7 | -------------------------------------------------------------------------------- /gatsby-theme-code-notes/src/templates/404.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Layout } from '../components/Layout' 3 | import { Heading, Box, Styled } from 'theme-ui' 4 | 5 | const ErrorPage = ({ pageContext, location }) => { 6 | return ( 7 | 14 |
    15 | 16 | 17 | Page not found 18 | 19 | 20 | 21 | 22 | Oops! This page you are looking for has been removed or relocated. 23 | 24 |
    25 |
    26 | ) 27 | } 28 | 29 | export default ErrorPage 30 | -------------------------------------------------------------------------------- /gatsby-theme-code-notes/src/templates/Note.js: -------------------------------------------------------------------------------- 1 | import { graphql } from 'gatsby' 2 | import { NotePage } from '../components/NotePage' 3 | 4 | export default NotePage 5 | 6 | export const pageQuery = graphql` 7 | query NoteById($id: String!) { 8 | mdx(id: { eq: $id }) { 9 | body 10 | frontmatter { 11 | title 12 | tags 13 | emoji 14 | link 15 | modified(formatString: "LL") 16 | modifiedTimestamp: modified 17 | } 18 | references: inboundReferences { 19 | ... on Mdx { 20 | frontmatter { 21 | title 22 | } 23 | slug 24 | } 25 | } 26 | fields { 27 | slug 28 | } 29 | tableOfContents(maxDepth: 3) 30 | parent { 31 | ... on File { 32 | relativePath 33 | } 34 | } 35 | } 36 | } 37 | ` 38 | -------------------------------------------------------------------------------- /gatsby-theme-code-notes/src/templates/Notes.js: -------------------------------------------------------------------------------- 1 | import { graphql } from 'gatsby' 2 | import { NotesPage } from '../components/NotesPage' 3 | 4 | export default NotesPage 5 | 6 | export const pageQuery = graphql` 7 | fragment AllPages on Mdx { 8 | id 9 | frontmatter { 10 | title 11 | tags 12 | emoji 13 | modified(formatString: "LL") 14 | modifiedTimestamp: modified 15 | } 16 | fields { 17 | slug 18 | } 19 | } 20 | 21 | query { 22 | allMdx { 23 | edges { 24 | node { 25 | ...AllPages 26 | } 27 | } 28 | } 29 | } 30 | ` 31 | -------------------------------------------------------------------------------- /gatsby-theme-code-notes/src/templates/TagPage.js: -------------------------------------------------------------------------------- 1 | import { graphql } from 'gatsby' 2 | import { NotesPage } from '../components/NotesPage' 3 | 4 | export default NotesPage 5 | 6 | export const pageQuery = graphql` 7 | query($tag: String!) { 8 | allMdx( 9 | sort: { fields: frontmatter___title, order: ASC } 10 | filter: { frontmatter: { tags: { eq: $tag } } } 11 | ) { 12 | edges { 13 | node { 14 | id 15 | ...AllPages 16 | } 17 | } 18 | } 19 | } 20 | ` 21 | -------------------------------------------------------------------------------- /gatsby-theme-code-notes/src/templates/UntaggedTagPage.js: -------------------------------------------------------------------------------- 1 | import { graphql } from 'gatsby' 2 | import { NotesPage } from '../components/NotesPage' 3 | 4 | export default NotesPage 5 | 6 | export const pageQuery = graphql` 7 | query { 8 | allMdx( 9 | sort: { fields: frontmatter___title, order: ASC } 10 | filter: { frontmatter: { tags: { eq: null } } } 11 | ) { 12 | edges { 13 | node { 14 | id 15 | ...AllPages 16 | } 17 | } 18 | } 19 | } 20 | ` 21 | -------------------------------------------------------------------------------- /gatsby-theme-code-notes/src/use-all-tags.ts: -------------------------------------------------------------------------------- 1 | import { graphql, useStaticQuery } from 'gatsby' 2 | import slugify from '@alexcarpenter/slugify' 3 | 4 | export const useAllTags = () => { 5 | const data = useStaticQuery(graphql` 6 | { 7 | allMdx { 8 | tags: group(field: frontmatter___tags) { 9 | tag: fieldValue 10 | totalCount 11 | } 12 | } 13 | } 14 | `) 15 | 16 | return data.allMdx.tags.sort().map((item) => { 17 | return { 18 | ...item, 19 | slug: slugify(item.tag), 20 | } 21 | }) 22 | } 23 | -------------------------------------------------------------------------------- /gatsby-theme-code-notes/src/use-site-metadata.ts: -------------------------------------------------------------------------------- 1 | import { graphql, useStaticQuery } from 'gatsby' 2 | 3 | export const useSiteMetadata = () => { 4 | const data = useStaticQuery(graphql` 5 | { 6 | site { 7 | siteMetadata { 8 | title 9 | description 10 | gitRepoContentPath 11 | showThemeInfo 12 | showDescriptionInSidebar 13 | logo 14 | openSearch { 15 | siteUrl 16 | siteShortName 17 | } 18 | showDate 19 | basePath 20 | } 21 | } 22 | } 23 | `) 24 | 25 | return data.site.siteMetadata 26 | } 27 | -------------------------------------------------------------------------------- /gatsby-theme-code-notes/src/utils/createOpenSearch.js: -------------------------------------------------------------------------------- 1 | module.exports = (options) => { 2 | const { 3 | siteUrl = '', 4 | siteShortName = 'Code Notes Search', 5 | siteDescription = null, 6 | siteTags = null, 7 | siteContact = null, 8 | } = options 9 | 10 | let data = ` 11 | 12 | ${siteShortName} 13 | 14 | ` 15 | 16 | if (siteDescription !== null) { 17 | data += ` ${siteDescription} 18 | ` 19 | } 20 | if (siteTags !== null) { 21 | data += ` ${siteTags} 22 | ` 23 | } 24 | if (siteContact !== null) { 25 | data += ` ${siteContact} 26 | ` 27 | } 28 | 29 | data += `` 30 | 31 | return data 32 | } 33 | -------------------------------------------------------------------------------- /gatsby-theme-code-notes/src/utils/getColourFromString.ts: -------------------------------------------------------------------------------- 1 | import ColorHash from 'color-hash' 2 | 3 | const colorHash = new ColorHash({ 4 | lightness: 0.6, 5 | saturation: 0.4, 6 | }) 7 | 8 | export const getColourFromString = (item: string): string => { 9 | const tagHsl: number[] = colorHash.hsl(item) 10 | const tagColor = `hsla(${tagHsl[0]},${tagHsl[1] * 100}%,${ 11 | tagHsl[2] * 100 12 | }%,0.5)` 13 | return tagColor 14 | } 15 | -------------------------------------------------------------------------------- /gatsby-theme-code-notes/src/utils/resolve-url.js: -------------------------------------------------------------------------------- 1 | const slugify = require('@alexcarpenter/slugify') 2 | 3 | module.exports = (title) => slugify(`/${title}`) 4 | -------------------------------------------------------------------------------- /gatsby-theme-code-notes/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowJs": true, 4 | "esModuleInterop": true, 5 | "forceConsistentCasingInFileNames": true, 6 | "isolatedModules": true, 7 | "jsx": "preserve", 8 | "lib": ["dom", "dom.iterable", "esnext"], 9 | "module": "esnext", 10 | "moduleResolution": "node", 11 | "noEmit": true, 12 | "resolveJsonModule": true, 13 | "skipLibCheck": true, 14 | "strict": false, 15 | "target": "es5" 16 | }, 17 | "exclude": [ 18 | "node_modules", 19 | "./.storybook/**/*", 20 | "./cypress/**/*", 21 | "./static/scripts/**/*" 22 | ], 23 | "include": ["src/**/*.ts", "src/**/*.tsx", "global.d.ts"] 24 | } 25 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gatsby-theme-code-notes-workspace", 3 | "private": true, 4 | "version": "0.0.1", 5 | "main": "index.js", 6 | "license": "MIT", 7 | "scripts": { 8 | "prebuild": "yarn clean", 9 | "build": "yarn workspace example build", 10 | "predev": "yarn clean", 11 | "dev": "yarn workspace example dev", 12 | "clean": "yarn workspace example clean", 13 | "start": "yarn workspace example start", 14 | "lint": "eslint './gatsby-theme-code-notes/src/**/*'" 15 | }, 16 | "workspaces": [ 17 | "gatsby-theme-code-notes", 18 | "example" 19 | ], 20 | "dependencies": { 21 | "eslint": "^7.24.0", 22 | "eslint-config-prettier": "^8.2.0", 23 | "eslint-plugin-prettier": "^3.4.0", 24 | "eslint-plugin-react": "^7.23.2", 25 | "eslint-plugin-react-hooks": "^4.2.0", 26 | "husky": "^6.0.0", 27 | "jest": "^26.6.3", 28 | "jest-emotion": "^11.0.0", 29 | "lint-staged": "^10.5.4", 30 | "prettier": "^2.2.1" 31 | } 32 | } 33 | --------------------------------------------------------------------------------