├── .gitignore ├── CNAME ├── Dockerfile ├── LICENSE ├── README.md ├── assets ├── BaksoDaging-Regular.ttf ├── audio │ ├── awoken_blip.wav │ ├── awoken_c.wav │ ├── awoken_d.wav │ ├── awoken_e.wav │ ├── awoken_f.wav │ ├── awoken_final.wav │ ├── bgm.wav │ ├── bgm_final.wav │ ├── click.wav │ ├── drum.wav │ ├── hover.wav │ ├── negative_click.wav │ ├── riser.wav │ ├── task_complete.wav │ └── yap │ │ ├── __attribution.txt │ │ ├── a.wav │ │ ├── b.wav │ │ ├── c.wav │ │ ├── d.wav │ │ ├── e.wav │ │ ├── f.wav │ │ ├── g.wav │ │ ├── h.wav │ │ ├── i.wav │ │ ├── j.wav │ │ ├── k.wav │ │ ├── l.wav │ │ ├── m.wav │ │ ├── n.wav │ │ ├── o.wav │ │ ├── p.wav │ │ ├── q.wav │ │ ├── r.wav │ │ ├── s.wav │ │ ├── sh.wav │ │ ├── space.wav │ │ ├── t.wav │ │ ├── th.wav │ │ ├── thonk1.wav │ │ ├── thonk2.wav │ │ ├── thonk3.wav │ │ ├── u.wav │ │ ├── v.wav │ │ ├── w.wav │ │ ├── x.wav │ │ ├── y.wav │ │ └── z.wav ├── card.png ├── howler.js ├── img │ ├── 30m.png │ ├── advanced_custom_setup.png │ ├── api_key.png │ ├── api_key_highlighted.png │ ├── api_key_input.png │ ├── api_url_input.png │ ├── application_is_available.png │ ├── assets_folder.png │ ├── background_color.png │ ├── code_button.png │ ├── color_picker.png │ ├── command_palette_api_key.png │ ├── command_palette_api_url.png │ ├── config_yml.png │ ├── config_yml_contents.png │ ├── create_codespace_on_main.png │ ├── create_repository.png │ ├── css.png │ ├── css_rule.png │ ├── empty_front_matter_block.png │ ├── enable_dark_theme.png │ ├── extensions_icon.png │ ├── footer.png │ ├── forwarded_address.png │ ├── front_matter_block.png │ ├── gem_install_jekyll.png │ ├── github_desktop_clone.png │ ├── github_desktop_commit.png │ ├── github_desktop_sign_in.png │ ├── hackatime_settings.png │ ├── html.png │ ├── html5.png │ ├── html_body.png │ ├── html_p.png │ ├── includes_folder.png │ ├── jekyll-logo-2x.png │ ├── jekyll-new-code.png │ ├── jekyll-new.png │ ├── layouts_folder.png │ ├── lifeblood.png │ ├── lifeblood_navigation.png │ ├── manufacturetocat.png │ ├── markdown_bold.png │ ├── markdown_headings.png │ ├── markdown_italic.png │ ├── markdown_link.png │ ├── markdown_paragraph.png │ ├── my_theme.png │ ├── my_theme_scss.png │ ├── new_repository.png │ ├── nimmoi.png │ ├── no_staged_changes.png │ ├── open_on_github.png │ ├── parchment.png │ ├── pink_index_md.png │ ├── ports_tab.png │ ├── public_template.png │ ├── pull_and_push.png │ ├── repository_name.png │ ├── repository_settings.png │ ├── repository_template.png │ ├── repository_visibility.png │ ├── ruby-logo-2x.png │ ├── rubyinstaller_components.png │ ├── rubyinstaller_destination.png │ ├── rubyinstaller_license.png │ ├── rubyinstaller_ridk.png │ ├── sass.png │ ├── sass_folder.png │ ├── scss_error.png │ ├── shell_prompt.png │ ├── site_contents.png │ ├── source_control.png │ ├── source_control_graph.png │ ├── source_control_icon.png │ ├── source_control_with_message.png │ ├── styled_index_md.png │ ├── sync_changes.png │ ├── terminal_tab.png │ ├── time_tracking_wizard.png │ ├── title.png │ ├── tonic_starter.png │ ├── tonic_starter_index.png │ ├── tonic_starter_index_2.png │ ├── tonic_starter_index_3.png │ ├── tonic_starter_index_4.png │ ├── trust_publisher.png │ ├── use_this_template.png │ └── wakatime_extension.png ├── ms │ ├── arrow_left.svg │ ├── asleep.svg │ ├── blushing.svg │ ├── content.svg │ ├── exhausted.svg │ ├── flushed.svg │ ├── gray_question_mark.svg │ ├── grimace.svg │ ├── grin.svg │ ├── grinning.svg │ ├── halo.svg │ ├── hand_over_mouth.svg │ ├── hand_over_mouth_open_eyes.svg │ ├── head_shaking_horizontally.svg │ ├── head_shaking_vertically.svg │ ├── heart_eyes.svg │ ├── holding_back_tears.svg │ ├── hushed.svg │ ├── pensive.svg │ ├── red_exclamation_mark.svg │ ├── relaxed.svg │ ├── shush.svg │ ├── slight_smile.svg │ ├── smile_hearts.svg │ ├── smile_with_tear.svg │ ├── speaker_medium_volume.svg │ ├── speaker_muted.svg │ ├── starry_eyes.svg │ ├── sunglasses_face.svg │ ├── sweat_smile.svg │ ├── thinking.svg │ ├── tick.svg │ ├── unsure.svg │ ├── worried.svg │ └── yawn.svg └── yapping.js ├── attribution.js ├── favicon.svg ├── index.css ├── index.html ├── index.js ├── learn ├── Task.js ├── index.html ├── script.js ├── tasks.js └── tasks │ ├── 404.js │ ├── a_feature_of_your_own.js │ ├── all.js │ ├── element_showcase.js │ ├── gems.js │ ├── github_setup.js │ ├── hackatime_setup.js │ ├── includes.js │ ├── jekyll_setup.js │ ├── layouts.js │ ├── liquid.js │ ├── more_elements.js │ ├── sass.js │ ├── the_config_file.js │ ├── the_readme_file.js │ ├── using_your_theme.js │ └── your_first_page.js ├── package-lock.json ├── package.json ├── server.js └── style.css /.gitignore: -------------------------------------------------------------------------------- 1 | tonic.code-workspace 2 | .DS_Store 3 | .wakatime-project 4 | *.wav.asd 5 | node_modules 6 | .env -------------------------------------------------------------------------------- /CNAME: -------------------------------------------------------------------------------- 1 | tonic.hackclub.com 2 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM oven/bun:1.2.3 as base 2 | 3 | # Set working directory 4 | WORKDIR /app 5 | 6 | # Copy package.json 7 | COPY package.json . 8 | 9 | # Install dependencies 10 | RUN bun install --production 11 | 12 | # Copy application code 13 | COPY . . 14 | 15 | # Set environment variables 16 | ENV PORT=3000 17 | ENV NODE_ENV=production 18 | 19 | # Expose the port 20 | EXPOSE 3000 21 | 22 | # Start the application 23 | CMD ["bun", "run", "server.js"] -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Hack Club 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 | # tonic -------------------------------------------------------------------------------- /assets/BaksoDaging-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/BaksoDaging-Regular.ttf -------------------------------------------------------------------------------- /assets/audio/awoken_blip.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/audio/awoken_blip.wav -------------------------------------------------------------------------------- /assets/audio/awoken_c.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/audio/awoken_c.wav -------------------------------------------------------------------------------- /assets/audio/awoken_d.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/audio/awoken_d.wav -------------------------------------------------------------------------------- /assets/audio/awoken_e.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/audio/awoken_e.wav -------------------------------------------------------------------------------- /assets/audio/awoken_f.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/audio/awoken_f.wav -------------------------------------------------------------------------------- /assets/audio/awoken_final.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/audio/awoken_final.wav -------------------------------------------------------------------------------- /assets/audio/bgm.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/audio/bgm.wav -------------------------------------------------------------------------------- /assets/audio/bgm_final.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/audio/bgm_final.wav -------------------------------------------------------------------------------- /assets/audio/click.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/audio/click.wav -------------------------------------------------------------------------------- /assets/audio/drum.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/audio/drum.wav -------------------------------------------------------------------------------- /assets/audio/hover.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/audio/hover.wav -------------------------------------------------------------------------------- /assets/audio/negative_click.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/audio/negative_click.wav -------------------------------------------------------------------------------- /assets/audio/riser.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/audio/riser.wav -------------------------------------------------------------------------------- /assets/audio/task_complete.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/audio/task_complete.wav -------------------------------------------------------------------------------- /assets/audio/yap/__attribution.txt: -------------------------------------------------------------------------------- 1 | these are from https://github.com/equalo-official/animalese-generator! 2 | -------------------------------------------------------------------------------- /assets/audio/yap/a.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/audio/yap/a.wav -------------------------------------------------------------------------------- /assets/audio/yap/b.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/audio/yap/b.wav -------------------------------------------------------------------------------- /assets/audio/yap/c.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/audio/yap/c.wav -------------------------------------------------------------------------------- /assets/audio/yap/d.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/audio/yap/d.wav -------------------------------------------------------------------------------- /assets/audio/yap/e.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/audio/yap/e.wav -------------------------------------------------------------------------------- /assets/audio/yap/f.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/audio/yap/f.wav -------------------------------------------------------------------------------- /assets/audio/yap/g.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/audio/yap/g.wav -------------------------------------------------------------------------------- /assets/audio/yap/h.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/audio/yap/h.wav -------------------------------------------------------------------------------- /assets/audio/yap/i.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/audio/yap/i.wav -------------------------------------------------------------------------------- /assets/audio/yap/j.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/audio/yap/j.wav -------------------------------------------------------------------------------- /assets/audio/yap/k.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/audio/yap/k.wav -------------------------------------------------------------------------------- /assets/audio/yap/l.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/audio/yap/l.wav -------------------------------------------------------------------------------- /assets/audio/yap/m.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/audio/yap/m.wav -------------------------------------------------------------------------------- /assets/audio/yap/n.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/audio/yap/n.wav -------------------------------------------------------------------------------- /assets/audio/yap/o.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/audio/yap/o.wav -------------------------------------------------------------------------------- /assets/audio/yap/p.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/audio/yap/p.wav -------------------------------------------------------------------------------- /assets/audio/yap/q.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/audio/yap/q.wav -------------------------------------------------------------------------------- /assets/audio/yap/r.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/audio/yap/r.wav -------------------------------------------------------------------------------- /assets/audio/yap/s.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/audio/yap/s.wav -------------------------------------------------------------------------------- /assets/audio/yap/sh.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/audio/yap/sh.wav -------------------------------------------------------------------------------- /assets/audio/yap/space.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/audio/yap/space.wav -------------------------------------------------------------------------------- /assets/audio/yap/t.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/audio/yap/t.wav -------------------------------------------------------------------------------- /assets/audio/yap/th.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/audio/yap/th.wav -------------------------------------------------------------------------------- /assets/audio/yap/thonk1.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/audio/yap/thonk1.wav -------------------------------------------------------------------------------- /assets/audio/yap/thonk2.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/audio/yap/thonk2.wav -------------------------------------------------------------------------------- /assets/audio/yap/thonk3.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/audio/yap/thonk3.wav -------------------------------------------------------------------------------- /assets/audio/yap/u.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/audio/yap/u.wav -------------------------------------------------------------------------------- /assets/audio/yap/v.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/audio/yap/v.wav -------------------------------------------------------------------------------- /assets/audio/yap/w.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/audio/yap/w.wav -------------------------------------------------------------------------------- /assets/audio/yap/x.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/audio/yap/x.wav -------------------------------------------------------------------------------- /assets/audio/yap/y.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/audio/yap/y.wav -------------------------------------------------------------------------------- /assets/audio/yap/z.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/audio/yap/z.wav -------------------------------------------------------------------------------- /assets/card.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/card.png -------------------------------------------------------------------------------- /assets/img/30m.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/img/30m.png -------------------------------------------------------------------------------- /assets/img/advanced_custom_setup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/img/advanced_custom_setup.png -------------------------------------------------------------------------------- /assets/img/api_key.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/img/api_key.png -------------------------------------------------------------------------------- /assets/img/api_key_highlighted.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/img/api_key_highlighted.png -------------------------------------------------------------------------------- /assets/img/api_key_input.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/img/api_key_input.png -------------------------------------------------------------------------------- /assets/img/api_url_input.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/img/api_url_input.png -------------------------------------------------------------------------------- /assets/img/application_is_available.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/img/application_is_available.png -------------------------------------------------------------------------------- /assets/img/assets_folder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/img/assets_folder.png -------------------------------------------------------------------------------- /assets/img/background_color.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/img/background_color.png -------------------------------------------------------------------------------- /assets/img/code_button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/img/code_button.png -------------------------------------------------------------------------------- /assets/img/color_picker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/img/color_picker.png -------------------------------------------------------------------------------- /assets/img/command_palette_api_key.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/img/command_palette_api_key.png -------------------------------------------------------------------------------- /assets/img/command_palette_api_url.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/img/command_palette_api_url.png -------------------------------------------------------------------------------- /assets/img/config_yml.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/img/config_yml.png -------------------------------------------------------------------------------- /assets/img/config_yml_contents.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/img/config_yml_contents.png -------------------------------------------------------------------------------- /assets/img/create_codespace_on_main.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/img/create_codespace_on_main.png -------------------------------------------------------------------------------- /assets/img/create_repository.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/img/create_repository.png -------------------------------------------------------------------------------- /assets/img/css.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/img/css.png -------------------------------------------------------------------------------- /assets/img/css_rule.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/img/css_rule.png -------------------------------------------------------------------------------- /assets/img/empty_front_matter_block.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/img/empty_front_matter_block.png -------------------------------------------------------------------------------- /assets/img/enable_dark_theme.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/img/enable_dark_theme.png -------------------------------------------------------------------------------- /assets/img/extensions_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/img/extensions_icon.png -------------------------------------------------------------------------------- /assets/img/footer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/img/footer.png -------------------------------------------------------------------------------- /assets/img/forwarded_address.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/img/forwarded_address.png -------------------------------------------------------------------------------- /assets/img/front_matter_block.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/img/front_matter_block.png -------------------------------------------------------------------------------- /assets/img/gem_install_jekyll.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/img/gem_install_jekyll.png -------------------------------------------------------------------------------- /assets/img/github_desktop_clone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/img/github_desktop_clone.png -------------------------------------------------------------------------------- /assets/img/github_desktop_commit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/img/github_desktop_commit.png -------------------------------------------------------------------------------- /assets/img/github_desktop_sign_in.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/img/github_desktop_sign_in.png -------------------------------------------------------------------------------- /assets/img/hackatime_settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/img/hackatime_settings.png -------------------------------------------------------------------------------- /assets/img/html.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/img/html.png -------------------------------------------------------------------------------- /assets/img/html5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/img/html5.png -------------------------------------------------------------------------------- /assets/img/html_body.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/img/html_body.png -------------------------------------------------------------------------------- /assets/img/html_p.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/img/html_p.png -------------------------------------------------------------------------------- /assets/img/includes_folder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/img/includes_folder.png -------------------------------------------------------------------------------- /assets/img/jekyll-logo-2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/img/jekyll-logo-2x.png -------------------------------------------------------------------------------- /assets/img/jekyll-new-code.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/img/jekyll-new-code.png -------------------------------------------------------------------------------- /assets/img/jekyll-new.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/img/jekyll-new.png -------------------------------------------------------------------------------- /assets/img/layouts_folder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/img/layouts_folder.png -------------------------------------------------------------------------------- /assets/img/lifeblood.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/img/lifeblood.png -------------------------------------------------------------------------------- /assets/img/lifeblood_navigation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/img/lifeblood_navigation.png -------------------------------------------------------------------------------- /assets/img/manufacturetocat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/img/manufacturetocat.png -------------------------------------------------------------------------------- /assets/img/markdown_bold.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/img/markdown_bold.png -------------------------------------------------------------------------------- /assets/img/markdown_headings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/img/markdown_headings.png -------------------------------------------------------------------------------- /assets/img/markdown_italic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/img/markdown_italic.png -------------------------------------------------------------------------------- /assets/img/markdown_link.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/img/markdown_link.png -------------------------------------------------------------------------------- /assets/img/markdown_paragraph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/img/markdown_paragraph.png -------------------------------------------------------------------------------- /assets/img/my_theme.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/img/my_theme.png -------------------------------------------------------------------------------- /assets/img/my_theme_scss.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/img/my_theme_scss.png -------------------------------------------------------------------------------- /assets/img/new_repository.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/img/new_repository.png -------------------------------------------------------------------------------- /assets/img/nimmoi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/img/nimmoi.png -------------------------------------------------------------------------------- /assets/img/no_staged_changes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/img/no_staged_changes.png -------------------------------------------------------------------------------- /assets/img/open_on_github.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/img/open_on_github.png -------------------------------------------------------------------------------- /assets/img/parchment.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/img/parchment.png -------------------------------------------------------------------------------- /assets/img/pink_index_md.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/img/pink_index_md.png -------------------------------------------------------------------------------- /assets/img/ports_tab.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/img/ports_tab.png -------------------------------------------------------------------------------- /assets/img/public_template.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/img/public_template.png -------------------------------------------------------------------------------- /assets/img/pull_and_push.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/img/pull_and_push.png -------------------------------------------------------------------------------- /assets/img/repository_name.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/img/repository_name.png -------------------------------------------------------------------------------- /assets/img/repository_settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/img/repository_settings.png -------------------------------------------------------------------------------- /assets/img/repository_template.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/img/repository_template.png -------------------------------------------------------------------------------- /assets/img/repository_visibility.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/img/repository_visibility.png -------------------------------------------------------------------------------- /assets/img/ruby-logo-2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/img/ruby-logo-2x.png -------------------------------------------------------------------------------- /assets/img/rubyinstaller_components.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/img/rubyinstaller_components.png -------------------------------------------------------------------------------- /assets/img/rubyinstaller_destination.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/img/rubyinstaller_destination.png -------------------------------------------------------------------------------- /assets/img/rubyinstaller_license.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/img/rubyinstaller_license.png -------------------------------------------------------------------------------- /assets/img/rubyinstaller_ridk.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/img/rubyinstaller_ridk.png -------------------------------------------------------------------------------- /assets/img/sass.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/img/sass.png -------------------------------------------------------------------------------- /assets/img/sass_folder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/img/sass_folder.png -------------------------------------------------------------------------------- /assets/img/scss_error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/img/scss_error.png -------------------------------------------------------------------------------- /assets/img/shell_prompt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/img/shell_prompt.png -------------------------------------------------------------------------------- /assets/img/site_contents.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/img/site_contents.png -------------------------------------------------------------------------------- /assets/img/source_control.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/img/source_control.png -------------------------------------------------------------------------------- /assets/img/source_control_graph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/img/source_control_graph.png -------------------------------------------------------------------------------- /assets/img/source_control_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/img/source_control_icon.png -------------------------------------------------------------------------------- /assets/img/source_control_with_message.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/img/source_control_with_message.png -------------------------------------------------------------------------------- /assets/img/styled_index_md.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/img/styled_index_md.png -------------------------------------------------------------------------------- /assets/img/sync_changes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/img/sync_changes.png -------------------------------------------------------------------------------- /assets/img/terminal_tab.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/img/terminal_tab.png -------------------------------------------------------------------------------- /assets/img/time_tracking_wizard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/img/time_tracking_wizard.png -------------------------------------------------------------------------------- /assets/img/title.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/img/title.png -------------------------------------------------------------------------------- /assets/img/tonic_starter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/img/tonic_starter.png -------------------------------------------------------------------------------- /assets/img/tonic_starter_index.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/img/tonic_starter_index.png -------------------------------------------------------------------------------- /assets/img/tonic_starter_index_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/img/tonic_starter_index_2.png -------------------------------------------------------------------------------- /assets/img/tonic_starter_index_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/img/tonic_starter_index_3.png -------------------------------------------------------------------------------- /assets/img/tonic_starter_index_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/img/tonic_starter_index_4.png -------------------------------------------------------------------------------- /assets/img/trust_publisher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/img/trust_publisher.png -------------------------------------------------------------------------------- /assets/img/use_this_template.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/img/use_this_template.png -------------------------------------------------------------------------------- /assets/img/wakatime_extension.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackclub/tonic/9b24b8623241041bf0c6506e95aa5c59032fceb2/assets/img/wakatime_extension.png -------------------------------------------------------------------------------- /assets/ms/arrow_left.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 9 | Mutant Standard emoji 2024.06 10 | 11 | 12 | 13 | 14 | Caius Nocturne 15 | http://mutant.tech/ 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /assets/ms/asleep.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 11 | Mutant Standard emoji 2024.06 12 | 13 | 14 | 15 | 16 | Caius Nocturne 17 | http://mutant.tech/ 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /assets/ms/blushing.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 11 | Mutant Standard emoji 2024.06 12 | 13 | 14 | 15 | 16 | Caius Nocturne 17 | http://mutant.tech/ 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /assets/ms/content.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 11 | Mutant Standard emoji 2024.06 12 | 13 | 14 | 15 | 16 | Caius Nocturne 17 | http://mutant.tech/ 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /assets/ms/exhausted.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 11 | Mutant Standard emoji 2024.06 12 | 13 | 14 | 15 | 16 | Caius Nocturne 17 | http://mutant.tech/ 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /assets/ms/flushed.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 11 | Mutant Standard emoji 2024.06 12 | 13 | 14 | 15 | 16 | Caius Nocturne 17 | http://mutant.tech/ 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /assets/ms/gray_question_mark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 11 | Mutant Standard emoji 2024.06 12 | 13 | 14 | 15 | 16 | Caius Nocturne 17 | http://mutant.tech/ 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /assets/ms/grimace.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 11 | Mutant Standard emoji 2024.06 12 | 13 | 14 | 15 | 16 | Caius Nocturne 17 | http://mutant.tech/ 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /assets/ms/grin.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 11 | Mutant Standard emoji 2024.06 12 | 13 | 14 | 15 | 16 | Caius Nocturne 17 | http://mutant.tech/ 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /assets/ms/grinning.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 11 | Mutant Standard emoji 2024.06 12 | 13 | 14 | 15 | 16 | Caius Nocturne 17 | http://mutant.tech/ 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /assets/ms/halo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 11 | Mutant Standard emoji 2024.06 12 | 13 | 14 | 15 | 16 | Caius Nocturne 17 | http://mutant.tech/ 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /assets/ms/hand_over_mouth.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 11 | Mutant Standard emoji 2024.06 12 | 13 | 14 | 15 | 16 | Caius Nocturne 17 | http://mutant.tech/ 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /assets/ms/hand_over_mouth_open_eyes.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 11 | Mutant Standard emoji 2024.06 12 | 13 | 14 | 15 | 16 | Caius Nocturne 17 | http://mutant.tech/ 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /assets/ms/head_shaking_horizontally.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 11 | Mutant Standard emoji 2024.06 12 | 13 | 14 | 15 | 16 | Caius Nocturne 17 | http://mutant.tech/ 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /assets/ms/head_shaking_vertically.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 11 | Mutant Standard emoji 2024.06 12 | 13 | 14 | 15 | 16 | Caius Nocturne 17 | http://mutant.tech/ 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /assets/ms/heart_eyes.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 11 | Mutant Standard emoji 2024.06 12 | 13 | 14 | 15 | 16 | Caius Nocturne 17 | http://mutant.tech/ 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /assets/ms/holding_back_tears.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 11 | Mutant Standard emoji 2024.06 12 | 13 | 14 | 15 | 16 | Caius Nocturne 17 | http://mutant.tech/ 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /assets/ms/hushed.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 11 | Mutant Standard emoji 2024.06 12 | 13 | 14 | 15 | 16 | Caius Nocturne 17 | http://mutant.tech/ 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /assets/ms/pensive.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 11 | Mutant Standard emoji 2024.06 12 | 13 | 14 | 15 | 16 | Caius Nocturne 17 | http://mutant.tech/ 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /assets/ms/red_exclamation_mark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 11 | Mutant Standard emoji 2024.06 12 | 13 | 14 | 15 | 16 | Caius Nocturne 17 | http://mutant.tech/ 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /assets/ms/relaxed.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 11 | Mutant Standard emoji 2024.06 12 | 13 | 14 | 15 | 16 | Caius Nocturne 17 | http://mutant.tech/ 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /assets/ms/shush.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 11 | Mutant Standard emoji 2024.06 12 | 13 | 14 | 15 | 16 | Caius Nocturne 17 | http://mutant.tech/ 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /assets/ms/slight_smile.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 11 | Mutant Standard emoji 2024.06 12 | 13 | 14 | 15 | 16 | Caius Nocturne 17 | http://mutant.tech/ 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /assets/ms/smile_hearts.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 11 | Mutant Standard emoji 2024.06 12 | 13 | 14 | 15 | 16 | Caius Nocturne 17 | http://mutant.tech/ 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /assets/ms/smile_with_tear.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 11 | Mutant Standard emoji 2024.06 12 | 13 | 14 | 15 | 16 | Caius Nocturne 17 | http://mutant.tech/ 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /assets/ms/speaker_medium_volume.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 9 | Mutant Standard emoji 2024.06 10 | 11 | 12 | 13 | 14 | Caius Nocturne 15 | http://mutant.tech/ 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /assets/ms/speaker_muted.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 9 | Mutant Standard emoji 2024.06 10 | 11 | 12 | 13 | 14 | Caius Nocturne 15 | http://mutant.tech/ 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /assets/ms/starry_eyes.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 11 | Mutant Standard emoji 2024.06 12 | 13 | 14 | 15 | 16 | Caius Nocturne 17 | http://mutant.tech/ 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /assets/ms/sunglasses_face.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 11 | Mutant Standard emoji 2024.06 12 | 13 | 14 | 15 | 16 | Caius Nocturne 17 | http://mutant.tech/ 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /assets/ms/sweat_smile.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 11 | Mutant Standard emoji 2024.06 12 | 13 | 14 | 15 | 16 | Caius Nocturne 17 | http://mutant.tech/ 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /assets/ms/thinking.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 11 | Mutant Standard emoji 2024.06 12 | 13 | 14 | 15 | 16 | Caius Nocturne 17 | http://mutant.tech/ 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /assets/ms/tick.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 11 | Mutant Standard emoji 2024.06 12 | 13 | 14 | 15 | 16 | Caius Nocturne 17 | http://mutant.tech/ 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /assets/ms/unsure.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 11 | Mutant Standard emoji 2024.06 12 | 13 | 14 | 15 | 16 | Caius Nocturne 17 | http://mutant.tech/ 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /assets/ms/worried.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 11 | Mutant Standard emoji 2024.06 12 | 13 | 14 | 15 | 16 | Caius Nocturne 17 | http://mutant.tech/ 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /assets/ms/yawn.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 11 | Mutant Standard emoji 2024.06 12 | 13 | 14 | 15 | 16 | Caius Nocturne 17 | http://mutant.tech/ 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /assets/yapping.js: -------------------------------------------------------------------------------- 1 | const yap_sounds = { 2 | // these sounds and most of the yapping code are adapted from https://github.com/equalo-official/animalese-generator 3 | a: new Howl({ src: '/assets/audio/yap/a.wav' }), 4 | b: new Howl({ src: '/assets/audio/yap/b.wav' }), 5 | c: new Howl({ src: '/assets/audio/yap/c.wav' }), 6 | d: new Howl({ src: '/assets/audio/yap/d.wav' }), 7 | e: new Howl({ src: '/assets/audio/yap/e.wav' }), 8 | f: new Howl({ src: '/assets/audio/yap/f.wav' }), 9 | g: new Howl({ src: '/assets/audio/yap/g.wav' }), 10 | h: new Howl({ src: '/assets/audio/yap/h.wav' }), 11 | i: new Howl({ src: '/assets/audio/yap/i.wav' }), 12 | j: new Howl({ src: '/assets/audio/yap/j.wav' }), 13 | k: new Howl({ src: '/assets/audio/yap/k.wav' }), 14 | l: new Howl({ src: '/assets/audio/yap/l.wav' }), 15 | m: new Howl({ src: '/assets/audio/yap/m.wav' }), 16 | n: new Howl({ src: '/assets/audio/yap/n.wav' }), 17 | o: new Howl({ src: '/assets/audio/yap/o.wav' }), 18 | p: new Howl({ src: '/assets/audio/yap/p.wav' }), 19 | q: new Howl({ src: '/assets/audio/yap/q.wav' }), 20 | r: new Howl({ src: '/assets/audio/yap/r.wav' }), 21 | s: new Howl({ src: '/assets/audio/yap/s.wav' }), 22 | t: new Howl({ src: '/assets/audio/yap/t.wav' }), 23 | u: new Howl({ src: '/assets/audio/yap/u.wav' }), 24 | v: new Howl({ src: '/assets/audio/yap/v.wav' }), 25 | w: new Howl({ src: '/assets/audio/yap/w.wav' }), 26 | x: new Howl({ src: '/assets/audio/yap/x.wav' }), 27 | y: new Howl({ src: '/assets/audio/yap/y.wav' }), 28 | z: new Howl({ src: '/assets/audio/yap/z.wav' }), 29 | th: new Howl({ src: '/assets/audio/yap/th.wav' }), 30 | sh: new Howl({ src: '/assets/audio/yap/sh.wav' }), 31 | _: new Howl({ src: '/assets/audio/yap/space.wav' }) 32 | } 33 | 34 | async function yap(text, { 35 | letterCallback = () => {}, 36 | endCallback = () => {}, 37 | baseRate = 3.2, 38 | rateVariance = 1, 39 | } = {}) { 40 | 41 | const yap_queue = []; 42 | for (let i = 0; i < text.length; i++) { 43 | const char = text[i]; 44 | const lowerChar = char?.toLowerCase() 45 | const prevChar = text[i - 1] 46 | const prevLowerChar = prevChar?.toLowerCase() 47 | const nextChar = text[i + 1] 48 | const nextLowerChar = nextChar?.toLowerCase() 49 | 50 | if (lowerChar === 's' && nextLowerChar === 'h') { // test for 'sh' sound 51 | yap_queue.push({letter: char, sound: yap_sounds['sh']}); 52 | continue; 53 | } else if (lowerChar === 't' && nextLowerChar === 'h') { // test for 'th' sound 54 | yap_queue.push({letter: char, sound: yap_sounds['th']}); 55 | continue; 56 | } else if (lowerChar === 'h' && (prevLowerChar === 's' || prevLowerChar === 't')) { // test if previous letter was 's' or 't' and current letter is 'h' 57 | yap_queue.push({letter: char, sound: yap_sounds['_']}); 58 | continue; 59 | } else if (',?. '.includes(char)) { 60 | yap_queue.push({letter: char, sound: yap_sounds['_']}); 61 | continue; 62 | } else if (lowerChar === prevLowerChar) { // skip repeat letters 63 | yap_queue.push({letter: char, sound: yap_sounds['_']}); 64 | continue; 65 | } 66 | 67 | if (lowerChar.match(/[a-z.]/)) { 68 | yap_queue.push({letter: char, sound: yap_sounds[lowerChar]}) 69 | continue; // skip characters that are not letters or periods 70 | } 71 | 72 | yap_queue.push({letter: char, sound: yap_sounds['_']}) 73 | } 74 | 75 | function next_yap() { 76 | if (yap_queue.length === 0) { 77 | endCallback() 78 | return 79 | } 80 | let {sound, letter} = yap_queue.shift() 81 | sound.rate(Math.random() * rateVariance + baseRate) 82 | sound.volume(0.035) 83 | sound.once('end', next_yap) 84 | sound.play() 85 | sound.once('play', () => { 86 | letterCallback({sound, letter, length: yap_queue.length}) 87 | }) 88 | } 89 | 90 | next_yap(); 91 | } -------------------------------------------------------------------------------- /attribution.js: -------------------------------------------------------------------------------- 1 | // import { play_sound } from "./learn/script.js"; 2 | 3 | // containers to change the opacity for 4 | const oh_containers = location.pathname === '/learn/' 5 | ? [ 6 | // document.getElementById('back'), 7 | document.getElementById('options_container'), 8 | document.getElementById('mutant_container'), 9 | document.getElementById('image_container'), 10 | document.getElementById('tasks_container'), 11 | ] 12 | : [document.getElementById('index_container')]; 13 | // containers to disable user interaction for 14 | const in_containers = location.pathname === '/learn/' 15 | ? [ 16 | // document.getElementById('back'), 17 | document.getElementById('options_container'), 18 | document.getElementById('mutant_container'), 19 | document.getElementById('image_container'), 20 | ] 21 | : [document.getElementById('index_container')]; 22 | 23 | document.getElementById('attribution_a').onclick = function () { 24 | // if (location.pathname === '/learn/') { 25 | // play_sound('click'); 26 | // } 27 | document.getElementById('attribution_container').className = ''; 28 | oh_containers.forEach(c => c.classList.add('oh')); 29 | in_containers.forEach(c => c.classList.add('in')); 30 | document.getElementById('footer').className = 'oh in'; 31 | document.body.className = 'attribution_open'; 32 | } 33 | 34 | document.getElementById('close_attribution').onclick = function () { 35 | // if (location.pathname === '/learn/') { 36 | // play_sound('click'); 37 | // } 38 | document.getElementById('attribution_container').className = 'dn'; 39 | oh_containers.forEach(c => c.classList.remove('oh')); 40 | in_containers.forEach(c => c.classList.remove('in')); 41 | document.getElementById('footer').className = ''; 42 | document.body.className = ''; 43 | } -------------------------------------------------------------------------------- /favicon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 12 | 14 | 16 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /index.css: -------------------------------------------------------------------------------- 1 | #index_container { 2 | height:200px; 3 | } 4 | 5 | #tonic, #tonic_underline, #tonic_tagline, #buttons_container { 6 | display:flex; 7 | justify-content:center; 8 | align-items:center; 9 | } 10 | 11 | #tonic, #tonic_underline { 12 | height:50px; 13 | user-select:none; 14 | } 15 | 16 | #tonic_underline { 17 | color:#a7936e; 18 | } 19 | 20 | #tonic_tagline { 21 | height:25px; 22 | gap:5px; 23 | } 24 | 25 | #buttons_container { 26 | height:75px; 27 | gap:8px; 28 | } 29 | 30 | #login, #logout { 31 | width:100px; 32 | } 33 | 34 | #login { 35 | background-color:#706044; 36 | color:antiquewhite; 37 | } 38 | 39 | #login:hover { 40 | background-color:#8e7b56; 41 | border:2px solid #8e7b56; 42 | } -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Tonic 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 |
28 |

Tonic

29 |

30 |

Make a Jekyll theme. Show it to the world.

31 |
32 | 33 | 34 | 35 | 36 |
37 |
38 |
39 |

Attribution

40 |

Tonic is made with love by @lux

41 |

Patterns made by Dima Shiper and Hugo Loning

42 |

Mutant Standard emoji are licensed CC BY-NC-SA 4.0

43 |

Jekyll logo is licensed CC BY 4.0

44 |

Manufacturetocat image made by Haley Carroll via Octodex

45 |

Ruby logo is licensed CC BY-SA 2.5

46 |

HTML5 logo is licensed CC BY 3.0

47 |

Sass logo is licensed CC BY-NC-SA 3.0

48 | 49 |
50 | 55 |
56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | fetch('/auth', { 2 | credentials: 'include' 3 | }) 4 | .then(response => response.json()) 5 | .then(data => { 6 | if (data.auth) { 7 | document.getElementById('login').innerText = 'Play'; 8 | document.getElementById('login').className = ''; 9 | document.getElementById('login_a').href = window.location.origin + '/learn'; 10 | document.getElementById('logout').className = ''; 11 | document.getElementById('logout').onclick = () => { 12 | window.location.href = '/auth/logout'; 13 | }; 14 | } else { 15 | document.getElementById('login').innerText = 'Sign in with Slack'; 16 | document.getElementById('login').className = 'w150'; 17 | document.getElementById('logout').className = 'dn'; 18 | document.getElementById('login_a').href = 'https://hackclub.slack.com/oauth' 19 | + '?client_id=2210535565.8800685498865' 20 | + '&scope=' 21 | + '&user_scope=openid' 22 | + `&redirect_uri=${window.location.origin}/auth/slack` 23 | + '&state=' 24 | + '&granular_bot_scope=1' 25 | + '&single_channel=0' 26 | + '&install_redirect=' 27 | + '&tracked=1' 28 | + '&team='; 29 | } 30 | }); -------------------------------------------------------------------------------- /learn/Task.js: -------------------------------------------------------------------------------- 1 | export default class Task { 2 | constructor (opts) { 3 | this.name = opts.name; 4 | this.description = opts.description; 5 | this.group = opts.group; 6 | this.requires_group = opts.requires_group || null; 7 | this.requires_tasks = opts.requires_tasks || []; 8 | this.updates_on_reveal = opts.updates_on_reveal || {}; 9 | this.updates_on_complete = opts.updates_on_complete || {}; 10 | this.state = 0; 11 | return this; 12 | } 13 | with_callback (c) { 14 | this.callback = c; 15 | return this; 16 | } 17 | override (c) { 18 | this.callback = c; 19 | return this; 20 | } 21 | get id () { 22 | return `task_${this.name.split(' ').join('_').toLowerCase()}`; 23 | } 24 | get element () { 25 | return document.getElementById(this.id); 26 | } 27 | get icon_element () { 28 | return this.element.querySelector('img'); 29 | } 30 | get name_element () { 31 | return this.element.querySelector('a'); 32 | } 33 | get middot_element () { 34 | return this.element.querySelector('.task_middot'); 35 | } 36 | get description_element () { 37 | return this.element.querySelector('.task_description'); 38 | } 39 | } -------------------------------------------------------------------------------- /learn/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Tonic 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 18 |
19 |

20 | Music 21 | 22 |

23 | 27 |
28 |

29 |
30 | 31 |

32 |
33 | 34 | 35 |
36 |
37 |
38 | 39 | 40 |
41 |
42 |
43 |
44 | 45 |
46 |
47 |

48 |         
49 |       
50 |
51 |

Your tasks

52 |
53 |
54 |
55 |

Attribution

56 |

Tonic is made with love by @lux

57 |

Patterns made by Dima Shiper and Hugo Loning

58 |

Mutant Standard emoji are licensed CC BY-NC-SA 4.0

59 |

Jekyll logo is licensed CC BY 4.0

60 |

Manufacturetocat image made by Haley Carroll via Octodex

61 |

Ruby logo is licensed CC BY-SA 2.5

62 |

HTML5 logo is licensed CC BY 3.0

63 |

Sass logo is licensed CC BY-NC-SA 3.0

64 | 65 |
66 | 71 |
72 | 73 | 74 | 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /learn/tasks/404.js: -------------------------------------------------------------------------------- 1 | import Task from '../Task.js'; 2 | import { mutant, hide_image, show_image } from '../script.js'; 3 | 4 | // can't use a number :) 5 | const four_oh_four = new Task({ 6 | name: '404', 7 | description: "Let people know when a page is missing.", 8 | group: 'Going further', 9 | requires_tasks: ['The config file', 'Layouts', 'Includes', 'Sass'], 10 | }).with_callback(async () => { 11 | mutant.emote = 'slight_smile'; 12 | }) 13 | 14 | export default four_oh_four; -------------------------------------------------------------------------------- /learn/tasks/a_feature_of_your_own.js: -------------------------------------------------------------------------------- 1 | import Task from '../Task.js'; 2 | import { mutant, hide_image, show_image } from '../script.js'; 3 | 4 | const a_feature_of_your_own = new Task({ 5 | name: 'A feature of your own', 6 | description: "Time to get creative!", 7 | group: 'Going further', 8 | requires_tasks: ['More elements', '404', 'Liquid'], 9 | updates_on_complete: { 10 | 'The README file': 3, 11 | 'Element showcase': 3, 12 | 'Gems': 1, 13 | 'Using your theme': 1, 14 | }, 15 | }).with_callback(async () => { 16 | mutant.emote = 'slight_smile'; 17 | }) 18 | 19 | export default a_feature_of_your_own; -------------------------------------------------------------------------------- /learn/tasks/all.js: -------------------------------------------------------------------------------- 1 | import github_setup from "./github_setup.js"; 2 | import jekyll_setup from "./jekyll_setup.js"; 3 | import your_first_page from "./your_first_page.js"; 4 | import the_config_file from './the_config_file.js'; 5 | import layouts from './layouts.js'; 6 | import includes from './includes.js'; 7 | import sass from './sass.js'; 8 | import more_elements from "./more_elements.js"; 9 | import four_oh_four from "./404.js"; 10 | import liquid from "./liquid.js"; 11 | import a_feature_of_your_own from "./a_feature_of_your_own.js"; 12 | import the_readme_file from "./the_readme_file.js"; 13 | import element_showcase from "./element_showcase.js"; 14 | import gems from "./gems.js"; 15 | import using_your_theme from "./using_your_theme.js"; 16 | import hackatime_setup from "./hackatime_setup.js"; 17 | 18 | export default { 19 | github_setup, 20 | hackatime_setup, 21 | jekyll_setup, 22 | your_first_page, 23 | the_config_file, 24 | layouts, 25 | includes, 26 | sass, 27 | // more_elements, 28 | // four_oh_four, 29 | // liquid, 30 | // a_feature_of_your_own, 31 | // the_readme_file, 32 | // element_showcase, 33 | // gems, 34 | // using_your_theme, 35 | } -------------------------------------------------------------------------------- /learn/tasks/element_showcase.js: -------------------------------------------------------------------------------- 1 | import Task from '../Task.js'; 2 | import { mutant, hide_image, show_image } from '../script.js'; 3 | 4 | const element_showcase = new Task({ 5 | name: 'Element showcase', 6 | description: "All your theme's elements in one central place!", 7 | group: 'Show the world', 8 | }).with_callback(async () => { 9 | mutant.emote = 'slight_smile'; 10 | }) 11 | 12 | export default element_showcase; -------------------------------------------------------------------------------- /learn/tasks/gems.js: -------------------------------------------------------------------------------- 1 | import Task from '../Task.js'; 2 | import { mutant, hide_image, show_image } from '../script.js'; 3 | 4 | const gems = new Task({ 5 | name: 'Gems', 6 | description: "Use RubyGems to publish your Jekyll theme!", 7 | group: 'Show the world', 8 | requires_tasks: ['The README file', 'Element showcase'], 9 | }).with_callback(async () => { 10 | mutant.emote = 'slight_smile'; 11 | }) 12 | 13 | export default gems; -------------------------------------------------------------------------------- /learn/tasks/github_setup.js: -------------------------------------------------------------------------------- 1 | import Task from '../Task.js'; 2 | import { mutant, hide_image, show_image } from '../script.js'; 3 | 4 | const github_setup = new Task({ 5 | name: 'GitHub setup', 6 | description: 'Use GitHub to store the code for your theme!', 7 | group: 'Setting up', 8 | updates_on_complete: { 9 | 'Hackatime setup': 3, 10 | }, 11 | }).with_callback(async () => { 12 | mutant.emote = 'slight_smile'; 13 | await mutant.say('Before I can teach you how to build a Jekyll theme...'); 14 | await mutant.grinning.say("...we'll need a place for your Jekyll theme to live!"); 15 | await mutant.hushed.say("We're going to use a website called *GitHub* for this."); 16 | await mutant.thinking.say('Have you heard of *GitHub* before?'); 17 | await mutant.thinking.choice2({ 18 | option_a: 'Yes, I have', 19 | option_b: "No, I haven't", 20 | callback_a: async () => await mutant.grinning.say('Amazing!'), 21 | callback_b: async () => await mutant.grinning.say("That's okay!"), 22 | }); 23 | await mutant.thinking.say('You really only need to remember two things about GitHub.', { image: 'manufacturetocat' }); 24 | await mutant.hand_over_mouth_open_eyes.say('First, it lets you _share_ your code with others...'); 25 | await mutant.grinning.say("...and second, it lets you keep track of all the _changes_ you've made to your code over time!"); 26 | await mutant.slight_smile.say('Also, using GitHub will make it really easy for people to use your Jekyll theme on their site.'); 27 | await mutant.thinking.say('As you work on your tasks, I might ask you to open another page or add some code to your theme.', { image: null }); 28 | await mutant.hushed.say('When you do that, *remember not to close this page.*'); 29 | await mutant.smile_with_tear.say('Otherwise, you might have to start the whole task over again!'); 30 | await mutant.grinning.say('Sound good?'); 31 | await mutant.slight_smile.choice1({ 32 | option_a: 'I think so', 33 | callback_a: async () => await mutant.grinning.say('Fantastic!'), 34 | }); 35 | await mutant.thinking.say('Do you already have a GitHub account?'); 36 | await mutant.thinking.choice2({ 37 | option_a: 'Yes, I do', 38 | option_b: "No, I don't", 39 | callback_a: async () => await mutant.slight_smile.say("In that case, let's go to ^GitHub$https://github.com^ and *sign in*."), 40 | callback_b: async () => await mutant.grinning.say("In that case, let's go to ^GitHub$https://github.com^ and *sign up*!") 41 | }); 42 | await mutant.slight_smile.choice1({ 43 | option_a: 'I did it', 44 | callback_a: async () => await mutant.smile_hearts.say('Lovely!'), 45 | }); 46 | await mutant.slight_smile.say("We're going to create a *new repository* to store your Jekyll theme in.") 47 | await mutant.thinking.say("We'll be using a _template repository_ called *tonic-starter* as a baseline.", { image: 'public_template', image_width: 300 }); 48 | await mutant.slight_smile.say("Let's visit the ^tonic-starter$https://github.com/hackclub/tonic-starter^ repository now."); 49 | await mutant.slight_smile.choice1({ 50 | option_a: 'I did it', 51 | callback_a: async () => await mutant.grinning.say('Excellent!'), 52 | }) 53 | await mutant.thinking.say('Now, click *"Use this template"*, then click *"Create a new repository"*.', { image: 'use_this_template' }); 54 | await mutant.slight_smile.say("Make sure that _you're the owner_, then give your new repository a _fun name_.", { image: 'repository_name' }); 55 | await mutant.hushed.say("Don't just call it *\"my-theme\"*, like I've done here!"); 56 | await mutant.thinking.say('Make sure the repository is set to *Public* so anyone can see it...', { image: 'repository_visibility', image_width: 400 }); 57 | await mutant.grinning.say('Then, click *"Create repository"*!', { image: 'create_repository', image_width: 200 }); 58 | await mutant.slight_smile.say("Come back here when you're done and I'll ask you for the repository link.", { image: null }); 59 | await mutant.slight_smile.choice1({ 60 | option_a: 'All done', 61 | callback_a: async () => await mutant.grinning.say('Excellent!'), 62 | }); 63 | await mutant.thinking.say('What is the link to your GitHub repository?'); 64 | await mutant.thinking.text_entry({ 65 | placeholder: 'github.com/username/theme-name', 66 | exp: /^(https:\/\/)?github.com\/[\w-]+\/[\w-]+$/gm, 67 | callback: async () => await mutant.grinning.say('Looks good to me!'), 68 | }); 69 | await mutant.slight_smile.say('Great work getting that online.'); 70 | await mutant.grinning.say('Just one more thing before we move on!'); 71 | await mutant.thinking.say('Each time you upload changes to GitHub, your repository gains one *commit*.'); 72 | await mutant.thinking.say("I'm going to have you *regularly push commits* to the repository you just made..."); 73 | await mutant.thinking.say('...and give me *direct links* to them afterwards.'); 74 | await mutant.hushed.say('That means no uploading your entire theme all at once at the end!'); 75 | await mutant.grinning.say("To accomplish this, I'd like to have you create a *codespace*."); 76 | await mutant.slight_smile.say('This is a feature provided by GitHub that lets you develop your theme completely online.'); 77 | await mutant.hand_over_mouth_open_eyes.say("If you know what you're doing, it's possible to push commits without one..."); 78 | await mutant.grinning.say('However, I *strongly recommend* that you set one up!'); 79 | await mutant.slight_smile.say("It'll make installing Jekyll and other required tools *much easier*."); 80 | await mutant.hushed.say("If you aren't allowed to download things on your computer, it might even be your only option!"); 81 | await mutant.grinning.say("So, how do you create a codespace?"); 82 | await mutant.thinking.say("You'll want to go to your repository and click *\"Code\"*.", { image: 'code_button', image_width: 200 }); 83 | await mutant.thinking.say('Click *"Codespaces"*, then click *"Create codespace on main"*.', { image: 'create_codespace_on_main', image_width: 400 }); 84 | await mutant.grinning.say('This will open a fresh codespace in a new tab!'); 85 | await mutant.slight_smile.say("It'll take a couple of minutes to set itself up for the first time..."); 86 | await mutant.grinning.say("...but as soon as you see something like this, that means it's all done!", { image: 'shell_prompt' }); 87 | // TODO: use the same link that the user provided in text entry 88 | await mutant.slight_smile.say("Let's go to your repository and create a codespace now.", { image: null }); 89 | await mutant.slight_smile.choice1({ 90 | option_a: 'I did it', 91 | callback_a: async () => await mutant.grinning.say('Wonderful!'), 92 | }); 93 | await mutant.slight_smile.say("I'll show you how to push a commit from your codespace in a little while."); 94 | await mutant.grinning.say("Let's move on to the next task!"); 95 | }) 96 | 97 | export default github_setup; -------------------------------------------------------------------------------- /learn/tasks/hackatime_setup.js: -------------------------------------------------------------------------------- 1 | import Task from '../Task.js'; 2 | import { mutant, hide_image, show_image, show_code, hide_code } from '../script.js'; 3 | 4 | const hackatime_setup = new Task({ 5 | name: 'Hackatime setup', 6 | description: 'Use Hackatime to track time spent working on your theme!', 7 | group: 'Setting up', 8 | updates_on_complete: { 9 | 'Jekyll setup': 3, 10 | }, 11 | }).with_callback(async () => { 12 | mutant.emote = 'slight_smile'; 13 | await mutant.hand_over_mouth_open_eyes.say("Now that you've set up the Tonic starter and created a codespace..."); 14 | await mutant.grinning.say("...there's something that I'd like you to install inside it!"); 15 | await mutant.hushed.say('I want to have you track the *amount of time* you spend working on your theme...'); 16 | await mutant.thinking.say("...by setting up a tool called *Hackatime*."); 17 | await mutant.slight_smile.say("It doesn't really matter if you spend more or less time than others..."); 18 | await mutant.grinning.say("I'm happy to help, no matter how much time you need!"); 19 | await mutant.hand_over_mouth_open_eyes.say("I just want to make sure we're doing good work together..."); 20 | await mutant.hand_over_mouth.say('...and your amount of time spent is one piece of that puzzle.'); 21 | await mutant.hushed.say('Does that sound okay?'); 22 | await mutant.hushed.choice1({ 23 | option_a: 'Sure thing', 24 | callback_a: async () => await mutant.grin.say('Wonderful!'), 25 | }); 26 | await mutant.thinking.say('*Hackatime* has to connect to a tool called *WakaTime* in order to do any time tracking.'); 27 | await mutant.thinking.say("To set it up in your codespace, you'll need to install the *WakaTime extension*."); 28 | await mutant.thinking.say('First, open your codespace and click on this icon on the left side.', { image: 'extensions_icon', image_width: 50 }); 29 | await mutant.hushed.say('Then, search for *"WakaTime"*, and click *"Install"* on the top result.', { image: 'wakatime_extension', image_width: 300 }); 30 | await mutant.thinking.say("When you do this, you'll get a warning asking if you trust the publisher.", { image: 'trust_publisher', image_width: 400 }); 31 | await mutant.thinking.say('Click *"Trust Publisher & Install"* to install the extension!'); 32 | await mutant.grinning.say("Let me know when you've completed these steps.", { image: null }); 33 | await mutant.slight_smile.choice1({ 34 | option_a: 'All done', 35 | callback_a: async () => await mutant.grinning.say('Fantastic!'), 36 | }); 37 | await mutant.hushed.say('Now, we need to connect the WakaTime extension to *Hackatime*.'); 38 | await mutant.slight_smile.say("Let's go to ^Hackatime$https://hackatime.hackclub.com/^ and *sign in with Slack*."); 39 | await mutant.thinking.choice1({ 40 | option_a: 'I did it', 41 | callback_a: async () => await mutant.grinning.say('Excellent!'), 42 | }); 43 | await mutant.thinking.say("You'll want to click *Settings*, on the left side of the screen...", { image: 'hackatime_settings', image_width: 200 }); 44 | await mutant.thinking.say('...then click *Set up time tracking*, under _Time tracking wizard_.', { image: 'time_tracking_wizard', image_width: 400 }); 45 | await mutant.thinking.say('Then, click on *Advanced/Custom Setup*.', { image: 'advanced_custom_setup', image_width: 200 }); 46 | await mutant.hushed.say("Here, you'll be able find your *API key*.", { image: 'api_key', image_width: 400 }); 47 | await mutant.grinning.say('This string lets WakaTime know who you are!'); 48 | await mutant.hand_over_mouth_open_eyes.say('Copy *just the API key* from here (everything after *api@_key =*)...', { image: 'api_key_highlighted' }); 49 | await mutant.grinning.say("Then, we'll head back to your codespace to paste it."); 50 | await mutant.thinking.say('Press *Control+Shift+P* or *Command+Shift+P*, then search for *"API Key"* and press *Enter*.', { image: 'command_palette_api_key' }); 51 | await mutant.thinking.say('Then, paste your API key in the box that appears, and press *Enter* again.'); 52 | await mutant.grinning.say("Let me know when you've completed these steps.", { image: null }); 53 | await mutant.slight_smile.choice1({ 54 | option_a: 'All done', 55 | callback_a: async () => await mutant.grinning.say('Great!'), 56 | }); 57 | await mutant.hushed.say("Now, the WakaTime extension knows whose time to track, but not where to send it!"); 58 | await mutant.grinning.say('To fix this, the last thing we need to do is provide an *API URL*.'); 59 | await mutant.thinking.say('Go back to your codespace, and press *Control+Shift+P* or *Command+Shift+P* again...', { image: 'command_palette_api_url' }); 60 | await mutant.thinking.say('...but this time, search for *"API URL*" and press *Enter*.'); 61 | show_code('https://hackatime.hackclub.com/api/hackatime/v1'); 62 | await mutant.thinking.say("This is the URL you need to provide.", { image: null }); 63 | await mutant.grinning.say("It's a link that goes directly to the Hackatime servers!"); 64 | await mutant.slight_smile.say('As before, paste it in the box, then press *Enter*.'); 65 | await mutant.grinning.say("Let me know when you've completed these steps.") 66 | await mutant.slight_smile.choice1({ 67 | option_a: 'All done', 68 | callback_a: async () => await mutant.smile_hearts.say('Lovely!'), 69 | }); 70 | hide_code(); 71 | await mutant.hand_over_mouth_open_eyes.say('That was a lot of ground to cover...'); 72 | await mutant.grinning.say('But we made it through!'); 73 | await mutant.slight_smile.say('If all goes well, you should start to see a time on the bottom of the screen as you work.', { image: '30m', image_width: 100 }); 74 | await mutant.slight_smile.say("I'm going to trust that you were able to do this - I won't ask you for a link this time.", { image: null }); 75 | await mutant.hand_over_mouth_open_eyes.say('I just need you to promise me that you did it all...'); 76 | await mutant.hand_over_mouth_open_eyes.text_entry({ 77 | placeholder: 'I promise!', 78 | exp: /^I promise!$/gm, 79 | callback: async () => await mutant.grinning.say('Perfect!'), 80 | }); 81 | await mutant.slight_smile.say("Let's move on."); 82 | }) 83 | 84 | export default hackatime_setup; -------------------------------------------------------------------------------- /learn/tasks/includes.js: -------------------------------------------------------------------------------- 1 | import Task from '../Task.js'; 2 | import { mutant, hide_image, show_image, hide_code, show_code } from '../script.js'; 3 | 4 | const includes = new Task({ 5 | name: 'Includes', 6 | description: "Move parts of your theme into their own files.", 7 | group: 'Theme structure', 8 | updates_on_complete: { 9 | 'Sass': 3, 10 | } 11 | }).with_callback(async () => { 12 | mutant.emote = 'slight_smile'; 13 | await mutant.grinning.say('In the last task, I showed you how to add a basic layout to your Jekyll theme using HTML.'); 14 | await mutant.hand_over_mouth_open_eyes.say('As you add more features, however, that layout will become more difficult to manage.'); 15 | await mutant.thinking.say("Let's say you wanted to add some navigation to your theme, like in sporeball's theme *lifeblood*.", { image: 'lifeblood_navigation' }); 16 | await mutant.thinking.say("Wouldn't it be nice if you could split things up and keep all the navigation code in its own file?"); 17 | await mutant.thinking.say("That way, the main layout file could stay nice and clean."); 18 | await mutant.grinning.say('Luckily, Jekyll has a feature called *includes* that allows you to do exactly that!'); 19 | await mutant.thinking.say("All of your theme's includes live in a folder called *@_includes*.", { image: 'includes_folder', image_width: 200 }); 20 | await mutant.hand_over_mouth_open_eyes.say('Similar to layouts, you create an include by adding a *.html* file to this folder...'); 21 | await mutant.hand_over_mouth_open_eyes.say('...and you can have as many of them as you want.') 22 | await mutant.hand_over_mouth.say("This time, however, the files don't have to be full pages!"); 23 | await mutant.slight_smile.say("Let's open your codespace and create the *@_includes* folder now."); 24 | await mutant.slight_smile.choice1({ 25 | option_a: 'I did it', 26 | callback_a: async () => await mutant.grinning.say('Fantastic!'), 27 | }); 28 | await mutant.hushed.say("We're going to make an include for the HTML *head* tag.", { image: null }); 29 | await mutant.thinking.say('Rather than containing content you can see, like the *body* tag...'); 30 | await mutant.thinking.say("...the *head* tag allows you to _configure_ a page using settings you can't always see."); 31 | await mutant.grinning.say("It's sort of like HTML's version of the @_config.yml file!"); 32 | await mutant.slight_smile.say("Let's create a file inside the @_includes folder called *head.html*."); 33 | show_code( 34 | ` 35 | {{ page.title }} | {{ site.title }} 36 | 37 | 38 | 39 | ` 40 | ); 41 | await mutant.thinking.say("Here's the code I'd like you to put inside of it."); 42 | await mutant.thinking.say('The first line inside the head tag uses some keywords from Liquid to fill in the page title...'); 43 | await mutant.hand_over_mouth_open_eyes.say('...and the next two lines contain some settings that are used on almost every website.'); 44 | await mutant.thinking.say("In particular, the last line will help your Jekyll theme look good on both desktop and mobile devices."); 45 | await mutant.grinning.say("Let me know when you've added this code."); 46 | await mutant.slight_smile.choice1({ 47 | option_a: 'I did it', 48 | callback_a: async () => await mutant.grinning.say('Excellent!'), 49 | }); 50 | show_code(`{% include head.html %}`); 51 | await mutant.thinking.say('Next, open *default.html* in the *@_layouts* folder, and add this code on a new line above the *body* tag.'); 52 | await mutant.grinning.say("Let me know when you've added this code."); 53 | await mutant.slight_smile.choice1({ 54 | option_a: 'I did it', 55 | callback_a: async () => await mutant.smile_hearts.say('Lovely!'), 56 | }); 57 | hide_code(); 58 | await mutant.hand_over_mouth_open_eyes.say("Now, when you run *jekyll serve*, the page will still look the same...", { image: 'my_theme', image_width: 400 }); 59 | await mutant.hand_over_mouth.say('...but the title of the page will change, to match what we put in the include!', { image: 'title', image_width: 200 }); 60 | await mutant.thinking.say('Can you commit your changes and give me the link?', { image: null }); 61 | await mutant.thinking.text_entry({ 62 | placeholder: 'github.com/x/y/commit/...', 63 | exp: /^(https:\/\/)?github.com\/[\w-]+\/[\w-]+\/commit\/[0-9a-f]{40}$/gm, 64 | callback: async () => await mutant.grinning.say('Looks good to me!'), 65 | }); 66 | await mutant.slight_smile.say("Let's move on."); 67 | }) 68 | 69 | export default includes; -------------------------------------------------------------------------------- /learn/tasks/jekyll_setup.js: -------------------------------------------------------------------------------- 1 | import Task from '../Task.js'; 2 | import { mutant, hide_image, show_image, show_code, hide_code } from '../script.js'; 3 | 4 | const jekyll_setup = new Task({ 5 | name: 'Jekyll setup', 6 | description: 'Set up Jekyll so you can start building your theme!', 7 | group: 'Setting up', 8 | updates_on_complete: { 9 | 'Your first page': 3 10 | }, 11 | }).with_callback(async () => { 12 | mutant.emote = 'slight_smile'; 13 | await mutant.grinning.say('This task will be an easy one!'); 14 | await mutant.hand_over_mouth_open_eyes.say("Now that you've set up the Tonic starter and created a codespace..."); 15 | await mutant.grinning.say('...the last thing we need to do before we begin is install *Jekyll*!'); 16 | await mutant.thinking.say('Jekyll is based on a programming language called *Ruby*.', { image: 'ruby-logo-2x', image_width: 125 }); 17 | await mutant.thinking.say("If you wanted to work on your theme offline, you'd have to install Ruby yourself first..."); 18 | await mutant.hushed.say('...which can be a little tricky!'); 19 | await mutant.grinning.say('Luckily, your codespace already has it installed!'); 20 | await mutant.slight_smile.say("Let's go to your codespace now.", { image: null }); 21 | await mutant.slight_smile.choice1({ 22 | option_a: 'I have it open', 23 | callback_a: async () => await mutant.grinning.say('Great!'), 24 | }) 25 | await mutant.slight_smile.say("To install Jekyll, we'll need to run a couple of commands in your codespace's *terminal*."); 26 | await mutant.thinking.say("All you need to do is click on the *Terminal* tab in your codespace...", { image: 'terminal_tab', image_width: 400 }); 27 | show_code(`sudo gem install bundler jekyll`); 28 | await mutant.thinking.say('...type in this command, and press *Enter*.', { image: null }); 29 | await mutant.thinking.say('This will install both *Jekyll* and a tool called *Bundler* which is needed by the codespace.'); 30 | await mutant.thinking.say("After a minute or two, you'll see a line that reads _\"Successfully installed jekyll-4.4.1\"."); 31 | await mutant.grinning.say('That means Jekyll is ready to use!'); 32 | await mutant.slight_smile.say("Let me know when this command is done."); 33 | await mutant.slight_smile.choice1({ 34 | option_a: 'All done', 35 | callback_a: async () => await mutant.grinning.say('Wonderful!'), 36 | }); 37 | show_code(`bundle exec jekyll serve --watch`); 38 | await mutant.thinking.say('To see what the Tonic starter looks like, type in this command.'); 39 | await mutant.grinning.say("This is the command you'll use every time you want to see your theme in action!"); 40 | await mutant.slight_smile.say('The *--watch* flag will allow you to see your changes every time you refresh the page.'); 41 | await mutant.slight_smile.say("Let me know when this command is done."); 42 | await mutant.slight_smile.choice1({ 43 | option_a: 'All done', 44 | callback_a: async () => await mutant.grinning.say('Excellent!'), 45 | }); 46 | hide_code(); 47 | await mutant.relaxed.say("If you're crafty, you'll have seen this popup in the bottom right corner.", { image: 'application_is_available' }); 48 | await mutant.hand_over_mouth_open_eyes.say("If you missed that, there's another way to see what the popup is talking about."); 49 | await mutant.thinking.say('Click on the *Ports* tab, and find the port labeled *4000*.', { image: 'ports_tab' }); 50 | await mutant.thinking.say('Right-click on the *forwarded address*, then choose *Open in Browser*.', { image: 'forwarded_address' }); 51 | await mutant.grinning.say('The end result should be...', { image: 'tonic_starter', sleep_ms: 1500 }); 52 | await mutant.grimace.say("Well, that's not very exciting, is it?"); 53 | await mutant.grinning.say("Don't worry, though - in a little while I'll teach you how to make it look amazing!"); 54 | await mutant.slight_smile.say("Again, I'm going to trust that you were able to do this.", { image: null }); 55 | await mutant.hand_over_mouth_open_eyes.say('I just need you to promise me that you did it...'); 56 | await mutant.hand_over_mouth_open_eyes.text_entry({ 57 | placeholder: 'I promise!', 58 | exp: /^I promise!$/gm, 59 | callback: async () => await mutant.grinning.say('Perfect!'), 60 | }); 61 | await mutant.grinning.say("Let's move on!"); 62 | }) 63 | 64 | export default jekyll_setup; -------------------------------------------------------------------------------- /learn/tasks/layouts.js: -------------------------------------------------------------------------------- 1 | import Task from '../Task.js'; 2 | import { mutant, hide_image, show_image, hide_code, show_code } from '../script.js'; 3 | 4 | const layouts = new Task({ 5 | name: 'Layouts', 6 | description: 'Decide how each page should be structured.', 7 | group: 'Theme structure', 8 | updates_on_complete: { 9 | 'Includes': 3, 10 | } 11 | }).with_callback(async () => { 12 | mutant.emote = 'slight_smile'; 13 | await mutant.grinning.say('Earlier, I mentioned that you would be using *HTML* to create your theme.'); 14 | await mutant.hushed.say('But what is it, and what is it actually used for?'); 15 | await mutant.thinking.say('*HTML* is the language that websites are made of.', { image: 'html5', image_width: 150 }); 16 | await mutant.grinning.say('If you think of a website like a building, then HTML is like the foundation!'); 17 | await mutant.slight_smile.say("It may not look that pretty, but you can't build your site without it."); 18 | await mutant.thinking.say('HTML uses _tags_ to place content on a webpage.', { image: 'html_p', image_width: 400 }); 19 | await mutant.thinking.say('Each tag has an opening part, some content, and a closing part.'); 20 | await mutant.hand_over_mouth_open_eyes.say('Tags are allowed to contain other tags, like this...', { image: 'html_body' }); 21 | await mutant.grinning.say('...and if you build up enough of them, you get a complete page!', { image: 'html' }) 22 | await mutant.thinking.say('When you write individual pages using *Markdown*, like I showed you earlier...', { image: null }); 23 | await mutant.thinking.say('Jekyll has to convert them into HTML before it can show you the contents of the page.'); 24 | await mutant.slight_smile.say("In order to tell Jekyll how to make that conversion, you use what's called a *layout*!"); 25 | await mutant.thinking.say("All of your theme's layouts live in a folder called *@_layouts*.", { image: 'layouts_folder', image_width: 200 }); 26 | await mutant.hushed.say("The Tonic starter doesn't include this folder, so you'll have to create it yourself.") 27 | await mutant.thinking.say('You can create a new folder in your codespace by right-clicking underneath *tonic-starter.gemspec*...'); 28 | await mutant.thinking.say('...then clicking *"New Folder..."*.'); 29 | await mutant.slight_smile.say("Let's open your codespace and create the *@_layouts* folder now."); 30 | await mutant.slight_smile.choice1({ 31 | option_a: 'I did it', 32 | callback_a: async () => await mutant.grinning.say('Excellent!'), 33 | }); 34 | await mutant.slight_smile.say('Creating a layout is as simple as adding a *.html* file to the @_layouts folder.', { image: null }); 35 | await mutant.hand_over_mouth_open_eyes.say("To allow for different types of content, your theme can have as many layouts as you want."); 36 | await mutant.slight_smile.say("For now, though, let's right-click on the @_layouts folder and create just one file called *default.html*."); 37 | show_code( 38 | ` 39 | 40 | 41 | 42 | ` 43 | ); 44 | await mutant.thinking.say("Here's the code I'd like you to put inside of it."); 45 | await mutant.thinking.say('This creates an HTML page with nothing in it except a *body* to hold the contents.'); 46 | await mutant.grinning.say("Let me know when you've added this code."); 47 | await mutant.slight_smile.choice1({ 48 | option_a: 'I did it', 49 | callback_a: async () => await mutant.grinning.say('Fantastic!'), 50 | }); 51 | hide_code(); 52 | await mutant.hushed.say('But wait... how do we actually add our content?'); 53 | show_code('{{ content }}'); 54 | await mutant.slight_smile.say("There's a special keyword for that: *{{ content }}*."); 55 | await mutant.hand_over_mouth_open_eyes.say("This comes from a language called *Liquid*, which you'll learn about later."); 56 | await mutant.grinning.say('Add it inside the *body* tag, and Jekyll will fill in the content for each page automatically!'); 57 | await mutant.slight_smile.choice1({ 58 | option_a: 'I did it', 59 | callback_a: async () => await mutant.smile_hearts.say('Lovely!'), 60 | }); 61 | hide_code(); 62 | await mutant.slight_smile.say("I'll show you how to expand upon this layout in a little while."); 63 | await mutant.grinning.say("For now, let's use it on your theme's front page!"); 64 | show_code('layout: default'); 65 | await mutant.slight_smile.say('All you need to do is open *index.md* and add this line inside the front matter block.'); 66 | await mutant.slight_smile.choice1({ 67 | option_a: 'I did it', 68 | callback_a: async () => await mutant.grin.say('Wonderful!'), 69 | }); 70 | hide_code(); 71 | await mutant.hand_over_mouth_open_eyes.say('After all of these changes, your theme will still look exactly the same.'); 72 | await mutant.grinning.say("However, these changes will be helpful to us later on!"); 73 | await mutant.thinking.say('Can you commit your changes and give me the link?'); 74 | await mutant.thinking.text_entry({ 75 | placeholder: 'github.com/x/y/commit/...', 76 | exp: /^(https:\/\/)?github.com\/[\w-]+\/[\w-]+\/commit\/[0-9a-f]{40}$/gm, 77 | callback: async () => await mutant.grinning.say('Looks good to me!'), 78 | }); 79 | await mutant.slight_smile.say("Let's move on."); 80 | }) 81 | 82 | export default layouts; -------------------------------------------------------------------------------- /learn/tasks/liquid.js: -------------------------------------------------------------------------------- 1 | import Task from '../Task.js'; 2 | import { mutant, hide_image, show_image } from '../script.js'; 3 | 4 | const liquid = new Task({ 5 | name: 'Liquid', 6 | description: "Do more complex things with your Jekyll theme!", 7 | group: 'Going further', 8 | requires_tasks: ['The config file', 'Layouts', 'Includes', 'Sass'], 9 | }).with_callback(async () => { 10 | mutant.emote = 'slight_smile'; 11 | }) 12 | 13 | export default liquid; -------------------------------------------------------------------------------- /learn/tasks/more_elements.js: -------------------------------------------------------------------------------- 1 | import Task from '../Task.js'; 2 | import { mutant, hide_image, show_image } from '../script.js'; 3 | 4 | const more_elements = new Task({ 5 | name: 'More elements', 6 | description: "Learn about lists, code blocks, tables, and more.", 7 | group: 'Going further', 8 | requires_group: 'Theme structure', 9 | requires_tasks: ['The config file', 'Layouts', 'Includes', 'Sass'], 10 | }).with_callback(async () => { 11 | mutant.emote = 'slight_smile'; 12 | await mutant.hand_over_mouth_open_eyes.say('Back in *Your first page*, I showed you some of the most important Markdown elements...'); 13 | await mutant.slight_smile.say('...including plain, bold, and italic text, headings, and links.'); 14 | await mutant.grinning.say("However, those aren't the only things that Markdown provides!"); 15 | }) 16 | 17 | export default more_elements; -------------------------------------------------------------------------------- /learn/tasks/the_config_file.js: -------------------------------------------------------------------------------- 1 | import Task from '../Task.js'; 2 | import { mutant, hide_image, show_image } from '../script.js'; 3 | 4 | const the_config_file = new Task({ 5 | name: 'The config file', 6 | description: 'Apply settings to your entire theme.', 7 | group: 'Theme structure', 8 | requires_group: 'Setting up', 9 | updates_on_complete: { 10 | 'Layouts': 3, 11 | }, 12 | }).with_callback(async () => { 13 | mutant.emote = 'slight_smile'; 14 | await mutant.grinning.say("So, you've created a codespace and set up Jekyll inside of it."); 15 | await mutant.hand_over_mouth_open_eyes.say("At this point, you're probably wondering something..."); 16 | await mutant.hushed.say('*How is a Jekyll theme actually built?*') 17 | await mutant.slight_smile.say("This is the section of the task list where we'll find out."); 18 | await mutant.thinking.say('The Tonic starter is purposely missing several key parts of a complete Jekyll theme.'); 19 | await mutant.hand_over_mouth_open_eyes.say("Before I teach you how to add them, however..."); 20 | await mutant.hand_over_mouth.say("I should teach you about the one key part that's already there!"); 21 | await mutant.thinking.say("In your codespace, there's a file called *@_config.yml*.", { image: 'config_yml', image_width: 200 }); 22 | await mutant.thinking.say('Jekyll uses this file to apply settings to your entire theme.'); 23 | await mutant.grinning.say("Let's look inside!"); 24 | await mutant.thinking.say('The Tonic starter config includes four keys: *title*, *description*, *encoding*, and *exclude*.', { image: 'config_yml_contents', image_width: 400 }); 25 | await mutant.thinking.say('*title* and *description* are self-explanatory.'); 26 | await mutant.hand_over_mouth_open_eyes.say('*encoding* should always be set to *utf-8*.'); 27 | await mutant.slight_smile.say('Finally, *exclude* takes a list of files which should not be included when the site is served.'); 28 | await mutant.thinking.say("If you look in the *@_site* folder, you'll notice that the only file inside is *index.html*.", { image: 'site_contents', image_width: 200 }); 29 | await mutant.thinking.say('That means when you bring it online with *jekyll serve*, none of the other files will be accessible.'); 30 | await mutant.hushed.say('Does all of this make sense?', { image: null }); 31 | await mutant.hushed.choice1({ 32 | option_a: 'I think so', 33 | callback_a: async () => await mutant.grinning.say('Excellent!'), 34 | }); 35 | await mutant.hand_over_mouth_open_eyes.say("There's something wrong with your @_config.yml file right now.", { image: 'config_yml_contents', image_width: 400 }); 36 | await mutant.hushed.say('The title still says *"tonic-starter"*, and the description is empty!'); 37 | await mutant.hushed.say("Let's update the @_config.yml file to use your theme's name, and add a short description inside the quotes."); 38 | await mutant.grinning.say('Remember to commit your changes, too!'); 39 | await mutant.slight_smile.say("Let me know when you've done that, and I'll ask you for the link.", { image: null }); 40 | await mutant.slight_smile.choice1({ 41 | option_a: 'All done', 42 | callback_a: async () => await mutant.grinning.say('Wonderful!'), 43 | }); 44 | await mutant.thinking.say('What is the link to the commit you made?'); 45 | await mutant.thinking.text_entry({ 46 | placeholder: 'github.com/x/y/commit/...', 47 | exp: /^(https:\/\/)?github.com\/[\w-]+\/[\w-]+\/commit\/[0-9a-f]{40}$/gm, 48 | callback: async () => await mutant.grinning.say('Looks good to me!'), 49 | }); 50 | await mutant.hand_over_mouth_open_eyes.say('Before we move on, there are three more important things you should keep in mind.'); 51 | await mutant.thinking.say("First, every time you change the @_config.yml file, you'll have to *fully restart Jekyll* to see the changes."); 52 | await mutant.thinking.say('That means pressing *Control+C* in the terminal and running *bundle exec jekyll serve --watch* again.'); 53 | await mutant.grinning.say('Second, you can add your own options to the @_config.yml file!'); 54 | await mutant.thinking.say('For example, you might want to add an option to allow people to use a dark mode provided by your theme.', { image: 'enable_dark_theme' }); 55 | await mutant.grinning.say("This is not part of Jekyll by default - you'd have to write the code to make this happen yourself!") 56 | await mutant.slight_smile.say("I'll show you how to do that later on."); 57 | await mutant.thinking.say('Lastly, any option in the @_config.yml file can be *overwritten* by a site that uses it.', { image: 'footer', image_width: 400 }); 58 | await mutant.thinking.say('For example, look at this *footer* option from the *nimmoi* theme, by sporeball.'); 59 | await mutant.hand_over_mouth_open_eyes.say("On nimmoi's website, the footer links to _sporeball_'s website, since she's the one who made it..."); 60 | await mutant.hand_over_mouth.say('...but if you wanted to use nimmoi on your own site, you could use your own @_config.yml file to change it.'); 61 | await mutant.slight_smile.say("Let's move on.", { image: null }); 62 | }) 63 | 64 | export default the_config_file; -------------------------------------------------------------------------------- /learn/tasks/the_readme_file.js: -------------------------------------------------------------------------------- 1 | import Task from '../Task.js'; 2 | import { mutant, hide_image, show_image } from '../script.js'; 3 | 4 | const the_readme_file = new Task({ 5 | name: 'The README file', 6 | description: "Add more information to your GitHub repository!", 7 | group: 'Show the world', 8 | requires_group: 'Going further', 9 | }).with_callback(async () => { 10 | mutant.emote = 'slight_smile'; 11 | }) 12 | 13 | export default the_readme_file; -------------------------------------------------------------------------------- /learn/tasks/using_your_theme.js: -------------------------------------------------------------------------------- 1 | import Task from '../Task.js'; 2 | import { mutant, hide_image, show_image } from '../script.js'; 3 | 4 | const using_your_theme = new Task({ 5 | name: 'Using your theme', 6 | description: "Learn how to use your published theme!", 7 | group: 'Show the world', 8 | requires_tasks: ['Gems'], 9 | }).with_callback(async () => { 10 | mutant.emote = 'slight_smile'; 11 | }) 12 | 13 | export default using_your_theme; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tonic", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "server.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "node server.js" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/hackclub/tonic.git" 13 | }, 14 | "keywords": [], 15 | "author": "", 16 | "license": "ISC", 17 | "bugs": { 18 | "url": "https://github.com/hackclub/tonic/issues" 19 | }, 20 | "homepage": "https://github.com/hackclub/tonic#readme", 21 | "dependencies": { 22 | "cookie-parser": "^1.4.7", 23 | "express": "^5.1.0", 24 | "morgan": "^1.10.0" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const morgan = require('morgan'); 3 | const { promisify } = require('util'); 4 | const exec = promisify(require('child_process').exec) 5 | const fs = require('fs/promises'); 6 | const crypto = require('crypto'); 7 | const cookieParser = require('cookie-parser'); 8 | 9 | const app = express(); 10 | app.use(express.json()); 11 | app.use(morgan('dev')); 12 | app.use(cookieParser()); 13 | 14 | const port = process.env.PORT || 3000; 15 | const redirect_url = process.env.NODE_ENV === 'production' 16 | ? process.env.PRODUCTION_REDIRECT_URL 17 | : `http://localhost:${process.env.PORT}` 18 | 19 | // static files 20 | app.use('/assets', express.static('assets')); 21 | app.use('/learn', express.static('learn')); 22 | app.get('/', (req, res) => { 23 | res.sendFile(__dirname + '/index.html'); 24 | }); 25 | app.get('/index.css', (req, res) => { 26 | res.sendFile(__dirname + '/index.css'); 27 | }); 28 | app.get('/style.css', (req, res) => { 29 | res.sendFile(__dirname + '/style.css'); 30 | }); 31 | app.get('/attribution.js', (req, res) => { 32 | res.sendFile(__dirname + '/attribution.js'); 33 | }); 34 | app.get('/index.js', (req, res) => { 35 | res.sendFile(__dirname + '/index.js'); 36 | }); 37 | app.get("/favicon.svg", (req, res) => { 38 | res.sendFile(__dirname + '/favicon.svg'); 39 | }); 40 | 41 | app.get('/auth', (req, res) => { 42 | res.json({ auth: !!req.cookies.uid }); 43 | }); 44 | 45 | app.get('/auth/slack', async (req, res) => { 46 | const R = await fetch(`https://slack.com/api/oauth.v2.access?code=${req.query.code}&client_id=${process.env.CLIENT_ID}&client_secret=${process.env.CLIENT_SECRET}&redirect_uri=${redirect_url}/auth/slack`, { 47 | headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, 48 | method: 'POST', 49 | }).then(R => R.json()); 50 | if (R.ok && R.authed_user?.access_token) { 51 | res.cookie('uid', R.authed_user.id, { 52 | httpOnly: true, 53 | secure: process.env.NODE_ENV === 'production', 54 | // sameSite: 'lax', 55 | maxAge: 1000 * 60 * 60 * 24 * 30, // 30 days 56 | }); 57 | // Make internal request to /scrap 58 | // await fetch(`${redirect_url}/scrap`, { 59 | // method: 'POST', 60 | // headers: { 61 | // 'Content-Type': 'application/json', 62 | // 'Cookie': `uid=${R.authed_user.id}` 63 | // }, 64 | // body: JSON.stringify({ 65 | // task: 'Login', 66 | // text_entry: '-' 67 | // }) 68 | // }); 69 | } 70 | res.redirect('/'); 71 | }); 72 | 73 | app.post('/scrap', async (req, res) => { 74 | // sanity check 75 | if (req.cookies.uid === undefined) { 76 | res.status(500).json({ success: false, lost_id: true }); 77 | return; 78 | } 79 | // ... 80 | const task = req.body.task; 81 | const text_entry = req.body.text_entry; 82 | const R = await fetch(`https://api.airtable.com/v0/${process.env.AIRTABLE_BASE_ID}/${process.env.AIRTABLE_SCRAPS_TABLE_ID}`, { 83 | headers: { 84 | 'Authorization': `Bearer ${process.env.AIRTABLE_PAT}`, 85 | 'Content-Type': 'application/json' 86 | }, 87 | // method: 'PATCH', 88 | method: 'POST', 89 | body: JSON.stringify({ 90 | // performUpsert: { fieldsToMergeOn: ['Slack ID'] }, 91 | records: [ 92 | { 93 | fields: { 94 | 'Slack ID': req.cookies.uid, 95 | 'Task': task, 96 | 'Text Entry': text_entry, 97 | }, 98 | }, 99 | ], 100 | }), 101 | }).then(R => R.json()); 102 | console.log(R); 103 | if (R.error) { 104 | res.status(500).json({ success: false }) 105 | } else { 106 | res.status(200).json({ success: true }); 107 | } 108 | }); 109 | 110 | app.get('/scraps', async (req, res) => { 111 | const R = await fetch(`https://api.airtable.com/v0/${process.env.AIRTABLE_BASE_ID}/Scraps?fields%5B%5D=Task&filterByFormula=%7BSlack+ID%7D%3D%22${req.cookies.uid}%22`, { 112 | headers: { 113 | 'Authorization': `Bearer ${process.env.AIRTABLE_PAT}`, 114 | 'Content-Type': 'application/json' 115 | }, 116 | method: 'GET', 117 | }).then(R => R.json()); 118 | console.log(R); 119 | if (R.error) { 120 | res.status(500).json({ success: false }) 121 | } else { 122 | res.status(200).json({ success: true, ...R }); 123 | } 124 | }); 125 | 126 | app.get('/auth/logout', (req, res) => { 127 | res.cookie('uid', '', { 128 | httpOnly: true, 129 | secure: process.env.NODE_ENV === 'production', 130 | // sameSite: 'lax', 131 | expires: new Date(0) 132 | }); 133 | res.redirect('/'); 134 | }); 135 | 136 | app.listen(port, () => { 137 | console.log(`Server is running at http://localhost:${port}`); 138 | }); --------------------------------------------------------------------------------