361 Commits

Author SHA1 Message Date
jakob.scheid 2aa6b3794c feat(legal): create empty loader module 2026-05-29 23:39:25 +02:00
jakob.scheid 36bcf32a05 Move legal modules into a feature directory 2026-05-29 23:35:54 +02:00
jakob.scheid eaa8968f64 Fix formatting in the locale files 2026-05-29 21:03:59 +02:00
johannes.vos 0a36bfe4a5 add languages - titel and placeholder 2026-05-29 19:30:35 +02:00
johannes.vos 085dc065bb add placeholder in privacy 2026-05-29 18:58:08 +02:00
johannes.vos a50531593b add router link to privacy 2026-05-29 18:57:40 +02:00
johannes.vos 09da4c5577 create imprint.vue&privacypolicy.vue in views/legal 2026-05-29 18:48:26 +02:00
jakob.scheid 3b8c387c44 Merge pull request 'Move the search bar in the search results view into the navigation bar' (#82) from chore/searchbar into main
Deploy on dev / Deploy on dev (push) Successful in 33s
Reviewed-on: #82
Reviewed-by: Jakob Scheid
2026-05-29 18:30:05 +02:00
jakob.scheid d461da90f2 Make search query model cleaner 2026-05-29 18:28:02 +02:00
jakob.scheid 295bfc19e6 Increase navbar vertical padding 2026-05-29 18:16:28 +02:00
jakob.scheid 7a7f698b44 Set navbar height to prevent layout shifting when the search bar is shown when the search results view is visible 2026-05-29 18:13:39 +02:00
jakob.scheid 2fd010ddfa Remove redundant search bar component 2026-05-29 18:08:21 +02:00
jakob.scheid f72a2bf2b1 Remove unnecessary whitespaces 2026-05-29 16:18:40 +02:00
johannes.vos 926010f128 chore(search-results): clean up unused search props and imports 2026-05-29 15:56:47 +02:00
johannes.vos 98c954e361 feat(navbar): show search input only on search results page 2026-05-29 15:55:38 +02:00
johannes.vos 325551d253 copy/paste searchbar.vue code to searchbar-searchresults.vue (placeholder) 2026-05-29 15:42:09 +02:00
johannes.vos a33bc047fa move Searchbar-SearchResults.vue file to nav/components 2026-05-29 15:41:33 +02:00
johannes.vos 1421789e43 create empty Searchbar-SearchResults.vue file 2026-05-29 15:37:40 +02:00
jakob.scheid 27a22ce569 Merge pull request 'add settings configuration parser' (#78) from feature/settings-config-parser into main
Deploy on dev / Deploy on dev (push) Successful in 37s
Reviewed-on: #78
Reviewed-by: Jakob Schei <1+jakob.scheid@noreply.localhost>
2026-05-29 14:03:15 +02:00
jakob.scheid b28af20d11 Merge branch 'main' into feature/settings-config-parser 2026-05-29 14:02:59 +02:00
jakob.scheid c81de2dce3 Merge pull request 'Bug/Link-color' (#81) from bug/link-color into main
Deploy on dev / Deploy on dev (push) Successful in 32s
Reviewed-on: #81
Reviewed-by: Jakob Scheid
2026-05-29 13:22:44 +02:00
johannes.vos 1b60984b5c add style - make links white/black 2026-05-28 21:48:22 +02:00
johannes.vos 733bc2b16a add class to link 2026-05-28 21:47:46 +02:00
jakob.scheid 11800f6ef4 feat(settings): Remove old JSDoc typedefs 2026-05-26 19:57:36 +02:00
jakob.scheid b47ab0355e feat(settings): Remove parameter 'url' from the loading function of the settings composable 2026-05-26 19:55:45 +02:00
jakob.scheid 34a7cb3f2c feat(settings): Make setting default value optional 2026-05-26 19:51:45 +02:00
johannes.vos da76d14d15 WIP 2026-05-26 14:55:18 +02:00
johannes.vos 547bc241da feat(settings): use type field as discriminant in JSDoc type definitions 2026-05-26 14:55:17 +02:00
johannes.vos 4ef6008976 feat(settings): default allowMultiple to false if not specified 2026-05-26 14:55:11 +02:00
johannes.vos 035aa1aa77 feat(settings): replace fetch with dynamic import for settings.json 2026-05-26 14:53:52 +02:00
johannes.vos 462ae00506 feat(settings): move settings.json to src and remove example content 2026-05-26 14:53:52 +02:00
jakob.scheid 5d2064134b feat(settings): Make setting default value optional 2026-05-26 12:21:54 +02:00
johannes.vos aeb348fcfd feat(settings): add example settings.json to public/ 2026-05-26 11:37:34 +02:00
johannes.vos 37ca0baa6d add emty public/settings.json 2026-05-26 11:36:26 +02:00
johannes.vos a165c13d7c feat(settings): add useSettingsConfig composable 2026-05-26 11:35:35 +02:00
johannes.vos 1378813783 add emtpy useSettingsConfig.js 2026-05-26 11:35:26 +02:00
johannes.vos e376e9b362 feat(settings): add settings config loader/parser 2026-05-26 11:34:56 +02:00
johannes.vos d09514f71f add emtpy settingsParser.js 2026-05-26 11:34:40 +02:00
johannes.vos f6d72dbea3 feat(settings): add settings config validator 2026-05-26 11:34:14 +02:00
johannes.vos 0d7814d655 add emty settingsValidator.js 2026-05-26 11:33:58 +02:00
johannes.vos c387c1486f feat(settings): add JSDoc type definitions for settings config 2026-05-26 11:32:51 +02:00
johannes.vos 878b558603 add emty settingsConfig.js 2026-05-26 11:31:41 +02:00
jakob.scheid 0251d5c577 Merge pull request 'Restructure assets directory' (#76) from refactor/restructure-assets-directory into main
Deploy on dev / Deploy on dev (push) Successful in 32s
Reviewed-on: #76
Reviewed-by: Jakob Gregory
2026-05-23 13:29:04 +02:00
jakob.scheid 280dd9d7c0 Remove .gitkeep in the assets directory 2026-05-23 13:21:38 +02:00
jakob.scheid 8e2fc6bfa5 Move image assets into a subdirectory for images in the assets directory 2026-05-23 13:21:21 +02:00
jakob.scheid 69d752f79b Merge pull request 'Add base settings page' (#74) from feature/base-settings-page into main
Deploy on dev / Deploy on dev (push) Successful in 30s
Reviewed-on: #74
Reviewed-by: Jakob Gregory
2026-05-23 13:16:37 +02:00
jakob.scheid 4d62ca2b75 Merge branch 'main' into feature/base-settings-page 2026-05-23 13:16:26 +02:00
jakob.scheid f5b88df831 Merge pull request 'Remove license placeholders' (#75) from bugfix/remove-placeholders-in-license-files into main
Deploy on dev / Deploy on dev (push) Successful in 35s
Reviewed-on: #75
Reviewed-by: Jakob Gregory
2026-05-23 13:14:02 +02:00
jakob.scheid bd25409d9f Add license header to left sidebar layout 2026-05-23 12:08:05 +02:00
jakob.scheid fe20a618fe Add license header to sidebar 2026-05-23 12:07:45 +02:00
jakob.scheid 17b49cdb6e Add license header to settings view 2026-05-23 12:07:32 +02:00
jakob.scheid 287d7ad713 Add padding to the settings view header 2026-05-23 12:03:03 +02:00
jakob.scheid d3e4d54c57 Add padding to the sidebar 2026-05-23 11:58:53 +02:00
jakob.scheid ead8821b0a Use semantic HTML elements in the left sidebar layout 2026-05-23 11:57:23 +02:00
jakob.scheid d3e8a0125d Use semantic HTML elements in the sidebar 2026-05-23 11:56:55 +02:00
jakob.scheid 34b7cfbf5a Use semantic HTML elements in the settings view 2026-05-23 11:53:47 +02:00
jakob.scheid 1ebf5780d2 Move main content padding frm the app root component to the views 2026-05-23 11:51:49 +02:00
jakob.scheid f354c1867c Add left sidebar layout top border 2026-05-23 11:46:28 +02:00
jakob.scheid 0583de0dfa Add settings view page wrapper 2026-05-23 11:43:53 +02:00
jakob.scheid 2a5e79b55d Add padding to the main content in the left sidebar layout 2026-05-23 11:42:45 +02:00
jakob.scheid 4df51a970d Include sidebar in the left sidebar layout 2026-05-23 11:42:25 +02:00
jakob.scheid 7013c60a20 Remove top margin from footer 2026-05-23 11:40:58 +02:00
jakob.scheid 3623265ae0 Remove main content bottom padding 2026-05-23 11:40:32 +02:00
jakob.scheid fbefb2e7fd Rename base layout to left sidebar layout 2026-05-23 11:40:03 +02:00
jakob.scheid b0d3258369 Remove main content wrapper 2026-05-23 11:26:06 +02:00
jakob.scheid f65c3d58aa Add containers to components without a content container 2026-05-23 11:25:48 +02:00
jakob.scheid 33e9ca7cf1 Add check whether there is a sidebar component in the base layout 2026-05-23 10:45:28 +02:00
jakob.scheid def3a84e59 Use base layout in the app root component 2026-05-23 10:44:58 +02:00
jakob.scheid b48a487d2f Rename left sidebar layout to base layout 2026-05-23 03:54:53 +02:00
jakob.scheid 76815c6a93 Add sidebar to the settings view 2026-05-23 03:32:32 +02:00
jakob.scheid aa47015d3a Add sidebar right border 2026-05-23 03:30:18 +02:00
jakob.scheid 989664422c Add sidebar container 2026-05-23 03:28:59 +02:00
jakob.scheid 2ad95385d2 Add CSS grid container in the left sidebar layout 2026-05-23 03:27:44 +02:00
jakob.scheid b00aaabbb1 Add sidebar component slot 2026-05-23 03:13:52 +02:00
jakob.scheid 5104c4e8e7 Add left sidebar layout content 2026-05-23 03:13:34 +02:00
jakob.scheid d1c428eccd Rename file 2026-05-23 03:10:37 +02:00
jakob.scheid ed2b3224b4 Remove sidebar section component 2026-05-23 03:09:15 +02:00
jakob.scheid 6cc94b651f Bugfix: Fix spelling mistake in the sidebar section component 2026-05-23 03:07:40 +02:00
jakob.scheid 16ee164d76 Add sidebar section boilerplate 2026-05-23 02:41:04 +02:00
jakob.scheid 2989817278 Use sidebar component in left sidebar layout 2026-05-23 02:38:56 +02:00
jakob.scheid 0a65bcdae1 Add left sidebar layout boilerplate 2026-05-23 02:36:33 +02:00
jakob.scheid d5601b8fdb Add sidebar boilerplate 2026-05-23 02:35:16 +02:00
jakob.scheid be96541b3e Replace hard-coded settings link text in the footer with a translation 2026-05-23 02:29:06 +02:00
jakob.scheid ad8bbfa666 Update footer flexbox 2026-05-23 02:27:43 +02:00
jakob.scheid b324b93141 Reduce footer height 2026-05-23 02:25:58 +02:00
jakob.scheid 7bcdf77ca8 Add link to settings page in the footer 2026-05-23 02:25:36 +02:00
jakob.scheid 6726600c8e Add settings view heading 2026-05-23 02:22:40 +02:00
jakob.scheid 027ee2d191 Add settings route 2026-05-23 02:21:37 +02:00
jakob.scheid 6d5c7e4270 Add translations for 'settings' 2026-05-23 02:17:53 +02:00
jakob.scheid 8901967ab0 Add settings view boilerplate 2026-05-23 02:13:41 +02:00
jakob.scheid e177ed279c Remove license placeholder in the search results view 2026-05-23 02:10:02 +02:00
jakob.scheid 923942f1b1 Remove license placeholder in the footer 2026-05-23 02:09:39 +02:00
jakob.scheid 539991fac7 Remove license placeholder in the not found view 2026-05-23 02:09:18 +02:00
jakob.scheid 9e049bc5c9 Remove license placeholder in the logo graphic 2026-05-23 02:09:07 +02:00
jakob.scheid 495c2379ce Merge pull request 'Change Colors' (#65) from bug/colors into main
Deploy on dev / Deploy on dev (push) Successful in 31s
Reviewed-on: #65
Reviewed-by: Jakob Scheid
2026-05-23 02:05:53 +02:00
jakob.scheid c50fe25d04 Add CSS variables for light hover background 2026-05-22 21:13:05 +02:00
jakob.scheid 1300ab46a0 Use the --border CSS variable in the language switch button component 2026-05-22 17:51:56 +02:00
jakob.scheid 28fcfa1f92 Use --border variable in the footer 2026-05-22 17:50:13 +02:00
jakob.scheid f808525d56 Merge branch 'main' into bug/colors 2026-05-22 17:46:56 +02:00
jakob.scheid 1c7a0c0e58 Use the CSS variable for dark in the navbar right links 2026-05-22 17:46:35 +02:00
jakob.scheid 0eccd33919 Add CSS variable for blue box shadow 2026-05-22 17:45:46 +02:00
jakob.scheid 1f67e95735 Add CSS variable for gray box shadow 2026-05-22 17:44:06 +02:00
jakob.scheid 3c82baca67 Use variable for the search results view error message background 2026-05-22 17:35:22 +02:00
jakob.scheid 6d95601399 Add CSS variable for borders 2026-05-22 17:33:03 +02:00
jakob.scheid 9aecc195a9 Merge pull request 'Add more Languages and switch button' (#70) from feature/add-languages into main
Deploy on dev / Deploy on dev (push) Successful in 29s
Reviewed-on: #70
Reviewed-by: Jakob Scheid
2026-05-22 17:19:25 +02:00
jakob.scheid 2bf433621a Remove flags from language labels 2026-05-22 17:18:15 +02:00
jakob.scheid abbfd0ad9d Include flags in the language name translations instead of hard-coding it in the language switch button component 2026-05-22 15:20:08 +02:00
jakob.scheid 619d4065b3 Align right navbar links vertically centered 2026-05-22 14:20:49 +02:00
jakob.scheid 3ca938d65b Update border color of language button 2026-05-22 14:19:12 +02:00
jakob.scheid 666ecb4e98 Update border color of language dropdown 2026-05-22 14:17:25 +02:00
jakob.scheid 3af6cb3c16 Remove CSS font-size property 2026-05-21 23:33:22 +02:00
jakob.scheid 006bb5136e Add feature to close the language switching dropdown when the user clicks outside of it 2026-05-21 23:28:42 +02:00
jakob.scheid 258a4025bc Use translations in the translation files instead of hard-coded strings for the language names 2026-05-21 22:32:39 +02:00
jakob.scheid 420f47dca4 Format SUPPORTED_LANGUAGES more readable 2026-05-21 22:06:23 +02:00
jakob.scheid 6f164a0256 Remove unsupported languages from SUPPORTED_LANGUAGES 2026-05-21 22:06:02 +02:00
jakob.scheid b30aea57ad Add list with languages written from right to left 2026-05-21 22:04:58 +02:00
jakob.scheid be19a3c29e Speak English in a comment 2026-05-21 21:59:59 +02:00
jakob.scheid f08acad086 Address the user formally in the German translations 2026-05-21 21:57:05 +02:00
jakob.scheid 90f99e4240 Rename 'src/features/language/' to 'src/features/i18n/' 2026-05-21 15:45:11 +02:00
johannes.vos 46d92675eb save lanugage in local storage 2026-05-20 20:33:07 +02:00
johannes.vos dc532d3848 implement switch button 2026-05-20 20:31:45 +02:00
johannes.vos cb42c9d368 remove lanugages 2026-05-20 20:31:24 +02:00
johannes.vos 6fa3ba6298 add switch button 2026-05-20 20:26:51 +02:00
johannes.vos 7c8cf6406d add content in langauge files 2026-05-20 20:25:26 +02:00
johannes.vos 010d29c74e add emty LangaugeSwitchButton.bue file 2026-05-20 20:22:10 +02:00
johannes.vos 5a04e2a2f1 Add suported languages in i18n.js 2026-05-20 20:20:57 +02:00
johannes.vos a7ff5e2bf4 Add emty langauge json files 2026-05-20 20:17:25 +02:00
jakob.scheid 9e4c9febdb Merge pull request 'Use vue-i18n' (#68) from feature/vue-i18n into main
Deploy on dev / Deploy on dev (push) Successful in 32s
Reviewed-on: #68
Reviewed-by: Johannes D. Vos
2026-05-20 17:35:02 +02:00
jakob.scheid 2b136e30c8 Add translation for the slogan 2026-05-19 21:56:19 +02:00
jakob.scheid 16e69ff072 Add translations for the color scheme button 2026-05-19 21:53:33 +02:00
jakob.scheid c8f8022f44 Add translation for the back to search link on the not found view 2026-05-19 21:47:49 +02:00
jakob.scheid ffaa6200c3 Add translation for the page not found error 2026-05-19 21:46:58 +02:00
jakob.scheid a60526e6d7 Add translations for search errors 2026-05-19 21:43:54 +02:00
jakob.scheid 5d7181bea2 Add translations for error messages 2026-05-19 21:27:58 +02:00
jakob.scheid dd0f560bc8 Add translations for the search bar 2026-05-19 21:14:07 +02:00
jakob.scheid ced76720cc Add English translations JSON file 2026-05-19 21:07:44 +02:00
jakob.scheid 94fc328737 Add error handling if a locale to be loaded does not exist 2026-05-19 21:06:45 +02:00
jakob.scheid 2eb187ec1a Add function to load languages dynamically 2026-05-19 21:05:09 +02:00
jakob.scheid 2882f78990 Adapt the language to the browser language 2026-05-19 19:47:58 +02:00
jakob.scheid 78cde4641c Fix: Replace placeholer in the license header of 'src/i18n.js' with the actual values 2026-05-19 19:43:14 +02:00
jakob.scheid f37d403636 Add base i18n 2026-05-19 19:39:56 +02:00
jakob.scheid 09c645e657 Add dependency 'vue-i18n' 2026-05-19 19:25:59 +02:00
johannes.vos 66483a3a6a Add Border 2026-05-19 19:11:27 +02:00
johannes.vos d5c714cbd8 Change Colors of Footer 2026-05-19 19:09:51 +02:00
johannes.vos d8a4c0023b Change Colors of Footer 2026-05-19 19:07:24 +02:00
johannes.vos 5b8c13c8cf Remove Background 2026-05-19 18:59:39 +02:00
johannes.vos b172e3cf2b Change Backgorund Color 2026-05-19 18:57:18 +02:00
johannes.vos 46a2caf845 Make Border of Button visible / lighter in DarkMode 2026-05-19 18:55:01 +02:00
johannes.vos 63d14ddefd Change Black Varbiable to gray 2026-05-19 18:49:00 +02:00
jakob.scheid ba45c0d488 Merge pull request 'Add option to adapt color scheme to the system color scheme' (#61) from feature/dynamic-dark-mode into main
Deploy on dev / Deploy on dev (push) Successful in 37s
Reviewed-on: #61
Reviewed-by: Jakob Gregory
2026-05-18 16:55:40 +02:00
jakob.scheid a7d1cc0f62 Set text color of inputs according to the color scheme 2026-05-17 21:05:31 +02:00
jakob.scheid 28fe027517 Set CSS property 'color-scheme' according to the current color scheme 2026-05-17 21:01:48 +02:00
jakob.scheid 05b6a5d513 Remove duplicated CSS rule '#app' 2026-05-17 20:59:37 +02:00
jakob.scheid c78357b61d Use the term 'color scheme' instead of 'color theme' 2026-05-17 20:52:55 +02:00
jakob.scheid 678c41e990 Set dark mode dynamically 2026-05-17 20:43:13 +02:00
jakob.scheid 6fb3d95cd5 Set color scheme button text color to dark 2026-05-17 20:41:44 +02:00
jakob.scheid 7f5cbf5665 Update color variables stylesheet 2026-05-17 20:40:53 +02:00
jakob.scheid 555bdb0cfb Remove appHasDarkClass function 2026-05-17 20:38:19 +02:00
jakob.scheid cd67bf486f Add color theme button 'auto' state 2026-05-17 17:54:09 +02:00
jakob.scheid c1525cd2f2 Add auto fallbacks 2026-05-17 17:34:35 +02:00
jakob.scheid 5d98f27b6b Make color theme switch more flexible 2026-05-17 17:33:22 +02:00
jakob.scheid 170f188435 Move color theme toggle button into a separate component 2026-05-17 15:24:53 +02:00
jakob.scheid c7fd0fe132 Merge pull request 'Add license headers' (#58) from chore/license-headers into main
Deploy on dev / Deploy on dev (push) Successful in 28s
Reviewed-on: #58
Reviewed-by: Johannes D. Vos
2026-05-17 14:37:40 +02:00
jakob.scheid 3313ed40e6 Add license headers 2026-05-17 14:30:50 +02:00
jakob.scheid 50c0ff4a8c Merge pull request 'Add dev deployment workflow #49' (#57) from chore/gitea-actions-workflow into main
Deploy on dev / Deploy on dev (push) Successful in 27s
Reviewed-on: #57
Reviewed-by: Johannes D. Vos
2026-05-17 14:23:08 +02:00
jakob.scheid 5e6ff65e68 Merge branch 'main' into chore/gitea-actions-workflow 2026-05-17 13:29:40 +02:00
jakob.scheid 8d2937d4a0 Add dev deployment workflow upload 2026-05-17 13:19:04 +02:00
jakob.scheid 7273a732b1 Add dev deployment workflow build step 2026-05-17 13:18:49 +02:00
jakob.scheid 62c6784e2c Add dev deployment workflow dependencies installation step 2026-05-17 13:18:37 +02:00
jakob.scheid c4d32c2815 Add dev deployment workflow boilerplate 2026-05-17 13:18:21 +02:00
jakob.scheid ceda686d03 Merge pull request 'use logo' (#52) from feature/use-logo into main
Reviewed-on: #52
Reviewed-by: Jakob Scheid
2026-05-15 18:53:03 +02:00
jakob.scheid 792b4b80b8 Remove comment 2026-05-15 18:50:28 +02:00
johannes.vos 22e50adaa5 fix: import logo in SearchView and use :src binding 2026-05-15 18:22:47 +02:00
johannes.vos 8eab67b78f fix: import logo in SearchView and use :src binding 2026-05-15 18:22:47 +02:00
jakob.scheid 6e3ef7aae0 Merge branch 'main' into feature/use-logo 2026-05-15 18:08:52 +02:00
jakob.scheid 3ef2b5b089 Merge pull request 'Change icons to toggle color theme to something more simple' (#54) from ui/change-icons-nav into main
Reviewed-on: #54
Reviewed-by: Jakob Scheid
2026-05-15 18:06:57 +02:00
johannes.vos 551327b018 Change Icons of Toggle to more simpler 2026-05-15 17:47:15 +02:00
johannes.vos d7246b9aab change height of logo 2026-05-15 17:25:37 +02:00
johannes.vos 5eddee4d4d feat: replace text with logo image in Navbar 2026-05-15 17:24:50 +02:00
johannes.vos ae639dbe71 feat: replace h1 text with logo image in SearchView 2026-05-15 17:23:59 +02:00
johannes.vos 31dd40e1d7 add svg file 2026-05-15 17:22:54 +02:00
jakob.scheid 4bdfa0a872 Merge pull request 'add DarkMode with Button' (#51) from feature/dark-mode into main
Reviewed-on: #51
Reviewed-by: Jakob Scheid
2026-05-15 12:47:25 +02:00
johannes.vos 488186d795 fix: restore full-height flex layout after dark mode wrapper 2026-05-15 10:21:30 +02:00
johannes.vos 67adb17936 feat: persist dark mode preference in localStorage 2026-05-15 10:19:44 +02:00
johannes.vos 2af6c74696 style: add dark mode toggle button styles 2026-05-15 10:14:39 +02:00
johannes.vos 81cebb4636 feat: add dark mode toggle button to Navbar 2026-05-15 10:14:05 +02:00
johannes.vos d24e11b466 feat: inject isDark in Navbar 2026-05-15 10:13:36 +02:00
johannes.vos a0eaf36db3 add isDark state in App.vue 2026-05-15 10:12:23 +02:00
johannes.vos 0b609598f0 style: add dark mode CSS varivale 2026-05-15 10:09:56 +02:00
jakob.scheid fc23a6d185 Merge pull request 'Add NotFound.vue #48' (#50) from feature/not-found-page into main
Reviewed-on: #50
Reviewed-by: Jakob Scheid
2026-05-14 16:21:30 +02:00
johannes.vos cf7525b32a Add style for the content 2026-05-14 16:06:23 +02:00
johannes.vos 6175e3b0be Remove unnecessary Style 2026-05-14 16:05:13 +02:00
johannes.vos 22c3aafcc5 Remove header 2026-05-14 16:04:38 +02:00
johannes.vos 7b601416ad Make the error code more human-readable 2026-05-14 16:02:22 +02:00
johannes.vos 7098414db1 Remove <div class=search-content> 2026-05-14 15:59:27 +02:00
johannes.vos a45e208f9b Remove space 2026-05-14 15:58:08 +02:00
johannes.vos f152d39db8 fix: use camelCase for not-found route name 2026-05-14 15:55:58 +02:00
johannes.vos 08c3de5cb2 Add Link Style 2026-05-14 12:36:57 +02:00
johannes.vos 61e4f3d1f2 Rename class 'slogan' to 'error-message' 2026-05-14 12:27:15 +02:00
johannes.vos 6aca8a6087 Add RouterLink to Searchview.vue 2026-05-14 12:25:45 +02:00
johannes.vos fb5985dcb1 Remove unnecesary script 2026-05-14 12:24:02 +02:00
johannes.vos 78679fe03b Add script 2026-05-14 12:22:58 +02:00
johannes.vos 06f7037e11 chore: update package-lock.json 2026-05-14 12:22:08 +02:00
johannes.vos 9b04b00c11 merge: resolve conflict, add NotFound route 2026-05-14 12:21:48 +02:00
johannes.vos 2956c54496 Make .slogan bigger 2026-05-14 12:19:47 +02:00
johannes.vos ba6e8e652d Copy style from SearchView.vue 2026-05-14 12:17:12 +02:00
johannes.vos 237fcd33dc Remove unnecessary Form 2026-05-14 12:15:50 +02:00
johannes.vos d2302afdc4 Change Text in NotFound.vue 2026-05-14 12:15:10 +02:00
johannes.vos 6f93d42b02 Fix Router bug 2026-05-14 12:14:12 +02:00
johannes.vos 25958bf7bb Add Router 2026-05-14 12:12:36 +02:00
johannes.vos 8d93ba2638 Add template in NotFound.vue 2026-05-14 12:10:39 +02:00
johannes.vos 336a063d1e Add NotFound.vue 2026-05-14 12:08:55 +02:00
jakob.scheid cda4d58486 Merge pull request 'Add search form submit #32' (#45) from feature/search-form-submit into main
Reviewed-on: #45
Reviewed-by: Jakob Gregory
2026-05-14 11:29:18 +02:00
jakob.scheid e7986146a1 Merge branch 'main' into feature/search-form-submit 2026-05-14 11:28:42 +02:00
jakob.scheid d58845dad9 Merge pull request 'Use HTML minifier from vite-plugin-html #46' (#47) from chore/use-html-minifier into main
Reviewed-on: #47
Reviewed-by: Johannes D. Vos
2026-05-13 16:42:11 +02:00
jakob.scheid cd1ed0ab3f Include licenses for vite-plugin-html and its dependencies 2026-05-11 19:20:41 +02:00
jakob.scheid 40b68dd52d Use HTML minifier from vite-plugin-html 2026-05-11 19:18:19 +02:00
jakob.scheid c1f1099848 Use CSS variables 2026-05-11 18:41:25 +02:00
jakob.scheid 926c1fc463 Merge branch 'main' into feature/search-form-submit 2026-05-11 15:20:05 +02:00
jakob.scheid 3e605f60d0 Merge pull request 'Use CSS variables #33' (#44) from refactor/css-variables into main
Reviewed-on: #44
Reviewed-by: Jakob Gregory
2026-05-11 14:18:34 +02:00
jakob.scheid 49d4401d00 Add search results error message outline 2026-05-11 00:12:27 +02:00
jakob.scheid 587a97fa03 Add search results error message animation 2026-05-11 00:11:04 +02:00
jakob.scheid 4d5ddfd7ea Add search results error message 2026-05-11 00:10:49 +02:00
jakob.scheid c5af7366a9 Update search bar width at the start page 2026-05-11 00:06:45 +02:00
jakob.scheid cab550051f Add search bar with search query to the search results view 2026-05-10 19:47:23 +02:00
jakob.scheid d407c2f0e9 Add padding to the main content 2026-05-10 19:46:46 +02:00
jakob.scheid 261657d343 Move search form to the search bar component 2026-05-10 19:45:56 +02:00
jakob.scheid e3117110e9 Move flexbox to the start page view 2026-05-10 19:04:49 +02:00
jakob.scheid 1097694a8e Add router link to the startpage in the navbar 2026-05-10 17:27:57 +02:00
jakob.scheid 9f60c23c60 Add search submit callback 2026-05-10 17:22:11 +02:00
jakob.scheid 399d7caabc Add search query model 2026-05-10 17:11:35 +02:00
jakob.scheid 679adb4cf9 Use class instead of ID for the search form 2026-05-10 16:52:25 +02:00
jakob.scheid 8f48f3dcb5 Add page title router hook 2026-05-10 16:50:29 +02:00
jakob.scheid 8cdf16f144 Add search results route 2026-05-10 16:40:48 +02:00
jakob.scheid 24926b7312 Add search results view 2026-05-10 16:09:51 +02:00
jakob.scheid 2ed25157e3 Rename startpage route to startPage 2026-05-10 16:07:21 +02:00
jakob.scheid f7f8cda2a5 Add @ alias 2026-05-10 16:06:45 +02:00
jakob.scheid 95c383d58a Apply CSS variables to the search bar 2026-05-10 15:35:54 +02:00
jakob.scheid 933f5ace80 Bug fix: remove commas from the CSS variables starting with --black 2026-05-10 15:32:31 +02:00
jakob.scheid 7ea64524d1 Update light colors 2026-05-10 15:26:39 +02:00
jakob.scheid 6450c12600 Update dark colors 2026-05-10 15:24:44 +02:00
jakob.scheid 94cdf6aa99 Use CSS variables in the footer 2026-05-10 15:23:07 +02:00
jakob.scheid 2718227745 Update black shades 2026-05-10 15:21:37 +02:00
jakob.scheid 257b7ed886 Update white shades 2026-05-10 15:20:06 +02:00
jakob.scheid 0e1148094a Add background colors 2026-05-10 15:12:05 +02:00
jakob.scheid 61c3d2ff7c Add dark colors 2026-05-10 13:41:57 +02:00
jakob.scheid de20ae89fc Add light colors 2026-05-10 13:28:54 +02:00
jakob.scheid 13f026eaab Add dark colors 2026-05-10 13:28:15 +02:00
jakob.scheid 9cdca85a4d Add shades of white 2026-05-10 13:27:16 +02:00
jakob.scheid 69453b947d Add shades of black 2026-05-10 13:27:03 +02:00
jakob.scheid 0e8a49969a Use color variables 2026-05-10 13:22:32 +02:00
jakob.scheid 32bfcf6b33 Add color variables 2026-05-10 13:22:20 +02:00
jakob.scheid 7a09198e9e Merge pull request 'Set maximum width of the search bar #42' (#43) from feature/limit-searchbar-width into main
Reviewed-on: #43
Reviewed-by: Johannes D. Vos
2026-05-10 00:50:16 +02:00
jakob.scheid b1812764f6 Set maximum width for the search form 2026-05-08 20:00:06 +02:00
jakob.scheid e69bbf361b Bug fix: Search bar width did not include the additional padding at the left 2026-05-08 19:53:54 +02:00
jakob.scheid 6a9c5d4035 Merge pull request 'Add footer #27' (#41) from feature/footer into main
Reviewed-on: #41
Reviewed-by: Johannes D. Vos
2026-05-08 18:58:15 +02:00
jakob.scheid a243c0bf9a Merge branch 'main' into feature/footer 2026-05-07 23:32:43 +02:00
jakob.scheid 0da2d0df76 Add copyright note 2026-05-07 23:24:52 +02:00
jakob.scheid 3d9a509581 Merge pull request 'Fix search bar input field height #36' (#38) from bugfix/searchbar-input-field-height into main
Reviewed-on: #38
Reviewed-by: Johannes D. Vos
Reviewed-by: Jakob Gregory
2026-05-07 21:11:27 +02:00
jakob.scheid 6e7e512a9b Move .main-content container from the search view to App.vue 2026-05-07 21:10:55 +02:00
jakob.scheid 41500560f3 Avoid that there is any space between the footer and the viewport bottom 2026-05-07 20:09:47 +02:00
jakob.scheid a1ddf0d145 Make app container a flexbox 2026-05-07 20:08:53 +02:00
jakob.scheid 0f8d974d31 Remove .content-background container 2026-05-07 20:08:19 +02:00
jakob.scheid 703410e0e3 Add footer component boilerplate 2026-05-07 19:37:14 +02:00
jakob.scheid f2a941efca Merge branch 'main' into bugfix/searchbar-input-field-height 2026-05-07 19:13:04 +02:00
jakob.scheid 9799a40958 Merge pull request 'Add license headers in source code files #29' (#30) from feature/add-licenses into main
Reviewed-on: #30
Reviewed-by: Johannes D. Vos
2026-05-07 19:11:46 +02:00
jakob.scheid ecc6598167 Set search bar input height 2026-05-07 18:39:42 +02:00
jakob.scheid 4edc240b10 Rename CSS variable
Rename the variable --submit-button-content-height in the search bar
component to --content-height
2026-05-07 18:36:55 +02:00
jakob.scheid e4ba19bc9b Make searchbar higher 2026-05-07 18:24:15 +02:00
jakob.scheid 93aec1c44d Make searchbar wider 2026-05-07 18:22:51 +02:00
jakob.scheid bb13b22810 Add license headers in sourc code files 2026-05-07 17:56:24 +02:00
jakob.scheid 1a3d1080b9 Merge pull request 'Migrate to Vue.js' (#28) from feature/migrate-to-vue-js into main
Reviewed-on: #28
Reviewed-by: Jakob Gregory
2026-05-07 14:20:13 +02:00
jakob.scheid f846307eee Merge branch 'main' into feature/migrate-to-vue-js 2026-05-07 14:13:46 +02:00
jakob.scheid d54b913bfd Remove old index.html 2026-05-06 22:27:48 +02:00
jakob.scheid 3585b7fc1f Add navbar styles 2026-05-06 22:24:01 +02:00
jakob.scheid d4814edc65 Add common stylesheets 2026-05-06 22:20:52 +02:00
jakob.scheid 5ba4485bf5 Add main content container 2026-05-06 22:18:16 +02:00
jakob.scheid 6a36b8de68 Remove background 2026-05-06 21:20:32 +02:00
jakob.scheid 8703fe4555 Remove unnecessary with of form 2026-05-06 21:13:21 +02:00
jakob.scheid 59ffc4daff Remove width of the search wrapper 2026-05-06 21:06:58 +02:00
jakob.scheid d5e19b6aee Make component styles scoped 2026-05-06 20:39:11 +02:00
jakob.scheid a4bc7698c7 Add searchbar styles 2026-05-06 20:37:32 +02:00
jakob.scheid 4f8240457c Add header styles 2026-05-06 20:35:27 +02:00
jakob.scheid d454405cb7 Add content background 2026-05-06 20:34:13 +02:00
jakob.scheid 7892fe2ed6 Add searchbar component 2026-05-06 20:29:19 +02:00
jakob.scheid 06e3601d70 Add navbar component 2026-05-06 20:23:27 +02:00
jakob.scheid 2593b65baf Setup Vue router 2026-05-06 20:23:01 +02:00
jakob.scheid c5a06b6ae4 Setup Vue router 2026-05-06 20:20:31 +02:00
jakob.scheid 28c79ac203 Add vue-router dependency 2026-05-06 18:33:01 +02:00
jakob.scheid dd43667bed Update stylesheets directory 2026-05-06 18:17:56 +02:00
jakob.scheid f0c4bb9d06 Add notice 2026-05-06 18:13:04 +02:00
jakob.scheid 63685121f8 Add third-party licenses 2026-05-06 17:27:35 +02:00
jakob.scheid 703c4c1632 Set up NodeJS project 2026-05-06 17:02:34 +02:00
jakob.scheid 87c0e031f1 Add .gitignore 2026-05-06 16:42:40 +02:00
jakob.scheid 7fdcbfe3ee Merge pull request 'New Style' (#24) from feature/reworked-style into main
Reviewed-on: #24
Reviewed-by: Jakob Scheid
2026-05-06 15:51:57 +02:00
jakob.scheid d928f2d25a Remove unnecessary CSS rule 2026-05-06 15:51:21 +02:00
jakob.scheid 35915e8c7f Remove example links 2026-05-06 15:50:35 +02:00
jakob.scheid 01151d476c Remove background 2026-05-06 15:48:35 +02:00
jakob.scheid cc38b00033 Solve merge conflicts 2026-05-06 15:43:31 +02:00
jakob.scheid d50bf475b6 Make CSS border-radius and padding properties values dynamic 2026-05-06 15:17:11 +02:00
johannes.vos 0a28644dd7 Change Shadow 2026-05-06 15:05:12 +02:00
johannes.vos 377aa919cb Add Input Filed Shadow 2026-05-06 15:03:10 +02:00
johannes.vos b43bac4c79 Fix Background 2026-05-06 14:59:54 +02:00
johannes.vos 8d4c54b01b Change Background 2026-05-06 14:52:01 +02:00
johannes.vos 4497396a8f Fix Layout 2026-05-06 14:48:35 +02:00
johannes.vos 5f7838ebbb Fix Gradient 2026-05-06 14:46:55 +02:00
johannes.vos 8d4ef81b0f Move Slogan closer to h1 2026-05-06 14:44:57 +02:00
johannes.vos 4d78a742e1 Make h1 bigger 2026-05-06 14:40:31 +02:00
johannes.vos ddad350e6d Add text decoration underline when hover in navbar 2026-05-06 14:37:12 +02:00
johannes.vos 5696c0f992 Restructe Navbar 2026-05-06 14:35:48 +02:00
johannes.vos 755c297486 Allign Link to end 2026-05-06 14:29:03 +02:00
jakob.scheid 821afc6e7c Merge pull request 'New Style' (#20) from feature/reworked-style into main
Reviewed-on: #20
Reviewed-by: Jakob Scheid
2026-05-06 14:22:47 +02:00
jakob.scheid 45ef5b67f8 Merge branch 'main' into feature/reworked-style 2026-05-06 14:09:23 +02:00
jakob.scheid fd910db9e9 Merge pull request 'Update license to original license text' (#22) from chore/update-license into main
Reviewed-on: #22
Reviewed-by: Johannes D. Vos
2026-05-06 14:08:08 +02:00
jakob.scheid 01631afb73 Make CSS border-radius and padding properties values dynamic 2026-05-06 12:59:08 +02:00
jakob.scheid bac2718663 Update license to original license text 2026-05-05 19:30:50 +02:00
johannes.vos c7ea4d23af Gitea verify test 2026-05-05 19:11:59 +02:00
johannes.vos ceb4f74739 Test signed commit 2026-05-05 19:11:08 +02:00
johannes.vos 991eac065b Test signed commit 2026-05-05 18:31:00 +02:00
johannes.vos 123bd22d07 Revise Requirements 2026-05-04 21:10:23 +02:00
jakob.scheid c6559e478d Make CSS border-radius and padding properties values dynamic 2026-05-04 19:44:15 +02:00
johannes.vos 2fa206f866 Make Gradient bigger and Center Searchbar vertically 2026-05-04 18:43:54 +02:00
johannes.vos 95948f11ec Remove Border 2026-05-04 18:35:39 +02:00
johannes.vos 5b8d6d3e2e Add hero for Gradient in Background 2026-05-04 18:34:37 +02:00
johannes.vos b9cbdc7392 Search Button in Searchbar 2026-05-04 18:24:01 +02:00
johannes.vos 58d52f59da Make Searchbutton rounder 2026-05-04 18:17:29 +02:00
johannes.vos 8f5426d468 Make Searchbar 'rounder' 2026-05-04 18:12:22 +02:00
johannes.vos 4070a7d5ba Make header p smaller 2026-05-04 18:06:54 +02:00
johannes.vos 04b5b29cd5 Add Color Gradient to h1 2026-05-04 18:03:34 +02:00
johannes.vos 25e497ac57 Change Seekra to seekra in Footer 2026-05-04 18:00:27 +02:00
johannes.vos 691647ae63 Change Seekra to seekra 2026-05-04 17:59:02 +02:00
johannes.vos a75f6bcde6 Set Margin form Header p to Header h1 to 0 2026-05-04 17:56:58 +02:00
jakob.scheid 450fb33c0f Merge pull request 'Improve static resources directory structure' (#15) from refactor/css-directory into main
Reviewed-on: #15
Reviewed-by: Johannes Vos
2026-05-03 15:45:53 +02:00
jakob.scheid 7467a80bb6 Improve static resources directory structure 2026-05-02 23:25:40 +02:00
jakob.scheid 5333cc9a97 Merge pull request 'Add Navbar' (#14) from feature/add-navbar into main
Reviewed-on: #14
2026-05-02 23:16:02 +02:00
jakob.scheid 920e71d1a2 Remove example navbar items 2026-05-02 23:14:59 +02:00
jakob.scheid db404a389b Use classes instead of element selectors at the navbar 2026-05-02 23:13:02 +02:00
jakob.scheid 0f20b52aa9 Update directory structure for stylesheets 2026-05-02 23:11:20 +02:00
jakob.scheid ab37ca4137 Update directory structure for stylesheets 2026-05-02 23:08:57 +02:00
jakob.scheid 5d3278fdb1 Merge branch 'main' into feature/add-navbar 2026-05-02 23:05:26 +02:00
johannes.vos 02ee937ac9 Remove nav style from main.css 2026-05-02 19:54:51 +02:00
johannes.vos c93215e241 Update navbar styles 2026-05-02 19:51:34 +02:00
johannes.vos 83be71821f Add link to navbar.css 2026-05-02 19:48:55 +02:00
johannes.vos b564a5146e Create a seperate stylesheet for navabar 2026-05-02 19:45:31 +02:00
jakob.scheid f2b46c96de Merge pull request 'Format CSS' (#13) from style/format-more-readable into main
Reviewed-on: #13
2026-05-02 19:37:11 +02:00
jakob.scheid 14d219ce8a Format CSS 2026-05-02 19:18:40 +02:00
jakob.scheid 49948899b1 Merge pull request 'Add base styles' (#12) from feature/base-style into main
Reviewed-on: #12
Reviewed-by: Jakob Gregory
2026-05-02 18:21:11 +02:00
johannes.vos 23b2f78093 Add Navbar with style 2026-05-02 17:50:56 +02:00
jakob.scheid d27d66c6e4 Merge pull request 'Bug fix: Use <p> instead of <div> for the slogan' (#10) from bugfix/use-more-semantic-html-element-for-slogan into main
Reviewed-on: #10
Reviewed-by: Jakob Gregory
2026-05-02 17:43:14 +02:00
jakob.scheid 6e6e9cb1bc Bug fix: Use <p> instead of <div> for the slogan 2026-05-02 17:40:52 +02:00
jakob.scheid a79976036a Change styles directory 2026-05-02 17:30:56 +02:00
johannes.vos 36d584f238 Add Style - Align Items in Center - Make Searchbar widther 2026-05-02 17:19:20 +02:00
jakob.scheid 80ca6dcb5d Merge pull request 'Add searchbar' (#7) from feature/add-searchbar into main
Reviewed-on: #7
Reviewed-by: Jakob Scheid
2026-05-02 17:04:53 +02:00
johannes.vos 467b85b524 Add searchbar 2026-05-02 17:01:20 +02:00
jakob.scheid 0eb74a78d3 Merge pull request 'Add header and footer' (#4) from feature/ui-base into main
Reviewed-on: #4
Reviewed-by: Jakob Gregory
Reviewed-by: Johannes Vos
2026-05-02 16:54:03 +02:00
jakob.scheid 3b3614bd62 Add footer 2026-05-02 15:38:47 +02:00
jakob.scheid 1fd1fbba57 Add header 2026-05-02 15:38:33 +02:00
jakob.scheid cebf33fb47 Merge pull request 'Write description and name into README.md' (#2) from docs/update-readme into main
Reviewed-on: #2
Reviewed-by: Jakob Gregory
2026-05-02 14:29:37 +02:00
jakob.scheid 3fad06f9b5 Write description and name into README.md 2026-05-02 14:25:21 +02:00
jakob.scheid 47634f017e Merge pull request 'Add boilerplate' (#1) from feature/ui-boilerplate into main
Reviewed-on: #1
Reviewed-by: Jakob Gregory
2026-05-02 14:09:32 +02:00
jakob.scheid d4772dd35b Add boilerplate 2026-05-02 13:56:47 +02:00
55 changed files with 6281 additions and 55 deletions
+45
View File
@@ -0,0 +1,45 @@
name: Deploy on dev
on:
push:
branches:
- main
jobs:
deploy:
name: Deploy on dev
runs-on: node-minio
steps:
- uses: actions/checkout@v4
- name: Install dependencies
run: |
set -euo pipefail
npm install
- name: Build
run: |
set -euo pipefail
npm run build
- name: Set alias
run: mc alias set minio ${{ vars.S3_DEV_URL }} ${{ secrets.S3_DEV_ACCESS_KEY }} ${{ secrets.S3_DEV_SECRET_KEY }}
- name: Upload build artifacts to S3
run: |
set -euo pipefail
BUCKET=${{ vars.S3_DEV_BUCKET_NAME }}
BUILD_ID=$(date +%s)-$(git rev-parse --short HEAD)
echo "Build ID: $BUILD_ID"
printf "Copying files ... "
mc cp --recursive ./dist/ "minio/$BUCKET/builds/$BUILD_ID/"
echo "done"
printf "Update current build pointer ... "
echo "$BUILD_ID" | mc pipe "minio/$BUCKET/current"
echo "done"
+3
View File
@@ -0,0 +1,3 @@
.vscode/
node_modules/
dist/
+202
View File
@@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
+8
View File
@@ -0,0 +1,8 @@
Notice
This software is provided "as is", without warranty of any kind.
This software uses the Vue.js framework.
This software makes use of various open-source components.
See THIRD_PARTY_LICENSES for details.
+3
View File
@@ -0,0 +1,3 @@
# frontend
The frontend of the search engine Seekra.
+1647
View File
File diff suppressed because it is too large Load Diff
+26 -24
View File
@@ -1,26 +1,28 @@
<!DOCTYPE html> <!--
<html lang="de"> Copyright 2026 Seekra
<head>
<meta charset="UTF-8"> Licensed under the Apache License, Version 2.0 (the "License");
<meta name="viewport" content="width=device-width, initial-scale=1.0"> you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Seekra</title> <title>Seekra</title>
<link rel="stylesheet" href="style.css"> </head>
</head> <body>
<body> <div id="app"></div>
<header> <script type="module" src="/src/main.js"></script>
<h1>Seekra</h1> </body>
<h3>Build to search.</h3>
</header>
<form id="searchbar">
<input type="search" id="search" name="search"><br>
<input type="submit" value="Suchen">
</form>
<footer>
<span>Version 1.1</span>
<time datetime="2026-05-01">02/05/2026</time>
<time datetime="20:30">12:35 CEST</time>
</footer>
</body>
</html> </html>
+7
View File
@@ -0,0 +1,7 @@
{
"compilerOptions": {
"paths": {
"@/*": ["./src/*"]
}
}
}
+2256
View File
File diff suppressed because it is too large Load Diff
+22
View File
@@ -0,0 +1,22 @@
{
"name": "frontend",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
},
"dependencies": {
"terser": "^5.47.1",
"vue": "^3.5.32",
"vue-i18n": "^11.4.4",
"vue-router": "^5.0.6"
},
"devDependencies": {
"@vitejs/plugin-vue": "^6.0.6",
"vite": "^8.0.10",
"vite-plugin-html": "^3.2.2"
}
}
View File
+59
View File
@@ -0,0 +1,59 @@
<!--
Copyright 2026 Seekra
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<script setup>
import Navbar from './features/nav/components/Navbar.vue';
import Footer from './features/footer/components/Footer.vue';
import { useColorScheme } from './features/colorScheme/composables/useColorScheme';
import { ref, provide, watch } from 'vue';
const { getColorScheme, updateColorScheme } = useColorScheme();
const colorScheme = ref(getColorScheme());
provide('colorScheme', colorScheme);
watch(colorScheme, val => updateColorScheme(val))
</script>
<template>
<div
:style="{ colorScheme: colorScheme === 'auto' ? 'normal' : (colorScheme === 'dark' ? 'dark' : 'light')}"
:class="{ dark: colorScheme === 'dark', 'color-scheme-auto': colorScheme === 'auto' }"
id="app-wrapper"
>
<Navbar />
<router-view class="main-content" />
<Footer />
</div>
</template>
<style scoped>
.main-content {
--main-content-padding-x: 30px;
--main-content-padding-y: 40px;
--main-content-padding: var(--main-content-padding-y) var(--main-content-padding-x) 0;
flex-grow: 1;
}
#app-wrapper {
min-height: 100vh;
display: flex;
flex-direction: column;
background-color: var(--light-bg);
color: var(--dark);
}
</style>
+19
View File
@@ -0,0 +1,19 @@
<!--
Copyright 2026 Seekra
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<svg xmlns="http://www.w3.org/2000/svg" width="758.75" height="192.5">
<defs><linearGradient id="gradient" x1="0%" y1="0%" x2="100%" y2="0%"><stop offset="0%" style="stop-color:#4ba8eb"/><stop offset="100%" style="stop-color:#043485"/></linearGradient></defs>
<path stroke="url(#gradient)" stroke-width="15" fill="none" d=" M94 51 a43.75 43.75 0 0 0 -43.75 -43.75 a43.75 43.75 0 0 0 -43.75 43.75 a43.75 43.75 0 0 0 43.75 43.75 a43.75 43.75 0 0 1 43.75 43.75 a43.75 43.75 0 0 1 -43.75 43.75 a43.75 43.75 0 0 1 -43.75 -43.75 M138.75 95.25 h43.75 a43.75 43.75 0 0 0 43.75 -43.75 a43.75 43.75 0 0 0 -43.75 -43.75 a43.75 43.75 0 0 0 -43.75 43.75 v43.75 v43.75 a43.75 43.75 0 0 0 43.75 43.75 a43.75 43.75 0 0 0 43.75 -43.75 M270 95.25 h43.75 a43.75 43.75 0 0 0 43.75 -43.75 a43.75 43.75 0 0 0 -43.75 -43.75 a43.75 43.75 0 0 0 -43.75 43.75 v43.75 v43.75 a43.75 43.75 0 0 0 43.75 43.75 a43.75 43.75 0 0 0 43.75 -43.75 M401.25 0 v192.5 m0 -96.125 l87.5 -87.5 m-87.5 87.5 l87.5 87.5 M532.5 192.5 v-192.5 m0 51.25 a43.75 43.75 0 0 1 43.75 -43.75 a43.75 43.75 0 0 1 43.75 43.75 M663.75 50.25 a43.75 43.75 0 0 1 43.75 -43.75 a43.75 43.75 0 0 1 43.75 43.75 v43.75 v43.75 v43.75 m0 -43.75 a43.75 43.75 0 0 1 -43.75 43.75 a43.75 43.75 0 0 1 -43.75 -43.75 a43.75 43.75 0 0 1 43.75 -43.75 a43.75 43.75 0 0 1 43.75 43.75 v50"/></svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View File
@@ -0,0 +1,70 @@
<!--
Copyright 2026 Seekra
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<script setup>
import { inject } from 'vue';
import { useI18n } from 'vue-i18n';
const { t } = useI18n();
const colorScheme = inject('colorScheme');
const colorSchemeNextMapper = {
'light': 'dark',
'dark': 'auto',
'auto': 'light'
};
const colorSchemeIconMapper = {
'dark': '⏾',
'light': '☀',
'auto': '◐'
};
const getTooltipTranslation = function (colorScheme) {
return t(`preferences.colorScheme.switch.${colorSchemeNextMapper[colorScheme]}`);
};
</script>
<template>
<button class="color-scheme-button"
@click="colorScheme = colorSchemeNextMapper[colorScheme]"
:aria-label="getTooltipTranslation(colorScheme)"
:title="getTooltipTranslation(colorScheme)"
>
{{ colorSchemeIconMapper[colorSchemeNextMapper[colorScheme]] }}
</button>
</template>
<style scoped>
.color-scheme-button {
background: none;
border: 1.5px solid var(--border);
border-radius: 50%;
width: 36px;
height: 36px;
cursor: pointer;
font-size: 1rem;
display: flex;
align-items: center;
justify-content: center;
color: var(--dark);
}
.color-scheme-button:hover {
background: var(--light-hover);
}
</style>
@@ -0,0 +1,39 @@
/*
Copyright 2026 Seekra
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
export const useColorScheme = function () {
const getColorScheme = function () {
let colorScheme = localStorage.getItem('colorScheme') || 'auto';
if (!(colorScheme === 'dark' || colorScheme === 'light' || colorScheme === 'auto')) {
colorScheme = 'auto';
};
return colorScheme;
};
const updateColorScheme = function (newScheme) {
let actualNewScheme = newScheme;
if (!(actualNewScheme === 'dark' || actualNewScheme === 'light' || actualNewScheme === 'auto')) {
actualNewScheme = 'auto';
};
localStorage.setItem('colorScheme', actualNewScheme);
};
return {
getColorScheme,
updateColorScheme
};
};
+67
View File
@@ -0,0 +1,67 @@
<!--
Copyright 2026 Seekra
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<script setup>
import { useI18n } from 'vue-i18n';
const { t } = useI18n();
const startYear = 2026
const currentYear = new Date().getFullYear()
const copyrightPeriod =
startYear === currentYear
? `${currentYear}`
: `${startYear}-${currentYear}`
</script>
<template>
<footer class="global-footer">
<div class="footer-segment">
<RouterLink to="settings" class="link">
{{ t('preferences.settings') }}
</RouterLink>
</div>
<div class="footer-segment">
<div class="copyright-note">
&copy; {{ copyrightPeriod }} Seekra
</div>
</div>
</footer>
</template>
<style scoped>
.global-footer {
--padding-y: 16px;
}
.footer-segment {
padding: var(--padding-y);
background-color: var(--light-bg);
border-top: 1px solid var(--border);
}
.global-footer a {
color: var(--dark);
}
.copyright-note {
display: flex;
justify-content: center;
}
</style>
@@ -0,0 +1,151 @@
<!--
Copyright 2026 Seekra
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<script setup>
import { ref } from 'vue';
import { useI18n } from 'vue-i18n';
import { loadLanguage, LANGUAGES_RTL, SUPPORTED_LANGUAGES } from '@/i18n';
const { t, locale } = useI18n();
const isOpen = ref(false);
const languageDropdown = ref(null);
async function selectLanguage(code) {
await loadLanguage(code);
localStorage.setItem('locale', code);
document.documentElement.lang = code;
document.documentElement.dir = LANGUAGES_RTL.includes(code) ? 'rtl' : 'ltr';
close();
};
const close = function () {
document.removeEventListener('click', closeWrapperOnClickOutsite);
isOpen.value = false;
};
const closeWrapperOnClickOutsite = function (e) {
if (languageDropdown.value) {
if (!languageDropdown.value.contains(e.target)) {
close();
};
};
};
const open = function () {
if (!isOpen.value) {
isOpen.value = true;
setTimeout(() => {
document.addEventListener('click', closeWrapperOnClickOutsite);
}, 0);
};
};
</script>
<template>
<div class="language-switch" tabindex="-1">
<button
class="language-button button"
@click="open"
:aria-expanded="isOpen"
aria-haspopup="listbox"
>
<span class="lang-code">{{ t(`preferences.locale.languages.${locale}`) }}</span>
<span class="chevron" :class="{ open: isOpen }"></span>
</button>
<ul v-if="isOpen" ref="languageDropdown" class="language-dropdown" role="listbox">
<li
v-for="lang in SUPPORTED_LANGUAGES"
:key="lang"
role="option"
:aria-selected="lang === locale"
:class="{ active: lang === locale }"
@click="selectLanguage(lang)"
>
<span class="lang-label">{{ t(`preferences.locale.languages.${lang}`) }}</span>
</li>
</ul>
</div>
</template>
<style scoped>
.language-switch {
position: relative;
}
.language-button {
display: flex;
align-items: center;
gap: 6px;
background: none;
border: 1px solid var(--border);
border-radius: 6px;
padding: 4px 10px;
cursor: pointer;
color: var(--dark);
}
.language-button:hover {
background-color: var(--light-hover);
}
.chevron {
font-size: 0.75rem;
transition: transform 0.2s;
display: inline-block;
}
.chevron.open {
transform: rotate(180deg);
}
.language-dropdown {
position: absolute;
right: 0;
top: calc(100% + 6px);
background-color: var(--light-bg);
border: 1px solid var(--border);
border-radius: 8px;
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);
list-style: none;
margin: 0;
padding: 6px 0;
min-width: 160px;
z-index: 100;
}
.language-dropdown li {
display: flex;
align-items: center;
gap: 10px;
padding: 8px 16px;
cursor: pointer;
font-size: 0.9rem;
}
.language-dropdown li:hover {
background-color: var(--light-hover);
}
.language-dropdown li.active {
font-weight: bold;
}
.flag {
font-size: 1.1rem;
}
</style>
+15
View File
@@ -0,0 +1,15 @@
/*
Copyright 2026 Seekra
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
+15
View File
@@ -0,0 +1,15 @@
/*
Copyright 2026 Seekra
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
@@ -0,0 +1,69 @@
```vue
<!--
Copyright 2026 Seekra
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<script setup>
import { computed } from 'vue';
import { useI18n } from 'vue-i18n';
// 1. ALLE Sprachen importieren (Verhindert den ReferenceError)
import de from '@/legal/privacy/de.md?raw';
import en from '@/legal/privacy/en.md?raw';
import fr from '@/legal/privacy/fr.md?raw';
import es from '@/legal/privacy/es.md?raw';
import it from '@/legal/privacy/it.md?raw';
import pt from '@/legal/privacy/pt.md?raw';
const { locale } = useI18n();
const content = computed(() => {
const map = {
de,
en,
fr,
es,
it,
pt
};
// Falls eine Sprache mal nicht existiert, nutzen wir 'de' oder 'en' als Fallback
return map[locale.value] || de;
});
</script>
<template>
<main class="privacy-policy-content main-content-padding">
<h1>{{ $t('legal.privacy.title') }}</h1>
<div class="markdown-body">{{ content }}</div>
</main>
</template>
<style scoped>
.privacy-policy-content {
max-width: 900px;
margin: 0 auto;
padding-top: 40px;
padding-bottom: 40px;
}
/* Sorgt dafür, dass die Zeilenumbrüche aus den .md Dateien erhalten bleiben */
.markdown-body {
white-space: pre-wrap;
font-family: inherit;
line-height: 1.6;
}
</style>
+95
View File
@@ -0,0 +1,95 @@
<!--
Copyright 2026 Seekra
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<script setup>
import { ref, watch } from 'vue';
import { useRoute } from 'vue-router';
import ColorSchemeButton from '@/features/colorScheme/components/ColorSchemeButton.vue';
import LanguageSwitchButton from '@/features/i18n/components/LanguageSwitchButton.vue';
import logo from '@/assets/images/logo.svg';
import Searchbar from '@/features/search/components/Searchbar.vue';
const route = useRoute();
const searchQueryModel = defineModel();
watch(() => route.name, name => {
searchQueryModel.value = name === 'searchResults' ? route.query.q || '' : '';
});
watch(() => route.query.q, q => {
if (route.name === 'searchResults') {
searchQueryModel.value = q || '';
}
});
</script>
<template>
<nav class="global-nav">
<RouterLink to="/" class="link button link">
<img :src="logo" alt="Seekra" class="nav-logo" />
</RouterLink>
<Searchbar
v-if="route.name === 'searchResults'"
class="search-bar"
v-model="searchQueryModel"
auto-submit
/>
<ul class="right-links">
<li>
<LanguageSwitchButton />
</li>
<li>
<ColorSchemeButton />
</li>
</ul>
</nav>
</template>
<style scoped>
.global-nav {
display: flex;
justify-content: space-between;
align-items: center;
padding: 18px 40px;
height: 42px;
}
.global-nav .right-links {
display: flex;
gap: 30px;
list-style: none;
margin: 0;
padding: 0;
align-items: center;
}
.global-nav .right-links a {
text-decoration: none;
color: var(--dark);
}
.global-nav .right-links a:hover{
text-decoration: underline;
}
.nav-logo {
height: 24px;
width: auto;
}
.search-bar {
width: 70%;
}
</style>
@@ -0,0 +1,94 @@
<!--
Copyright 2026 Seekra
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<script setup>
const searchQuery = defineModel();
import { useRouter } from 'vue-router';
import { useI18n } from 'vue-i18n';
const { t } = useI18n();
const router = useRouter()
const props = defineProps(['autoSubmit'])
const submitSearch = function () {
if (props.autoSubmit !== undefined) {
router.push({
name: 'searchResults',
query: { q: searchQuery.value }
});
};
}
</script>
<template>
<div>
<form @submit.prevent="submitSearch">
<div class="search-wrapper">
<input
v-model="searchQuery"
type="search"
:placeholder="t('search.searchBar.placeholder')"
required
/>
<button type="submit" class="search-button">{{ t('search.searchBar.submit') }}</button>
</div>
</form>
</div>
</template>
<style scoped>
.search-wrapper {
--submit-button-padding-y: 8px;
--content-height: 32px;
--padding: 4px;
--padding-left: calc(var(--content-height) + var(--padding));
display: flex;
align-items: center;
border: 1.5px solid var(--border);
box-shadow: 0 0px 32px var(--blue-box-shadow);
border-radius: calc(var(--content-height) * 0.5 + var(--submit-button-padding-y) + var(--padding));
padding: var(--padding);
padding-left: var(--padding-left);
width: calc(100% - var(--padding-left));
}
.search-wrapper input {
border: none;
outline: none;
width: 100%;
font-size: 1rem;
background: transparent;
height: calc(var(--content-height) + 2 * var(--submit-button-padding-y));
}
.search-button {
font-size: 1rem;
height: calc(var(--content-height) + 2 * var(--submit-button-padding-y));
border-radius: calc(var(--content-height) * 0.5 + var(--submit-button-padding-y));
border: none;
padding: var(--submit-button-padding-y) 20px;
background: var(--primary-color);
color: var(--white);
cursor: pointer;
white-space: nowrap;
}
.search-button:hover {
background: var(--primary-color-l-1);
}
</style>
@@ -0,0 +1,99 @@
<!--
Copyright 2026 Seekra
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<script setup>
import { useI18n } from 'vue-i18n';
const { t } = useI18n();
</script>
<template>
<div class="main-content-padding">
<div class="search-results-error-message-container">
<div class="search-results-error-message">
<p>{{ t('search.error.searchNotAvailable') }}</p>
<p>{{ t('error.tryAgainToAnotherTime') }}</p>
</div>
</div>
<div class="search-results-container">
</div>
</div>
</template>
<style scoped>
.search-bar {
width: 50%;
}
@media (max-width: 67.5rem) {
.search-bar {
width: 100%;
}
}
.search-results-container {
margin-top: 56px;
}
.search-results-error-message-container {
--error-message-height: 100px;
--error-message-padding: 2em;
display: flex;
justify-content: center;
width: calc(100% - 2 * var(--main-content-padding-x));
position: absolute;
top: calc(50vh - 0.5 * var(--error-message-height) - var(--error-message-padding));
}
.search-results-error-message {
padding: var(--error-message-padding);
font-size: 18px;
box-shadow: 0px 0px 42px var(--gray-box-shadow);
border-radius: 28px;
text-align: center;
background-color: var(--light-bg);
animation: fade-in 0.8s ease-out;
height: var(--error-message-height);
display: flex;
justify-content: center;
flex-direction: column;
outline: 1px solid var(--border);
}
@media (max-width: 48rem) {
.search-results-error-message-container {
--error-message-height: 150px;
}
.search-results-error-message {
padding: 1em;
width: 100%;
}
}
@keyframes fade-in {
0% {
opacity: 0;
}
10% {
opacity: 0;
transform: scale(1.02);
}
100% {
opacity: 100;
transform: scale(1);
}
}
</style>
@@ -0,0 +1,48 @@
/*
Copyright 2026 Seekra
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import { ref, readonly } from 'vue';
import { loadSettingsConfig } from '../utils/settingsParser.js';
const config = ref(null);
const error = ref(null);
const loading = ref(false);
/**
* Provides reactive access to the parsed settings configuration.
* The config is loaded once and shared across all consumers.
*/
export function useSettingsConfig() {
async function load() {
loading.value = true;
error.value = null;
try {
config.value = await loadSettingsConfig();
} catch (e) {
error.value = e.message;
config.value = null;
} finally {
loading.value = false;
}
}
return {
config: readonly(config),
error: readonly(error),
loading: readonly(loading),
load,
};
}
+3
View File
@@ -0,0 +1,3 @@
{
"contents": []
}
@@ -0,0 +1,68 @@
/*
Copyright 2026 Seekra
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/**
* @typedef {'bool' | 'number' | 'string' | 'selection' | 'section'} SettingType
*/
/**
* @typedef {Object} SelectionOption
* @property {string} name
* @property {string} i18n
*/
/**
* @typedef {Object} BaseSettingConfig
* @property {SettingType} type
* @property {string} name
* @property {string} i18n
* @property {string} [description]
*/
/**
* @typedef {BaseSettingConfig & {
* default: string | string[],
* allowMultiple: boolean,
* options: SelectionOption[]
* }} SelectionSettingConfig
*/
/**
* @typedef {Object} SectionSettingConfig
* @property {'section'} type
* @property {string} name
* @property {string} [i18n]
* @property {string} [description]
* @property {SettingConfigEntry[]} content
*/
/**
* @typedef {BoolSettingConfig | NumberSettingConfig | StringSettingConfig | SelectionSettingConfig | SectionSettingConfig} SettingConfigEntry
*/
/**
* @typedef {Object} SettingsConfig
* @property {SettingConfigEntry[]} contents
*/
/**
* @typedef {{ type: 'bool', name: string, i18n: string, description?: string, default: boolean }} BoolSettingConfig
* @typedef {{ type: 'number', name: string, i18n: string, description?: string, default: number }} NumberSettingConfig
* @typedef {{ type: 'string', name: string, i18n: string, description?: string, default: string }} StringSettingConfig
* @typedef {{ type: 'selection', name: string, i18n: string, description?: string, default: string | string[], allowMultiple?: boolean, options: SelectionOption[] }} SelectionSettingConfig
* @typedef {{ type: 'section', name: string, i18n?: string, description?: string, content: SettingConfigEntry[] }} SectionSettingConfig
* @typedef {BoolSettingConfig | NumberSettingConfig | StringSettingConfig | SelectionSettingConfig | SectionSettingConfig} SettingConfigEntry
* @typedef {{ contents: SettingConfigEntry[] }} SettingsConfig
*/
@@ -0,0 +1,38 @@
/*
Copyright 2026 Seekra
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import { validateSettingsConfig } from './settingsValidator.js';
/**
* Loads and parses the settings configuration via dynamic import.
* @returns {Promise<import('../types/settingsConfig').SettingsConfig>}
*/
export async function loadSettingsConfig() {
let raw;
try {
raw = (await import('../settings.json')).default;
} catch (e) {
throw new Error(`[settings] Failed to load settings.json: ${e.message}`);
}
const result = validateSettingsConfig(raw);
if (!result.valid) {
throw new Error(result.error);
}
return result.config;
}
@@ -0,0 +1,99 @@
/*
Copyright 2026 Seekra
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
const VALID_TYPES = ['bool', 'number', 'string', 'selection', 'section'];
function assertString(value, path) {
if (typeof value !== 'string' || value.trim() === '') {
throw new Error(`[settings] "${path}" must be a non-empty string`);
}
}
function assertType(value, path) {
if (!VALID_TYPES.includes(value)) {
throw new Error(
`[settings] "${path}" has invalid type "${value}". Must be one of: ${VALID_TYPES.join(', ')}`
);
}
}
function validateSelectionOptions(options, path) {
if (!Array.isArray(options) || options.length === 0) {
throw new Error(`[settings] "${path}.options" must be a non-empty array`);
}
options.forEach((opt, i) => {
assertString(opt.name, `${path}.options[${i}].name`);
assertString(opt.i18n, `${path}.options[${i}].i18n`);
});
}
function validateEntry(entry, path) {
assertType(entry.type, `${path}.type`);
if (entry.type === 'section') {
assertString(entry.name, `${path}.name`);
if (!Array.isArray(entry.content)) {
throw new Error(`[settings] "${path}.content" must be an array`);
}
entry.content.forEach((child, i) =>
validateEntry(child, `${path}.content[${i}]`)
);
return;
}
assertString(entry.name, `${path}.name`);
assertString(entry.i18n, `${path}.i18n`);
if (entry.default !== undefined) {
if (entry.type === 'bool' && typeof entry.default !== 'boolean') {
throw new Error(`[settings] "${path}.default" must be a boolean`);
}
if (entry.type === 'number' && typeof entry.default !== 'number') {
throw new Error(`[settings] "${path}.default" must be a number`);
}
if (entry.type === 'string' && typeof entry.default !== 'string') {
throw new Error(`[settings] "${path}.default" must be a string`);
}
if (entry.type === 'selection') {
validateSelectionOptions(entry.options, path);
if (typeof entry.allowMultiple !== 'boolean') {
throw new Error(`[settings] "${path}.allowMultiple" must be a boolean`);
}
}
}
}
/**
* Validates a raw settings config object.
* @param {unknown} raw
* @returns {{ valid: true, config: import('../types/settingsConfig').SettingsConfig } | { valid: false, error: string }}
*/
export function validateSettingsConfig(raw) {
try {
if (!raw || typeof raw !== 'object') {
throw new Error('[settings] Config must be an object');
}
if (!Array.isArray(raw.contents)) {
throw new Error('[settings] "contents" must be an array');
}
raw.contents.forEach((entry, i) =>
validateEntry(entry, `contents[${i}]`)
);
return { valid: true, config: raw };
} catch (e) {
return { valid: false, error: e.message };
}
}
@@ -0,0 +1,56 @@
<!--
Copyright 2026 Seekra
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<script setup>
import LeftSidebarLayout from '@/layouts/LeftSidebarLayout.vue';
import { useI18n } from 'vue-i18n';
const { t } = useI18n();
</script>
<template>
<div class="settings-page-wrapper">
<header class="header">
<h1>
{{ t('preferences.settings') }}
</h1>
</header>
<LeftSidebarLayout class="layout">
<template #sidebar>
</template>
</LeftSidebarLayout>
</div>
</template>
<style scoped>
.layout {
flex-grow: 1;
}
.settings-page-wrapper {
display: flex;
flex-direction: column;
}
.header {
padding: var(--main-content-padding-y) var(--main-content-padding-x);
}
.header h1 {
margin: 0;
}
</style>
@@ -0,0 +1,28 @@
<!--
Copyright 2026 Seekra
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<template>
<nav class="sidebar">
<slot />
</nav>
</template>
<style scoped>
.sidebar {
border-right: 1px solid var(--border);
padding: 20px;
}
</style>
+60
View File
@@ -0,0 +1,60 @@
/*
Copyright 2026 Seekra
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import { createI18n } from 'vue-i18n';
import getCurrentLanguage from './utils/currentLanguage';
export const fallbackLocale = 'en';
export const LANGUAGES_RTL = [
'ar'
];
export const SUPPORTED_LANGUAGES = [
'en',
'de',
'fr',
'es',
'it',
'pt'
];
export const i18n = createI18n({
legacy: false,
locale: getCurrentLanguage(),
fallbackLocale: fallbackLocale,
messages: {}
});
const loadedLanguages = new Set();
export async function loadLanguage (locale) {
if (!SUPPORTED_LANGUAGES.includes(locale)) {
locale = fallbackLocale;
}
if (loadedLanguages.has(locale)) {
i18n.global.locale.value = locale;
return;
};
const messages = (await import(`./locales/${locale}.json`)).default;
i18n.global.setLocaleMessage(locale, messages);
i18n.global.locale.value = locale;
loadedLanguages.add(locale);
};
+42
View File
@@ -0,0 +1,42 @@
<!--
Copyright 2026 Seekra
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<script setup>
import Sidebar from '@/features/sidebar/components/Sidebar.vue';
</script>
<template>
<div class="layout-container">
<Sidebar>
<slot name="sidebar" />
</Sidebar>
<main class="main-content">
<slot />
</main>
</div>
</template>
<style scoped>
.layout-container {
display: grid;
grid-template-columns: min(24%, 280px) 1fr;
border-top: 1px solid var(--border);
}
.main-content {
padding: 20px;
}
</style>
+3
View File
@@ -0,0 +1,3 @@
# Datenschutzerklärung
Hier steht deine Datenschutzerklärung auf Deutsch.
+3
View File
@@ -0,0 +1,3 @@
# Privacy Policy
Here is your privacy policy in English.
+3
View File
@@ -0,0 +1,3 @@
# Política de privacidad
Aquí tienes tu política de privacidad en español.
+3
View File
@@ -0,0 +1,3 @@
# Politique de confidentialité
Voici ta politique de confidentialité en français.
+3
View File
@@ -0,0 +1,3 @@
# Informativa sulla privacy
Qui trovi la tua informativa sulla privacy in italiano.
+3
View File
@@ -0,0 +1,3 @@
# Política de Privacidade
Aqui está a tua Política de Privacidade em português.
+47
View File
@@ -0,0 +1,47 @@
{
"search": {
"searchBar": {
"submit": "Suchen",
"placeholder": "Suchen..."
},
"error": {
"searchNotAvailable": "Die Suche ist momentan nicht verfügbar."
}
},
"error": {
"tryAgainToAnotherTime": "Bitte versuchen Sie es zu einem anderen Zeitpunkt erneut.",
"pageNotFound": "Die gesuchte Seite existiert nicht. Bitte überprüfen Sie die URL oder kehren Sie zur Suchseite zurück."
},
"links": {
"back": {
"search": "Zurück zur Suche"
}
},
"preferences": {
"settings": "Einstellungen",
"colorScheme": {
"switch": {
"light": "Zum hellen Modus wechseln",
"dark": "Zum dunklen Modus wechseln",
"auto": "Zum System-Farbschema wechseln"
}
},
"locale": {
"languages": {
"en": "English",
"de": "Deutsch",
"fr": "Français",
"es": "Español",
"it": "Italiano",
"pt": "Português"
}
}
},
"slogan": "Gebaut zum Suchen.",
"legal": {
"privacy": {
"title": "Datenschutzerklärung"
}
}
}
+47
View File
@@ -0,0 +1,47 @@
{
"search": {
"searchBar": {
"submit": "Search",
"placeholder": "Search..."
},
"error": {
"searchNotAvailable": "Search is not available right now."
}
},
"error": {
"tryAgainToAnotherTime": "Please try again to another time.",
"pageNotFound": "The page you are looking for does not exist. Please check the URL or return to the search page."
},
"links": {
"back": {
"search": "Back to Search"
}
},
"preferences": {
"settings": "Settings",
"colorScheme": {
"switch": {
"light": "Switch to light mode",
"dark": "Switch to dark mode",
"auto": "Switch to the system color scheme"
}
},
"locale": {
"languages": {
"en": "English",
"de": "Deutsch",
"fr": "Français",
"es": "Español",
"it": "Italiano",
"pt": "Português"
}
}
},
"slogan": "Built to search.",
"legal": {
"privacy": {
"title": "Privacy Policy"
}
}
}
+47
View File
@@ -0,0 +1,47 @@
{
"search": {
"searchBar": {
"submit": "Buscar",
"placeholder": "Buscar..."
},
"error": {
"searchNotAvailable": "La búsqueda no está disponible en este momento."
}
},
"error": {
"tryAgainToAnotherTime": "Por favor, inténtalo de nuevo más tarde.",
"pageNotFound": "La página que buscas no existe. Comprueba la URL o vuelve a la página de búsqueda."
},
"links": {
"back": {
"search": "Volver a la búsqueda"
}
},
"preferences": {
"settings": "Ajustes",
"colorScheme": {
"switch": {
"light": "Cambiar al modo claro",
"dark": "Cambiar al modo oscuro",
"auto": "Usar el esquema de color del sistema"
}
},
"locale": {
"languages": {
"en": "English",
"de": "Deutsch",
"fr": "Français",
"es": "Español",
"it": "Italiano",
"pt": "Português"
}
}
},
"slogan": "Hecho para buscar.",
"legal": {
"privacy": {
"title": "Política de Privacidad"
}
}
}
+47
View File
@@ -0,0 +1,47 @@
{
"search": {
"searchBar": {
"submit": "Rechercher",
"placeholder": "Rechercher..."
},
"error": {
"searchNotAvailable": "La recherche n'est pas disponible pour le moment."
}
},
"error": {
"tryAgainToAnotherTime": "Veuillez réessayer ultérieurement.",
"pageNotFound": "La page que vous recherchez n'existe pas. Vérifiez l'URL ou retournez à la page de recherche."
},
"links": {
"back": {
"search": "Retour à la recherche"
}
},
"preferences": {
"settings": "Paramètres",
"colorScheme": {
"switch": {
"light": "Passer en mode clair",
"dark": "Passer en mode sombre",
"auto": "Utiliser le thème système"
}
},
"locale": {
"languages": {
"en": "English",
"de": "Deutsch",
"fr": "Français",
"es": "Español",
"it": "Italiano",
"pt": "Português"
}
}
},
"slogan": "Conçu pour chercher.",
"legal": {
"privacy": {
"title": "Politique de Confidentialité"
}
}
}
+47
View File
@@ -0,0 +1,47 @@
{
"search": {
"searchBar": {
"submit": "Cerca",
"placeholder": "Cerca..."
},
"error": {
"searchNotAvailable": "La ricerca non è disponibile al momento."
}
},
"error": {
"tryAgainToAnotherTime": "Per favore riprova più tardi.",
"pageNotFound": "La pagina che cerchi non esiste. Controlla l'URL o torna alla pagina di ricerca."
},
"links": {
"back": {
"search": "Torna alla ricerca"
}
},
"preferences": {
"settings": "Impostazioni",
"colorScheme": {
"switch": {
"light": "Passa alla modalità chiara",
"dark": "Passa alla modalità scura",
"auto": "Usa la combinazione colori di sistema"
}
},
"locale": {
"languages": {
"en": "English",
"de": "Deutsch",
"fr": "Français",
"es": "Español",
"it": "Italiano",
"pt": "Português"
}
}
},
"slogan": "Costruito per cercare.",
"legal": {
"privacy": {
"title": "Politica di Privacy"
}
}
}
+47
View File
@@ -0,0 +1,47 @@
{
"search": {
"searchBar": {
"submit": "Pesquisar",
"placeholder": "Pesquisar..."
},
"error": {
"searchNotAvailable": "A pesquisa não está disponível no momento."
}
},
"error": {
"tryAgainToAnotherTime": "Por favor, tente novamente mais tarde.",
"pageNotFound": "A página que você procura não existe. Verifique o URL ou volte à página de pesquisa."
},
"links": {
"back": {
"search": "Voltar à pesquisa"
}
},
"preferences": {
"settings": "Configurações",
"colorScheme": {
"switch": {
"light": "Mudar para modo claro",
"dark": "Mudar para modo escuro",
"auto": "Usar esquema de cores do sistema"
}
},
"locale": {
"languages": {
"en": "English",
"de": "Deutsch",
"fr": "Français",
"es": "Español",
"it": "Italiano",
"pt": "Português"
}
}
},
"slogan": "Feito para pesquisar.",
"legal": {
"privacy": {
"title": "Política de Privacidade"
}
}
}
+30
View File
@@ -0,0 +1,30 @@
/*
Copyright 2026 Seekra
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import { createApp } from 'vue'
import App from './App.vue'
import { i18n, loadLanguage } from './i18n';
import getCurrentLanguage from './utils/currentLanguage';
import router from './router'
import './styles/common.css'
import './styles/variables/colors.css'
await loadLanguage(getCurrentLanguage());
createApp(App)
.use(router)
.use(i18n)
.mount('#app')
+86
View File
@@ -0,0 +1,86 @@
/*
Copyright 2026 Seekra
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import { createRouter, createWebHistory } from 'vue-router';
import { i18n } from '@/i18n';
import SearchView from '../views/SearchView.vue';
import SearchResultsView from '@/features/search/views/SearchResultsView.vue';
import SettingsView from '@/features/settings/views/SettingsView.vue';
import NotFound from '../views/NotFound.vue';
import PrivacyPolicyView from '@/features/legal/views/PrivacyPolicyView.vue';
const routes = [
{
path: '/',
name: 'startPage',
component: SearchView
},
{
path: '/search',
name: 'searchResults',
component: SearchResultsView,
props: route => ({
searchQuery: route.query.q
}),
meta: {
title: (route) => route.query.q
}
},
{
path: '/settings',
name: 'settings',
component: SettingsView,
meta: {
title: () => i18n.global.t('preferences.settings')
}
},
{
path: '/:pathMatch(.*)*',
name: 'notFound',
component: NotFound
},
{
path: '/privacy',
name: 'privacyPolicy',
component: PrivacyPolicyView,
meta: {
title: () => 'Privacy Policy'
}
},
];
const router = createRouter({
history: createWebHistory(),
routes
});
// set page title
router.afterEach(to => {
const title =
typeof to.meta.title === 'function'
? to.meta.title(to)
: to.meta.title;
if (title) {
document.title = `${title} - Seekra`;
} else {
document.title = 'Seekra';
};
});
export default router;
+37
View File
@@ -0,0 +1,37 @@
/*
Copyright 2026 Seekra
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
body {
margin: 0;
font-size: 16px;
}
.link {
text-decoration: none;
}
.link:hover:not(.button-link) {
text-decoration: underline;
}
input {
color: var(--dark);
}
.main-content-padding {
padding: var(--main-content-padding);
width: calc(100% - var(--main-content-padding-x) * 2);
}
+26
View File
@@ -0,0 +1,26 @@
/*
Copyright 2026 Seekra
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
form {
display: flex;
justify-content: center;
margin-top: 60px;
}
footer{
text-align: center;
margin-top: 20px;
}
+149
View File
@@ -0,0 +1,149 @@
/*
Copyright 2026 Seekra
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#app-wrapper {
--primary-color-l-8: oklch(0.897 0.202 260);
--primary-color-l-7: oklch(0.847 0.202 260);
--primary-color-l-6: oklch(0.797 0.202 260);
--primary-color-l-6: oklch(0.747 0.202 260);
--primary-color-l-4: oklch(0.697 0.202 260);
--primary-color-l-3: oklch(0.647 0.202 260);
--primary-color-l-2: oklch(0.597 0.202 260);
--primary-color-l-1: oklch(0.547 0.202 260);
--primary-color: oklch(0.497 0.202 260);
--primary-color-d-1: oklch(0.447 0.202 260);
--primary-color-d-2: oklch(0.397 0.202 260);
--primary-color-d-3: oklch(0.347 0.202 260);
--primary-color-d-4: oklch(0.297 0.202 260);
--primary-color-d-5: oklch(0.247 0.202 260);
--primary-color-d-6: oklch(0.197 0.202 260);
--primary-color-d-7: oklch(0.147 0.202 260);
--primary-color-d-8: oklch(0.097 0.202 260);
--black-l-8: oklch(0.4 0 0);
--black-l-7: oklch(0.35 0 0);
--black-l-6: oklch(0.3 0 0);
--black-l-5: oklch(0.25 0 0);
--black-l-4: oklch(0.2 0 0);
--black-l-3: oklch(0.15 0 0);
--black-l-2: oklch(0.1 0 0);
--black-l-1: oklch(0.05 0 0);
--black: oklch(0 0 0);
--white: oklch(1 0 0);
--white-d-1: oklch(0.95 0 0);
--white-d-2: oklch(0.9 0 0);
--white-d-3: oklch(0.85 0 0);
--white-d-4: oklch(0.8 0 0);
--white-d-5: oklch(0.75 0 0);
--white-d-6: oklch(0.7 0 0);
--white-d-7: oklch(0.65 0 0);
--white-d-8: oklch(0.6 0 0);
--dark-l-8: var(--black-l-8);
--dark-l-7: var(--black-l-7);
--dark-l-6: var(--black-l-6);
--dark-l-5: var(--black-l-5);
--dark-l-4: var(--black-l-4);
--dark-l-3: var(--black-l-3);
--dark-l-2: var(--black-l-2);
--dark-l-1: var(--black-l-1);
--dark: var(--black);
--light: var(--white);
--light-d-1: var(--white-d-1);
--light-d-2: var(--white-d-2);
--light-d-3: var(--white-d-3);
--light-d-4: var(--white-d-4);
--light-d-5: var(--white-d-5);
--light-d-6: var(--white-d-6);
--light-d-7: var(--white-d-7);
--light-d-8: var(--white-d-8);
--dark-bg: var(--black-l-2);
--light-bg: var(--white);
--border: var(--white-d-3);
--gray-box-shadow: oklch(0.8 0.0001 271 / 0.7);
--blue-box-shadow: oklch(0.52 0.15 268 / 0.25);
--light-hover: var(--light-d-2);
}
@media (prefers-color-scheme: dark) {
#app-wrapper.color-scheme-auto {
--dark-l-8: var(--white-d-8);
--dark-l-7: var(--white-d-7);
--dark-l-6: var(--white-d-6);
--dark-l-5: var(--white-d-5);
--dark-l-4: var(--white-d-4);
--dark-l-3: var(--white-d-3);
--dark-l-2: var(--white-d-2);
--dark-l-1: var(--white-d-1);
--dark: var(--white);
--light: var(--black);
--light-d-1: oklch(0.10 0 0);
--light-d-2: var(--black-l-2);
--light-d-3: var(--black-l-3);
--light-d-4: var(--black-l-4);
--light-d-5: var(--black-l-5);
--light-d-6: var(--black-l-6);
--light-d-7: var(--black-l-7);
--light-d-8: var(--black-l-8);
--dark-bg: var(--white);
--light-bg: oklch(0.18 0 0);
--border: var(--black-l-6);
--gray-box-shadow: oklch(0.25 0.0001 271 / 0.7);
--light-hover: var(--light-d-5);
}
}
#app-wrapper.dark {
--dark-l-8: var(--white-d-8);
--dark-l-7: var(--white-d-7);
--dark-l-6: var(--white-d-6);
--dark-l-5: var(--white-d-5);
--dark-l-4: var(--white-d-4);
--dark-l-3: var(--white-d-3);
--dark-l-2: var(--white-d-2);
--dark-l-1: var(--white-d-1);
--dark: var(--white);
--light: var(--black);
--light-d-1: oklch(0.10 0 0);
--light-d-2: var(--black-l-2);
--light-d-3: var(--black-l-3);
--light-d-4: var(--black-l-4);
--light-d-5: var(--black-l-5);
--light-d-6: var(--black-l-6);
--light-d-7: var(--black-l-7);
--light-d-8: var(--black-l-8);
--dark-bg: var(--white);
--light-bg: oklch(0.18 0 0);
--border: var(--black-l-6);
--gray-box-shadow: oklch(0.25 0.0001 271 / 0.7);
--light-hover: var(--light-d-5);
}
+22
View File
@@ -0,0 +1,22 @@
/*
Copyright 2026 Seekra
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
export default function getCurrentLanguage () {
const saved = localStorage.getItem('locale');
if (saved) return saved;
const locale = new Intl.Locale(navigator.language);
return locale.language;
};
+56
View File
@@ -0,0 +1,56 @@
<!--
Copyright 2026 Seekra
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<script setup>
import { useI18n } from 'vue-i18n';
const { t } = useI18n();
</script>
<template>
<div class="not-found main-content-padding">
<span class="error-message">
{{ t('error.pageNotFound') }}
</span>
<RouterLink to="/" id="link">
{{ t('links.back.search') }}
</RouterLink>
</div>
</template>
<style scoped>
.not-found {
text-align: center;
padding-top: 80px;
display: flex;
flex-direction: column;
align-items: center;
gap: 12px;
}
.error-message{
margin: 0;
font-weight: 600;
font-size: 2vw;
}
#link {
align-items: center;
display: flex;
justify-content: center;
margin-top: 20px;
padding: 10px 20px;
text-decoration: none;
}
</style>
+85
View File
@@ -0,0 +1,85 @@
<!--
Copyright 2026 Seekra
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<script setup>
import Searchbar from '../features/search/components/Searchbar.vue';
import { ref } from 'vue';
import { useRouter } from 'vue-router';
import { useI18n } from 'vue-i18n';
import logo from '@/assets/images/logo.svg';
const { t } = useI18n();
const router = useRouter();
const searchQuery = ref('');
const submitSearch = function () {
};
</script>
<template>
<div class="search-content main-content-padding">
<header class="global-header">
<img :src="logo" alt="Seekra" class="header-logo" />
<span class="slogan">
{{ t('slogan') }}
</span>
</header>
<div class="search-container">
<Searchbar v-model="searchQuery" ref="searchbar" class="search-bar" auto-submit />
</div>
</div>
</template>
<style scoped>
.search-content {
display: flex;
flex-direction: column;
align-items: center;
gap: 60px;
}
.global-header {
text-align: center;
display: flex;
flex-direction: column;
align-items: center;
padding-top: 40px;
}
.header-logo {
width: 320px;
max-width: 100%;
}
.slogan{
margin: 0;
font-size: small;
}
.search-container {
width: 70%;
max-width: 624px;
}
@media (max-width: 67.5rem) {
.search-container {
width: 100%;
}
}
</style>
-29
View File
@@ -1,29 +0,0 @@
body{
margin: 0;
}
header{
text-align: center;
}
h1{
margin-bottom: 0;
}
h3{
margin-top: 0;
}
#searchbar{
text-align: center;
}
#searchbar input[type="search"]{
width: 60%;
margin-bottom: 10px;
}
footer{
justify-content: center;
margin-top: 100px;
display: flex;
gap: 20px;
border-top: 1px solid;
}
+35
View File
@@ -0,0 +1,35 @@
/*
Copyright 2026 Seekra
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import path from 'path';
import { createHtmlPlugin } from 'vite-plugin-html';
// https://vite.dev/config/
export default defineConfig({
plugins: [
vue(),
createHtmlPlugin({
minify: true
})
],
resolve: {
alias: {
'@': path.resolve(__dirname, './src')
}
}
})