From de45a26f7f9a72671f7edf753908c9f4af0fe8c8 Mon Sep 17 00:00:00 2001 From: Can Cellek Date: Tue, 9 Jan 2024 18:10:11 +0300 Subject: [PATCH] Add Support For Docker Volumes * Restructure customization files into data folder * Remove theme list, add docker mode check for settings * Add fs setting load for docker * Add .env variable for checking is docker built or not * Fix Docker env variable override * Rename volume name into project name * Remove RWX permissions from data folder * Refactor theme list and theme read * Allow custom favicon, Add theme name to fetch * Move themes folder outside data folder * Bump docker compose version to 3.8 * Update Dockerfile job name * Simplify theme colors * Update Dockerfile with data chown * Update terminal opacity and blur handling * Update yarn docker build command to consider volume * Improve meta icon handling * Improve docker data handling for bind mounts * Update asset paths in settings * Update contributing guideline --- .env | 1 + .github/CONTRIBUTING.md | 21 +- .github/workflows/publish-release.yml | 2 +- Dockerfile | 10 +- .../icon.svg => data/assets/default-icon.svg | 0 data/assets/default-wallpaper.svg | 184 ++++++++++++ data/settings.json | 275 +++++++++++++++++ {public => data}/themes/bushido.json | 11 +- data/themes/catppuccin-frappe.json | 15 + data/themes/catppuccin-latte.json | 15 + data/themes/catppuccin-macchiato.json | 15 + .../themes/catppuccin-mocha.json | 17 +- {public => data}/themes/default.json | 11 +- {public => data}/themes/dracula.json | 13 +- {public => data}/themes/everforest-dark.json | 11 +- {public => data}/themes/hacker.json | 11 +- data/themes/horizon-dark.json | 15 + data/themes/horizon-light.json | 15 + {public => data}/themes/monokai.json | 11 +- {public => data}/themes/nord.json | 15 +- data/themes/onedark.json | 15 + {public => data}/themes/synthwave.json | 11 +- {public => data}/themes/tokyonight.json | 11 +- {public => data}/themes/verdant.json | 11 +- docker-compose.yml | 15 + next.config.js | 16 +- package.json | 10 +- public/favicon.ico | Bin 15406 -> 0 bytes public/themes/onedark.json | 16 - src/components/Config.js | 66 +++-- src/components/Editor.js | 2 +- src/components/Fetch.js | 39 ++- src/components/Help.js | 2 +- src/components/Meta.js | 37 ++- src/components/Prompt.js | 2 +- src/components/Search.js | 4 +- src/context/settings.js | 58 ++-- src/hooks/useFetchData.js | 2 + src/pages/_app.js | 4 +- src/pages/api/getData.js | 51 ++++ src/pages/api/getTheme.js | 39 +++ src/pages/api/loadSettings.js | 8 + src/pages/api/saveSettings.js | 8 + src/pages/index.js | 40 ++- src/styles/globals.css | 7 +- src/utils/fetchAsset.js | 26 ++ src/utils/themes.js | 14 - startpage.config.js | 276 ------------------ tailwind.config.js | 7 +- 49 files changed, 984 insertions(+), 481 deletions(-) create mode 100644 .env rename public/icon.svg => data/assets/default-icon.svg (100%) create mode 100644 data/assets/default-wallpaper.svg create mode 100644 data/settings.json rename {public => data}/themes/bushido.json (71%) create mode 100644 data/themes/catppuccin-frappe.json create mode 100644 data/themes/catppuccin-latte.json create mode 100644 data/themes/catppuccin-macchiato.json rename public/themes/catppuccin.json => data/themes/catppuccin-mocha.json (53%) rename {public => data}/themes/default.json (71%) rename {public => data}/themes/dracula.json (62%) rename {public => data}/themes/everforest-dark.json (72%) rename {public => data}/themes/hacker.json (71%) create mode 100644 data/themes/horizon-dark.json create mode 100644 data/themes/horizon-light.json rename {public => data}/themes/monokai.json (72%) rename {public => data}/themes/nord.json (53%) create mode 100644 data/themes/onedark.json rename {public => data}/themes/synthwave.json (72%) rename {public => data}/themes/tokyonight.json (72%) rename {public => data}/themes/verdant.json (66%) create mode 100644 docker-compose.yml delete mode 100644 public/favicon.ico delete mode 100644 public/themes/onedark.json create mode 100644 src/pages/api/getData.js create mode 100644 src/pages/api/getTheme.js create mode 100644 src/pages/api/loadSettings.js create mode 100644 src/pages/api/saveSettings.js create mode 100644 src/utils/fetchAsset.js delete mode 100644 src/utils/themes.js delete mode 100644 startpage.config.js diff --git a/.env b/.env new file mode 100644 index 00000000..3adb19de --- /dev/null +++ b/.env @@ -0,0 +1 @@ +BUILD_MODE=local \ No newline at end of file diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 14fbbd6d..82fb764c 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -35,7 +35,7 @@ To report a new issue, please follow these steps: - Provide your **OS** and **Browser** information. - Add any **additional Context** about the problem here such as console logs and screenshots. If you do not know how to get the console logs, please check your browser's documentation on how to get the console logs. If you cannot find the console logs, please mention that in the issue so that maintainers can help you get the logs. -> **Note** +> [!NOTE] > If you find a **Closed** issue that seems like it is the same thing that you're experiencing, open a new issue and include a link to the original issue in the body of your new one. ### Feature Requests @@ -52,29 +52,30 @@ If maintainers approve your suggestion, the discussion will be converted to an i In order to contribute to the source of the project 1. Fork the repository and create a new branch for your feature or enhancement `kebab-case` for the branch name. - - For new documents, please use the `theme-theme-name` as the branch name. + - For new theme, please use the `theme-theme-name` as the branch name. - For features or enhancements, please use the `feat-feat-name` as the branch name. - For fixes, please use the `fix-issue-name` as the branch name. 2. Use [commit messages](#commit-messages) guideline for your commits. 3. Send a pull request to the `main` branch using the [pull request](#pull-requests) guideline. -> **Warning** +> [!IMPORTANT] > Please **do not** change design into a complicated and / or overwhelming experience. It should launch fast, be easy to use and not distract from the functionality. ### New Themes I really appreciate contributions in the form of new themes to the project. If you'd like to add a new theme, please follow these steps: -1. Create a new `json` file in the `public/themes` folder using the theme name as the file name. Please use `kebab-case` for the file name. You can use the default theme file as a starting point. -2. Add the theme name to the array in the `src/utils/themes.js` file. Please use the same name as the file and use alphabetical order. -3. Test your theme by running the project locally by running `yarn dev` command +1. Have a look at [Themes](https://github.com/excalith/excalith-start-page/wiki/Themes#theme-scheme) wiki page for the scheme you should use. +2. Head to `data/themes` folder +3. Create `themename-variant.json` file using lowercase (variant should have hypens before ie. `catppuccin-latte`) +4. Fill the theme structure with your own colors +5. Test in on local server using `yarn dev` command 1. Use `set theme` command to see your theme on the list 2. Use `set theme ` to see your theme in action +6. Send a PR! -> **Warning** -> Since themes are stored in the local storage, you should run the `set theme ` command to see your changes. - -More information about themes can be found in the [Wiki Page](https://github.com/excalith/excalith-start-page/wiki/Themes). +> [!IMPORTANT] +> Since themes are stored in the local storage while running as a web app, you should run the `set theme ` command to see your changes. ### New Features / Enhancements diff --git a/.github/workflows/publish-release.yml b/.github/workflows/publish-release.yml index f3f501f6..34820883 100644 --- a/.github/workflows/publish-release.yml +++ b/.github/workflows/publish-release.yml @@ -6,7 +6,7 @@ on: jobs: push_to_registries: - name: Push Docker image to multiple registries + name: Push Docker image to GHCR and DockerHub registries runs-on: ubuntu-latest permissions: packages: write diff --git a/Dockerfile b/Dockerfile index 3345e8d4..8f82c4ec 100644 --- a/Dockerfile +++ b/Dockerfile @@ -15,13 +15,15 @@ RUN \ else echo "Lockfile not found." && exit 1; \ fi - # Rebuild the source code only when needed FROM base AS builder WORKDIR /app COPY --from=deps /app/node_modules ./node_modules COPY . . +# Set mode to Docker (for data fetching switches between docker and web) +ENV BUILD_MODE docker + # Next.js collects completely anonymous telemetry data about general usage. # Learn more here: https://nextjs.org/telemetry # Uncomment the following line in case you want to disable telemetry during the build. @@ -34,19 +36,21 @@ RUN yarn build FROM base AS runner WORKDIR /app +# Set NODE_ENV to production ENV NODE_ENV production + + # Uncomment the following line in case you want to disable telemetry during runtime. ENV NEXT_TELEMETRY_DISABLED 1 RUN addgroup --system --gid 1001 nodejs RUN adduser --system --uid 1001 nextjs -COPY --from=builder /app/public ./public - # Automatically leverage output traces to reduce image size # https://nextjs.org/docs/advanced-features/output-file-tracing COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static +COPY --from=builder --chown=nextjs:nodejs /app/data ./data USER nextjs diff --git a/public/icon.svg b/data/assets/default-icon.svg similarity index 100% rename from public/icon.svg rename to data/assets/default-icon.svg diff --git a/data/assets/default-wallpaper.svg b/data/assets/default-wallpaper.svg new file mode 100644 index 00000000..f27fc095 --- /dev/null +++ b/data/assets/default-wallpaper.svg @@ -0,0 +1,184 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/data/settings.json b/data/settings.json new file mode 100644 index 00000000..4fa955d2 --- /dev/null +++ b/data/settings.json @@ -0,0 +1,275 @@ +{ + "username": "Excalith", + "theme": { + "name": "Default", + "backgroundColor": "#121317", + "windowColor": "#1e212b", + "textColor": "#e2e2e2", + "black": "#16161e", + "red": "#ec6183", + "green": "#2ed8a2", + "yellow": "#e8b195", + "blue": "#2bc3de", + "magenta": "#d1aff8", + "cyan": "#62e0e2", + "white": "#e2e2e2", + "gray": "#97989d" + }, + "wallpaper": { + "url": "assets/default-wallpaper.svg", + "easing": "ease-in-out", + "fadeIn": true, + "blur": false + }, + "terminal": { + "fixedHeight": true, + "windowGlow": true, + "windowGlowColor": "#291f3325", + "textGlow": false, + "opacity": 0, + "blur": 16 + }, + "prompt": { + "ctrlC": true, + "placeholder": "command...", + "placeholderColor": "gray", + "userColor": "green", + "atColor": "gray", + "hostColor": "magenta", + "promptColor": "magenta", + "promptSymbol": "❯", + "caretColor": "green", + "selectionBg": "yellow", + "selectionFg": "black" + }, + "fetch": { + "timeFormat": "HH:mm", + "dateFormat": "DD/MM/YYYY", + "titleColor": "yellow", + "image": "assets/default-icon.svg", + "data": [ + "Time: {time}", + "Date: {date}", + "{seperator}", + "OS: {osName} {osVersion}", + "Theme: {theme}", + "{seperator}", + "Browser: {browser} {browserVersion}", + "Engine: {engineName}", + "{seperator}", + "{colors}" + ] + }, + "urlLaunch": { + "target": "_self", + "hoverColor": "magenta" + }, + "search": { + "default": "https://google.com/search?q=", + "target": "_self", + "shortcutRegex": "([A-Za-z0-9]+) (.*)", + "shortcuts": [ + { + "alias": "g", + "name": "Google Search", + "url": "https://google.com/search?q={}" + }, + { + "alias": "d", + "name": "DuckDuckGo Search", + "url": "https://duckduckgo.com/?q={}" + }, + { + "alias": "b", + "name": "Brave Search", + "url": "https://search.brave.com/search?q={}" + }, + { + "alias": "gh", + "name": "Github Search", + "url": "https://github.com/search?q={}" + }, + { + "alias": "s", + "name": "Stack Overflow Search", + "url": "https://stackoverflow.com/search?q={}" + }, + { + "alias": "r", + "name": "Subreddit Search", + "url": "https://reddit.com/r/{}" + }, + { + "alias": "w", + "name": "Wikipedia Search", + "url": "https://en.wikipedia.org/wiki/{}" + } + ] + }, + "sections": { + "list": [ + { + "title": "General", + "color": "green", + "align": "left", + "links": [ + { + "name": "Portfolio", + "url": "https://cancellek.com", + "icon": "mdi:web" + }, + { + "name": "Keybase", + "url": "https://keybase.io/", + "icon": "fa-brands:keybase" + }, + { + "name": "GPT", + "url": "https://chat.openai.com/", + "icon": "simple-icons:openai" + }, + { + "name": "OCI", + "url": "https://www.oracle.com/cloud/", + "icon": "simple-icons:oracle" + } + ] + }, + { + "title": "Dev", + "color": "magenta", + "align": "left", + "links": [ + { + "name": "GitHub", + "url": "https://github.com", + "icon": "mdi:github" + }, + { + "name": "GitLab", + "url": "https://gitlab.com", + "icon": "ph:gitlab-logo-simple-fill" + }, + { + "name": "Dev.to", + "url": "https://dev.to", + "icon": "material-symbols:logo-dev" + }, + { + "name": "Stack Overflow", + "url": "https://stackoverflow.com/", + "icon": "mdi:stack-overflow" + } + ] + }, + { + "title": "Social", + "color": "cyan", + "align": "left", + "links": [ + { + "name": "Twitter", + "url": "https://twitter.com", + "icon": "mdi:twitter" + }, + { + "name": "Mastodon", + "url": "https://mastodon.social/", + "icon": "ri:mastodon-fill" + }, + { + "name": "Reddit", + "url": "https://reddit.com", + "icon": "mdi:reddit" + }, + { + "name": "Polywork", + "url": "https://polywork.com", + "icon": "simple-icons:polywork" + } + ] + }, + { + "title": "Gaming", + "color": "red", + "align": "left", + "links": [ + { + "name": "Polygon", + "url": "https://polygon.com", + "icon": "uil:polygon" + }, + { + "name": "IGN", + "url": "https://ign.com", + "icon": "mdi:currency-sign" + }, + { + "name": "RPS", + "url": "https://rockpapershotgun.com/", + "icon": "ph:toilet-paper-bold" + }, + { + "name": "80lv", + "url": "https://80.lv/", + "icon": "tabler:hand-rock" + } + ] + }, + { + "title": "Science", + "color": "blue", + "align": "left", + "links": [ + { + "name": "PopSci", + "url": "https://popsci.com/", + "icon": "material-symbols:science" + }, + { + "name": "Space", + "url": "fa6-solid:user-astronaut", + "icon": "mdi:reddit" + }, + { + "name": "NASA", + "url": "https://blogs.nasa.gov/", + "icon": "simple-icons:nasa" + }, + { + "name": "ESA", + "url": "https://blogs.esa.int/", + "icon": "mdi:black-mesa" + } + ] + }, + { + "title": "Tech", + "color": "yellow", + "align": "left", + "links": [ + { + "name": "TechCrunch", + "url": "https://techcrunch.com/", + "icon": "game-icons:techno-heart" + }, + { + "name": "Verge", + "url": "https://www.theverge.com/", + "icon": "arcticons:verge" + }, + { + "name": "It's Foss", + "url": "https://itsfoss.com/", + "icon": "ri:mastodon-fill" + }, + { + "name": "9To5 Linux", + "url": "https://9to5linux.com/", + "icon": "uil:linux" + } + ] + } + ] + } +} \ No newline at end of file diff --git a/public/themes/bushido.json b/data/themes/bushido.json similarity index 71% rename from public/themes/bushido.json rename to data/themes/bushido.json index 030dd307..2f6a9c6d 100644 --- a/public/themes/bushido.json +++ b/data/themes/bushido.json @@ -1,16 +1,15 @@ { + "name": "Bushido", "backgroundColor": "#1c222d", "windowColor": "#252a33", - "glowColor": "#EC4C574B", - "white": "#f6f0e9", - "gray": "#596172;", + "textColor": "#f6f0e9", "black": "#16161e", "red": "#ec4c56", "green": "#ec4c56", "yellow": "#ec4c56", "blue": "#ec4c56", - "cyan": "#ec4c56", "magenta": "#ec4c56", - "violet": "#ec4c56", - "orange": "#ec4c56" + "cyan": "#ec4c56", + "white": "#f6f0e9", + "gray": "#596172;" } diff --git a/data/themes/catppuccin-frappe.json b/data/themes/catppuccin-frappe.json new file mode 100644 index 00000000..145f7398 --- /dev/null +++ b/data/themes/catppuccin-frappe.json @@ -0,0 +1,15 @@ +{ + "name": "Catppuccin Frappé", + "backgroundColor": "#232634", + "windowColor": "#303446", + "textColor": "#b5bfe2", + "black": "#51576d", + "red": "#e78284", + "green": "#a6d189", + "yellow": "#e5c890", + "blue": "#8caaee", + "magenta": "#f4b8e4", + "cyan": "#81c8be", + "white": "#b5bfe2", + "gray": "#97989d" +} diff --git a/data/themes/catppuccin-latte.json b/data/themes/catppuccin-latte.json new file mode 100644 index 00000000..57b4fef5 --- /dev/null +++ b/data/themes/catppuccin-latte.json @@ -0,0 +1,15 @@ +{ + "name": "Catppuccin Latte", + "backgroundColor": "#e6e9ef", + "windowColor": "#dce0e8", + "textColor": "#acb0be", + "black": "#5c5f77", + "red": "#d20f39", + "green": "#40a02b", + "yellow": "#df8e1d", + "blue": "#1e66f5", + "magenta": "#ea76cb", + "cyan": "#179299", + "white": "#acb0be", + "gray": "#97989d" +} diff --git a/data/themes/catppuccin-macchiato.json b/data/themes/catppuccin-macchiato.json new file mode 100644 index 00000000..70f2a8e0 --- /dev/null +++ b/data/themes/catppuccin-macchiato.json @@ -0,0 +1,15 @@ +{ + "name": "Catppuccin Macchiato", + "backgroundColor": "#181926", + "windowColor": "#24273a", + "textColor": "#cad3f5", + "black": "#494d64", + "red": "#ed8796", + "green": "#a6da95", + "yellow": "#eed49f", + "blue": "#8aadf4", + "magenta": "#caa4f7", + "cyan": "#8bd5ca", + "white": "#b8c0e0", + "gray": "#97989d" +} diff --git a/public/themes/catppuccin.json b/data/themes/catppuccin-mocha.json similarity index 53% rename from public/themes/catppuccin.json rename to data/themes/catppuccin-mocha.json index fe1997b6..e6a7f59e 100644 --- a/public/themes/catppuccin.json +++ b/data/themes/catppuccin-mocha.json @@ -1,16 +1,15 @@ { - "backgroundColor": "#121317", - "windowColor": "#181825", - "glowColor": "#464c79", - "white": "#bac2de", - "gray": "#97989d", + "name": "Catppuccin Mocha", + "backgroundColor": "#11111b", + "windowColor": "#1e1e2e", + "textColor": "#cdd6f4", "black": "#45475A", "red": "#f38ba8", "green": "#a6e3a1", - "blue": "#89b4fa", "yellow": "#f9e2af", - "cyan": "#94e2d5", + "blue": "#89b4fa", "magenta": "#f5c2e7", - "violet": "#caa4f7", - "orange": "#fbb185" + "cyan": "#94e2d5", + "white": "#bac2de", + "gray": "#97989d" } diff --git a/public/themes/default.json b/data/themes/default.json similarity index 71% rename from public/themes/default.json rename to data/themes/default.json index 0648e1a9..cdacc396 100644 --- a/public/themes/default.json +++ b/data/themes/default.json @@ -1,16 +1,15 @@ { + "name": "Default", "backgroundColor": "#121317", "windowColor": "#1e212b", - "glowColor": "#6b5cb157", - "white": "#e2e2e2", - "gray": "#97989d", + "textColor": "#e2e2e2", "black": "#16161e", "red": "#ec6183", "green": "#2ed8a2", "yellow": "#e8b195", "blue": "#2bc3de", - "cyan": "#62e0e2", "magenta": "#e069aa", - "violet": "#d1aff8", - "orange": "#ff8800" + "cyan": "#62e0e2", + "white": "#e2e2e2", + "gray": "#97989d" } diff --git a/public/themes/dracula.json b/data/themes/dracula.json similarity index 62% rename from public/themes/dracula.json rename to data/themes/dracula.json index c9feacc3..6e77dc67 100644 --- a/public/themes/dracula.json +++ b/data/themes/dracula.json @@ -1,16 +1,15 @@ { - "backgroundColor": "#1e212b", + "name": "Dracula", + "backgroundColor": "#191a21", "windowColor": "#282a36", - "glowColor": "#bd93f945", - "white": "#bfbfbf", - "gray": "#97989d", + "textColor": "#bfbfbf", "black": "#000000", "red": "#ff5555", "green": "#50fa7b", "yellow": "#f1fa8c", - "blue": "#bd93f9", "magenta": "#ff79c6", + "blue": "#bd93f9", "cyan": "#8be9fd", - "violet": "#bd93f9", - "orange": "#ffb86c" + "white": "#bfbfbf", + "gray": "#97989d" } diff --git a/public/themes/everforest-dark.json b/data/themes/everforest-dark.json similarity index 72% rename from public/themes/everforest-dark.json rename to data/themes/everforest-dark.json index 9b5cca09..a4d806e1 100644 --- a/public/themes/everforest-dark.json +++ b/data/themes/everforest-dark.json @@ -1,16 +1,15 @@ { + "name": "Everforest Dark", "backgroundColor": "#232a2e", "windowColor": "#2d353b", - "glowColor": "#425047", - "white": "#d3c6aa", - "gray": "#7a8478", + "textColor": "#d3c6aa", "black": "#000000", "red": "#e67e80", "green": "#a7c080", "yellow": "#dbbc7f", "blue": "#7fbbb3", - "cyan": "#83c092", "magenta": "#d699b6", - "violet": "#d699b6", - "orange": "#e69875" + "cyan": "#83c092", + "white": "#d3c6aa", + "gray": "#7a8478" } diff --git a/public/themes/hacker.json b/data/themes/hacker.json similarity index 71% rename from public/themes/hacker.json rename to data/themes/hacker.json index 15cbf071..288fda2a 100644 --- a/public/themes/hacker.json +++ b/data/themes/hacker.json @@ -1,16 +1,15 @@ { + "name": "Hacker", "backgroundColor": "#000000", "windowColor": "#000000", - "glowColor": "#0f9e004d", - "white": "#0f9e00", - "gray": "#0f9e00", + "textColor": "#0f9e00", "black": "#000000", "red": "#0f9e00", "green": "#0f9e00", "yellow": "#0f9e00", "blue": "#0f9e00", - "cyan": "#0f9e00", "magenta": "#0f9e00", - "violet": "#0f9e00", - "orange": "#0f9e00" + "cyan": "#0f9e00", + "white": "#0f9e00", + "gray": "#0f9e00" } diff --git a/data/themes/horizon-dark.json b/data/themes/horizon-dark.json new file mode 100644 index 00000000..09106c33 --- /dev/null +++ b/data/themes/horizon-dark.json @@ -0,0 +1,15 @@ +{ + "name": "Horizon Dark", + "backgroundColor": "#16161C", + "windowColor": "#1C1E26", + "textColor": "#FDF0ED", + "black": "#16161e", + "red": "#E95678", + "green": "#27D796", + "yellow": "#FAB795", + "blue": "#25B2BC", + "magenta": "#B877DB", + "cyan": "#62e0e2", + "white": "#FDF0ED", + "gray": "#97989d" +} diff --git a/data/themes/horizon-light.json b/data/themes/horizon-light.json new file mode 100644 index 00000000..7800b1ee --- /dev/null +++ b/data/themes/horizon-light.json @@ -0,0 +1,15 @@ +{ + "name": "Horizon Light", + "backgroundColor": "#fce9e4", + "windowColor": "#FDF0ED", + "textColor": "#FDF0ED", + "black": "#1A1C23", + "red": "#DA103F", + "green": "#1EB980", + "yellow": "#F77D26", + "blue": "#1EAEAE", + "magenta": "#8931B9", + "cyan": "#49A3A5", + "white": "#FDF0ED", + "gray": "#97989d" +} diff --git a/public/themes/monokai.json b/data/themes/monokai.json similarity index 72% rename from public/themes/monokai.json rename to data/themes/monokai.json index d74128d2..18afa405 100644 --- a/public/themes/monokai.json +++ b/data/themes/monokai.json @@ -1,16 +1,15 @@ { + "name": "Monokai", "backgroundColor": "#1d1e18", "windowColor": "#272821", - "glowColor": "#3a3b33", - "white": "#f8f8f2", - "gray": "#cfcfc2", + "textColor": "#f8f8f2", "black": "#272822", "red": "#f92672", "green": "#a3df2d", "yellow": "#9dd52c", "blue": "#66d9ef", - "cyan": "#a1efe4", "magenta": "#fd5ff0", - "violet": "#ae81ff", - "orange": "#fd9720" + "cyan": "#a1efe4", + "white": "#f8f8f2", + "gray": "#cfcfc2" } diff --git a/public/themes/nord.json b/data/themes/nord.json similarity index 53% rename from public/themes/nord.json rename to data/themes/nord.json index f388a6ad..cf867d52 100644 --- a/public/themes/nord.json +++ b/data/themes/nord.json @@ -1,16 +1,15 @@ { - "backgroundColor": "#2E3440", - "windowColor": "#2E3440", - "glowColor": "#0000002e", - "white": "#E5E9F0", - "gray": "#d8dee9", + "name": "Nord", + "backgroundColor": "#2e3440", + "windowColor": "#3b4252", + "textColor": "#E5E9F0", "black": "#3B4252", "red": "#BF616A", "green": "#A3BE8C", "yellow": "#EBCB8B", "blue": "#81A1C1", - "cyan": "#88C0D0", "magenta": "#B48EAD", - "violet": "#9879c6", - "orange": "#d08770" + "cyan": "#88C0D0", + "white": "#E5E9F0", + "gray": "#d8dee9" } diff --git a/data/themes/onedark.json b/data/themes/onedark.json new file mode 100644 index 00000000..bf7e7bae --- /dev/null +++ b/data/themes/onedark.json @@ -0,0 +1,15 @@ +{ + "name": "OneDark", + "backgroundColor": "#21252B", + "windowColor": "#282C34", + "textColor": "#abb2bf", + "black": "#282c34", + "red": "#E06C75", + "green": "#98C379", + "yellow": "#E5C07B", + "blue": "#61AFEF", + "magenta": "#C678DD", + "cyan": "#56B6C2", + "white": "#abb2bf", + "gray": "#5c6370" +} diff --git a/public/themes/synthwave.json b/data/themes/synthwave.json similarity index 72% rename from public/themes/synthwave.json rename to data/themes/synthwave.json index 35447a7c..71951a76 100644 --- a/public/themes/synthwave.json +++ b/data/themes/synthwave.json @@ -1,16 +1,15 @@ { + "name": "Synthwave", "backgroundColor": "#1E1429", "windowColor": "#2b213a", - "glowColor": "#ed73e1", - "white": "#e2e2e2", - "gray": "#97989d", + "textColor": "#e2e2e2", "black": "#16161e", "red": "#fe4450", "green": "#72f1b8", "yellow": "#fede5d", "blue": "#03edf9", - "cyan": "#03edf9", "magenta": "#ff7edb", - "violet": "#A46CF3", - "orange": "#F5A753" + "cyan": "#03edf9", + "white": "#e2e2e2", + "gray": "#97989d" } diff --git a/public/themes/tokyonight.json b/data/themes/tokyonight.json similarity index 72% rename from public/themes/tokyonight.json rename to data/themes/tokyonight.json index 7b85fe35..987b8005 100644 --- a/public/themes/tokyonight.json +++ b/data/themes/tokyonight.json @@ -1,16 +1,15 @@ { + "name": "Tokyo Night", "backgroundColor": "#191c2a", "windowColor": "#24283b", - "glowColor": "#394260", - "white": "#c0caf5", - "gray": "#6D7599", + "textColor": "#c0caf5", "black": "#737aa2", "red": "#f7768e", "green": "#9ece6a", "yellow": "#e0af68", "blue": "#7aa2f7", - "cyan": "#7dcfff", "magenta": "#bb9af7", - "violet": "#9d7cd8", - "orange": "#ff9e64" + "cyan": "#7dcfff", + "white": "#c0caf5", + "gray": "#6D7599" } diff --git a/public/themes/verdant.json b/data/themes/verdant.json similarity index 66% rename from public/themes/verdant.json rename to data/themes/verdant.json index 3ae409c2..df383752 100644 --- a/public/themes/verdant.json +++ b/data/themes/verdant.json @@ -1,16 +1,15 @@ { + "name": "Verdant", "backgroundColor": "#DCF0E0", "windowColor": "#CAEBCE", - "glowColor": "#94ab93", - "white": "#000", - "gray": "#66676b", + "textColor": "#000000", "black": "#16161e", "red": "#8a384c", "green": "#1d8262", "yellow": "#c7c41a", "blue": "#1d8699", - "cyan": "#4eb8ba", "magenta": "#b35287", - "violet": "#8872a1", - "orange": "#a65902" + "cyan": "#4eb8ba", + "white": "#ffffff", + "gray": "#66676b" } diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 00000000..daa04535 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,15 @@ +version: '3.8' + +services: + start-page: + container_name: excalith-start-page + image: excalith/start-page + restart: unless-stopped + ports: + - 8080:3000 + volumes: + - data:/app/public/data + +volumes: + data: + name: excalith-start-page \ No newline at end of file diff --git a/next.config.js b/next.config.js index b70e30b5..1e709260 100644 --- a/next.config.js +++ b/next.config.js @@ -25,7 +25,21 @@ const nextConfig = { } ] } - ] + ], + webpack(config) { + config.resolve.fallback = { + // if you miss it, all the other options in fallback, specified + // by next.js will be dropped. + ...config.resolve.fallback, + + fs: false // the solution + } + + return config + }, + env: { + BUILD_MODE: process.env.BUILD_MODE + } } module.exports = nextConfig diff --git a/package.json b/package.json index b331c7e3..778f8668 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "start-page", - "version": "3.0.0", + "version": "3.1.0", "private": true, "scripts": { "dev": "next dev", @@ -9,9 +9,9 @@ "format": "prettier . --write", "export": "next export", "build": "next build", - "docker:build:amd64": "docker buildx build --load --platform linux/amd64 -t excalith/start-page:latest .", - "docker:build:arm64": "docker buildx build --load --platform linux/arm64 -t excalith/start-page:latest .", - "docker:run": "docker run --name start-page --restart=always -p 8080:3000 -d excalith/start-page" + "docker:build:amd64": "docker buildx build --load --platform linux/amd64 -t excalith/start-page:latest .", + "docker:build:arm64": "docker buildx build --load --platform linux/arm64 -t excalith/start-page:latest .", + "docker:run": "docker run -d --name excalith-start-page -p 8080:3000 -v excalith-start-page:/app/public/data --restart unless-stopped excalith/start-page:latest" }, "devDependencies": { "autoprefixer": "^10.4.16", @@ -36,4 +36,4 @@ "react-dom": "18.2.0", "react-error-boundary": "^4.0.12" } -} \ No newline at end of file +} diff --git a/public/favicon.ico b/public/favicon.ico deleted file mode 100644 index d4571c5a1cc38043a2793812c44059a56e00a8ef..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15406 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 L{ILgI?}7gXHzT1C diff --git a/public/themes/onedark.json b/public/themes/onedark.json deleted file mode 100644 index fdeb9f64..00000000 --- a/public/themes/onedark.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "backgroundColor": "#21252b", - "windowColor": "#282c34", - "glowColor": "#3d46687d", - "white": "#abb2bf", - "gray": "#5c6370", - "black": "#282c34", - "red": "#e06c75", - "green": "#98c379", - "yellow": "#e5c07b", - "blue": "#61afef", - "cyan": "#56b6c2", - "magenta": "#c678dd", - "violet": "#9853e3", - "orange": "#d19a66" -} diff --git a/src/components/Config.js b/src/components/Config.js index 6aa10b83..0b9e22e1 100644 --- a/src/components/Config.js +++ b/src/components/Config.js @@ -3,16 +3,6 @@ import Prompt from "@/components/Prompt" import { isURL } from "@/utils/isURL" import { useSettings } from "@/context/settings" import dynamic from "next/dynamic" -import { themes } from "@/utils/themes" - -async function fetchTheme(themeName) { - try { - const theme = await fetch("/themes/" + themeName + ".json").then((res) => res.json()) - return theme - } catch { - return null - } -} const Config = ({ commands, closeCallback }) => { const [command] = useState(commands.join(" ")) @@ -37,18 +27,50 @@ const Config = ({ commands, closeCallback }) => { } else if (cmd === "theme") { if (commands.length === 3) { const themeName = commands[2] - fetchTheme(themeName).then((theme) => { - if (theme === null) { - invalidTheme(theme) - } else { - setTheme(theme, themeName) - } - }) + fetch(`/api/getTheme?name=${themeName}`) + .then((response) => { + if (!response.ok) { + appendToLog(response.statusText, "error") + return + } + return response.json() + }) + .then((theme) => { + if (theme.message) { + appendToLog(theme.message, "error") + } else if (!theme || Object.keys(theme).length === 0) { + appendToLog(`Theme "${themeName}" not found`, "error") + } else { + setTheme(theme, themeName) + } + }) + .catch((error) => { + appendToLog(`Error fetching theme: ${error.message}`, "error") + }) } else { - themes.map((theme) => { - appendToLog(theme) - }) - setDone(true) + fetch("/api/getTheme") + .then((response) => { + if (!response.ok) { + appendToLog(response.statusText, "error") + return + } + return response.json() + }) + .then((data) => { + if (data.message) { + appendToLog(data.message, "error") + } else if (!data || data.length === 0) { + appendToLog("No themes found in theme folder", "error") + } else { + data.forEach((theme) => { + appendToLog(theme) + }) + } + setDone(true) + }) + .catch((error) => { + appendToLog(`Error fetching themes: ${error.message}`, "error") + }) } } else if (cmd === "help") { usageExample() @@ -130,7 +152,7 @@ const Config = ({ commands, closeCallback }) => { } return ( -
+
  • diff --git a/src/components/Editor.js b/src/components/Editor.js index 42508521..fc5d9ef5 100644 --- a/src/components/Editor.js +++ b/src/components/Editor.js @@ -66,7 +66,7 @@ const Editor = () => {

    Invalid JSON

    ) : ( diff --git a/src/components/Fetch.js b/src/components/Fetch.js index 4d487da4..bf1cd5b9 100644 --- a/src/components/Fetch.js +++ b/src/components/Fetch.js @@ -1,6 +1,8 @@ import Prompt from "@/components/Prompt" +import { useState, useEffect } from "react" import { useSettings } from "@/context/settings" import useFetchData from "@/hooks/useFetchData" +import { fetchAsset } from "@/utils/fetchAsset" const paramsPattern = /[^{}]+(?=})/g const titlePattern = /[\w]+(:)/g @@ -8,10 +10,12 @@ const titlePattern = /[\w]+(:)/g const Fetch = ({ closeCallback }) => { const { settings } = useSettings() const [fetchData] = useFetchData() + const [icon, setIcon] = useState(null) const titleColor = settings.fetch.titleColor let mapping = { version: fetchData.version, + theme: settings.theme.name, time: fetchData.time, date: fetchData.date, osName: fetchData.osName, @@ -22,6 +26,19 @@ const Fetch = ({ closeCallback }) => { engineVersion: fetchData.engineVersion } + useEffect(() => { + // Fetch fetch image + fetchAsset(settings.fetch.image) + .then((data) => { + if (data) { + setIcon(data) // Set the image only if there is no warning message + } + }) + .catch((error) => { + console.error("Failed to fetch image:", error) + }) + }, [settings.fetch.image, setIcon]) + function getFetchData(index, item) { const params = item.match(paramsPattern) let titleMatch = item.match(titlePattern) @@ -41,16 +58,14 @@ const Fetch = ({ closeCallback }) => { if (params[0] === "colors") { return (
  • - - - - - - - - - - + + + + + + + +
  • ) } @@ -73,9 +88,9 @@ const Fetch = ({ closeCallback }) => {
    - + {icon && }
    -
    +
    diff --git a/src/components/Help.js b/src/components/Help.js index 67f79fe6..3987b7cc 100644 --- a/src/components/Help.js +++ b/src/components/Help.js @@ -5,7 +5,7 @@ const Help = ({ closeCallback }) => { const { settings } = useSettings() return ( -
    +
    diff --git a/src/components/Meta.js b/src/components/Meta.js index 3cc8f9b3..89251322 100644 --- a/src/components/Meta.js +++ b/src/components/Meta.js @@ -1,21 +1,52 @@ import React, { useEffect, useState, useContext } from "react" import Head from "next/head" import { useSettings } from "@/context/settings" +import { fetchAsset } from "@/utils/fetchAsset" const Meta = () => { const [title, setTitle] = useState("Start Page") + const [iconType, setIconType] = useState("na") + const [icon, setIcon] = useState(null) const { settings } = useSettings() useEffect(() => { + // Set title setTitle(settings.username + " Start Page") - }, [settings.username]) + + // Return if there is no icon + if (!settings.fetch.image) return + + // Set icon type for favicon + const iconExtension = settings.fetch.image.split(".").pop() + switch (iconExtension) { + case "svg": + setIconType("image/svg+xml") + break + case "png": + setIconType("image/png") + break + default: + setIconType("na") + } + + // Fetch icon image + fetchAsset(settings.fetch.image) + .then((data) => { + if (data) { + setIcon(data) + } + }) + .catch((error) => { + console.error("Failed to fetch icon:", error) + }) + }, [settings.fetch.image, settings.username]) return ( {title} - + - + {icon && } ) diff --git a/src/components/Prompt.js b/src/components/Prompt.js index 62064999..0cb59357 100644 --- a/src/components/Prompt.js +++ b/src/components/Prompt.js @@ -18,7 +18,7 @@ const Prompt = ({ command, showSymbol = true }) => { {promptSettings.promptSymbol}{" "} )} - {command && {command}} + {command && {command}} ) } diff --git a/src/components/Search.js b/src/components/Search.js index 422693e0..1f21d510 100644 --- a/src/components/Search.js +++ b/src/components/Search.js @@ -122,7 +122,7 @@ const Search = ({ commandChange, selectionChange }) => {
    { }} /> { const [settings, setSettings] = useState() const [items, setItems] = useState([]) + // Load settings useEffect(() => { - const settings = localStorage.getItem(SETTINGS_KEY) - if (settings && settings !== "undefined") { - try { - setSettings(JSON.parse(settings)) - } catch (e) { - setSettings(defaultConfig) - console.log("Error parsing settings, resetting to default") - } + let data + + if (IS_DOCKER) { + fetch("/api/loadSettings") + .then((response) => response.json()) + .then((data) => setSettings(data)) + .catch(() => setSettings(defaultConfig)) } else { - setSettings(defaultConfig) + data = localStorage.getItem(SETTINGS_KEY) + if (data === "undefined") { + console.log("LocalStorage configuration reset to defaults.") + } + setSettings(data ? JSON.parse(data) : defaultConfig) } }, []) + // Save settings useEffect(() => { if (settings && settings !== "undefined") { - localStorage.setItem(SETTINGS_KEY, JSON.stringify(settings)) + if (IS_DOCKER) { + fetch("/api/saveSettings", { + method: "POST", + headers: { + "Content-Type": "application/json" + }, + body: JSON.stringify(settings) + }) + } else { + localStorage.setItem(SETTINGS_KEY, JSON.stringify(settings)) + } let filterArr = [ "help", @@ -40,13 +55,20 @@ export const SettingsProvider = ({ children }) => { "config help", "config edit", "config import", - "config reset", - "config theme" + "config theme", + "config reset" ] - themes.map((theme) => { - filterArr.push("config theme " + theme) - }) + fetch("/api/getTheme") + .then((response) => response.json()) + .then((data) => { + if (!data.message) { + data.forEach((theme) => { + filterArr.push("config theme " + theme) + }) + } + }) + .catch((error) => console.log(`Error fetching themes: ${error.message}`)) settings.sections.list.map((section) => { section.links.map((link) => { @@ -59,10 +81,12 @@ export const SettingsProvider = ({ children }) => { } }, [settings]) + // Update settings const updateSettings = async (newSettings) => { await setSettings(newSettings) } + // Reset settings const resetSettings = () => { setSettings(defaultConfig) localStorage.setItem(SETTINGS_KEY, JSON.stringify(defaultConfig)) diff --git a/src/hooks/useFetchData.js b/src/hooks/useFetchData.js index 9ea587d9..b2c3d0af 100644 --- a/src/hooks/useFetchData.js +++ b/src/hooks/useFetchData.js @@ -13,6 +13,7 @@ import { useSettings } from "@/context/settings" let data = { version: "Unknown", + theme: "Unknown", time: "Unknown", date: "Unknown", osName: "Unknown", @@ -32,6 +33,7 @@ const useFetchData = () => { useEffect(() => { data = { version: version, + theme: settings.theme.name, time: moment().format(settings.fetch.timeFormat), date: moment().format(settings.fetch.dateFormat), osName: osName, diff --git a/src/pages/_app.js b/src/pages/_app.js index 03693cad..25fe50b8 100644 --- a/src/pages/_app.js +++ b/src/pages/_app.js @@ -1,7 +1,7 @@ import React from "react" import { ErrorBoundary } from "react-error-boundary" -import "@/styles/globals.css" import { SettingsProvider } from "@/context/settings" +import "@/styles/globals.css" function fallbackRender({ error, resetErrorBoundary }) { const isDataError = error.message.includes("undefined") @@ -19,7 +19,7 @@ function fallbackRender({ error, resetErrorBoundary }) { } return ( -
    +

    Something went wrong on your side:

    {error.message}

    diff --git a/src/pages/api/getData.js b/src/pages/api/getData.js new file mode 100644 index 00000000..ab304a03 --- /dev/null +++ b/src/pages/api/getData.js @@ -0,0 +1,51 @@ +import fs from "fs" +import path from "path" + +export default function handler(req, res) { + const { file } = req.query + + // Construct the full path to the file + const filePath = path.join(process.cwd(), "data", file) + + // Determine the MIME type of the file based on its extension + let mimeType + const extension = path.extname(file).toLowerCase() + switch (extension) { + case ".json": + mimeType = "application/json" + break + case ".svg": + mimeType = "image/svg+xml" + break + case ".png": + mimeType = "image/png" + break + case ".jpg": + case ".jpeg": + mimeType = "image/jpeg" + break + case ".webp": + mimeType = "image/webp" + break + case ".gif": + mimeType = "image/gif" + break + default: + mimeType = "application/octet-stream" // Fallback MIME type + } + + try { + // Read the file + const data = fs.readFileSync(filePath) + + // Set the Content-Type header to the appropriate value for the file + if (mimeType) { + res.setHeader("Content-Type", mimeType) + } + // Send the file data in the response + res.end(data) + } catch (err) { + res.status(200).json({ warning: "File not found: " + file }) + res.end() // Send the response + } +} diff --git a/src/pages/api/getTheme.js b/src/pages/api/getTheme.js new file mode 100644 index 00000000..4da005bf --- /dev/null +++ b/src/pages/api/getTheme.js @@ -0,0 +1,39 @@ +import fs from "fs" +import path from "path" + +export default function handler(req, res) { + const themeName = req.query.name + const themesDirectory = path.join(process.cwd(), "data", "themes") + + if (!fs.existsSync(themesDirectory)) { + res.status(200).json({ + message: "Themes folder does not exist. Please create one in the data folder" + }) + return + } + + if (themeName) { + // If a theme name is provided, return the specified theme + const filePath = path.join(themesDirectory, `${themeName}.json`) + + if (!fs.existsSync(filePath)) { + res.status(200).json({ message: `Theme ${themeName} not found` }) + return + } + + const fileContents = fs.readFileSync(filePath, "utf8") + const theme = JSON.parse(fileContents) + + res.status(200).json(theme) + } else { + // If no theme name is provided, return all themes + const fileNames = fs.readdirSync(themesDirectory) + const themeNames = fileNames.map((fileName) => path.parse(fileName).name) + + if (themeNames.length === 0) { + res.status(200).json({ message: "No themes found in theme folder" }) + } else { + res.status(200).json(themeNames) + } + } +} diff --git a/src/pages/api/loadSettings.js b/src/pages/api/loadSettings.js new file mode 100644 index 00000000..85e8bbbb --- /dev/null +++ b/src/pages/api/loadSettings.js @@ -0,0 +1,8 @@ +import fs from "fs" +import path from "path" + +export default function handler(req, res) { + const filePath = path.join(process.cwd(), "data", "settings.json") + const fileContents = fs.readFileSync(filePath, "utf8") + res.status(200).json(JSON.parse(fileContents)) +} diff --git a/src/pages/api/saveSettings.js b/src/pages/api/saveSettings.js new file mode 100644 index 00000000..cfe5cce4 --- /dev/null +++ b/src/pages/api/saveSettings.js @@ -0,0 +1,8 @@ +import fs from "fs" +import path from "path" + +export default function handler(req, res) { + const filePath = path.join(process.cwd(), "data", "settings.json") + fs.writeFileSync(filePath, JSON.stringify(req.body, null, 2)) + res.status(200).json({ message: "Settings saved" }) +} diff --git a/src/pages/index.js b/src/pages/index.js index 2471c825..b7014a72 100644 --- a/src/pages/index.js +++ b/src/pages/index.js @@ -5,9 +5,11 @@ import Terminal from "@/components/Terminal" import "@fontsource/fira-code/400.css" import "@fontsource/fira-code/600.css" import { useSettings } from "@/context/settings" +import { fetchAsset } from "@/utils/fetchAsset" export default function Home() { const { settings } = useSettings() + const [wallpaper, setWallpaper] = useState(null) const [isReady, setIsReady] = useState(false) const [isLoaded, setIsLoaded] = useState(false) @@ -23,17 +25,18 @@ export default function Home() { const documentStyle = document.documentElement.style // Set Terminal - documentStyle.setProperty("--glow-color", settings.theme.glowColor) + documentStyle.setProperty("--glow-color", settings.terminal.windowGlowColor) documentStyle.setProperty("--background-color", settings.theme.backgroundColor) - if (settings.terminal.blur > 0) { - documentStyle.setProperty("--window-color", "transparent") - documentStyle.setProperty("--window-opacity", 1) - documentStyle.setProperty("--window-blur", settings.terminal.blur + "px") - } else { + // Set Terminal Opacity / Blur (if opacity is 0, blur is used instead) + if (settings.terminal.opacity > 0) { documentStyle.setProperty("--window-color", settings.theme.windowColor) documentStyle.setProperty("--window-opacity", settings.terminal.opacity) documentStyle.setProperty("--window-blur", 0 + "px") + } else { + documentStyle.setProperty("--window-color", "transparent") + documentStyle.setProperty("--window-opacity", 1) + documentStyle.setProperty("--window-blur", settings.terminal.blur + "px") } // Set Prompt Selection Color @@ -45,10 +48,10 @@ export default function Home() { ) // Set URL Color - documentStyle.setProperty("--url-default", "var(--" + settings.urlLaunch.defaultColor + ")") documentStyle.setProperty("--url-hover", "var(--" + settings.urlLaunch.hoverColor + ")") // Set Text Colors + documentStyle.setProperty("--text-color", settings.theme.textColor) documentStyle.setProperty("--white", settings.theme.white) documentStyle.setProperty("--gray", settings.theme.gray) documentStyle.setProperty("--black", settings.theme.black) @@ -56,9 +59,8 @@ export default function Home() { documentStyle.setProperty("--green", settings.theme.green) documentStyle.setProperty("--yellow", settings.theme.yellow) documentStyle.setProperty("--blue", settings.theme.blue) - documentStyle.setProperty("--cyan", settings.theme.cyan) documentStyle.setProperty("--magenta", settings.theme.magenta) - documentStyle.setProperty("--violet", settings.theme.violet) + documentStyle.setProperty("--cyan", settings.theme.cyan) // Check text glow if (settings.terminal.textGlow) { @@ -67,6 +69,17 @@ export default function Home() { document.body.classList.remove("text-glow") } + // Set Wallpaper + fetchAsset(settings.wallpaper.url) + .then((data) => { + if (data) { + setWallpaper(data) + } + }) + .catch((error) => { + console.error("Failed to fetch wallpaper:", error) + }) + setIsReady(true) }, [settings]) @@ -75,18 +88,17 @@ export default function Home() { {isReady && ( <> - {settings.wallpaper.url && ( + {wallpaper && ( { + onLoad={() => { setIsLoaded(true) }} /> diff --git a/src/styles/globals.css b/src/styles/globals.css index 7661adef..8bf37327 100644 --- a/src/styles/globals.css +++ b/src/styles/globals.css @@ -9,6 +9,7 @@ --window-color: #1e212b; --window-blur: 0px; + --text-color: #e2e2e2; --white: #e2e2e2; --gray: #97989d; --black: #16161e; @@ -18,11 +19,9 @@ --blue: #2bc3de; --cyan: #62e0e2; --magenta: #e069aa; - --violet: #d1aff8; - --orange: #ff8800; - --url-default: var(--white); - --url-hover: var(--violet); + --url-default: var(--text-color); + --url-hover: var(--magenta); --selection-fg: var(--black); --selection-bg: var(--yellow); diff --git a/src/utils/fetchAsset.js b/src/utils/fetchAsset.js new file mode 100644 index 00000000..f459c30c --- /dev/null +++ b/src/utils/fetchAsset.js @@ -0,0 +1,26 @@ +import { isURL } from "@/utils/isURL" + +export async function fetchAsset(assetPath) { + if (!assetPath) { + // If assetPath is null or undefined, return an empty string + return "" + } + if (isURL(assetPath)) { + // If it's a URL, return it directly + return assetPath + } else { + // If it's not a URL, make an API call to fetch the asset + const response = await fetch(`/api/getData?file=${assetPath}`) + const data = await response + .clone() + .json() + .catch(() => response.blob()) + if (data.warning) { + console.log("File not found: " + assetPath) + return "" // Return an empty string + } + // Create a blob URL for the asset data + const url = URL.createObjectURL(data) + return url + } +} diff --git a/src/utils/themes.js b/src/utils/themes.js deleted file mode 100644 index bef1b90b..00000000 --- a/src/utils/themes.js +++ /dev/null @@ -1,14 +0,0 @@ -export const themes = [ - "default", - "bushido", - "catppuccin", - "dracula", - "everforest-dark", - "hacker", - "monokai", - "nord", - "onedark", - "synthwave", - "tokyonight", - "verdant" -] diff --git a/startpage.config.js b/startpage.config.js deleted file mode 100644 index ad9f7d12..00000000 --- a/startpage.config.js +++ /dev/null @@ -1,276 +0,0 @@ -const defaultConfig = { - username: "Excalith", - theme: { - backgroundColor: "#121317", - windowColor: "#1e212b", - glowColor: "#291f3325", - white: "#e2e2e2", - gray: "#97989d", - black: "#16161e", - red: "#ec6183", - green: "#2ed8a2", - yellow: "#e8b195", - blue: "#2bc3de", - cyan: "#62e0e2", - magenta: "#e069aa", - violet: "#d1aff8", - orange: "#ff8800" - }, - wallpaper: { - url: "https://raw.githubusercontent.com/excalith/.dotfiles/72154278fed732e84dd8f2dfe3f5c797bb8d91a2/assets/wallpaper/gnome-blobs.svg", - easing: "ease-in-out", - fadeIn: true, - blur: false - }, - terminal: { - fixedHeight: true, - windowGlow: true, - textGlow: false, - opacity: 1, - blur: 16 - }, - prompt: { - ctrlC: true, - placeholder: "command...", - placeholderColor: "gray", - userColor: "green", - atColor: "gray", - hostColor: "magenta", - promptColor: "magenta", - promptSymbol: "❯", - caretColor: "green", - selectionBg: "yellow", - selectionFg: "black" - }, - fetch: { - timeFormat: "HH:mm", - dateFormat: "DD/MM/YYYY", - titleColor: "yellow", - image: "icon.svg", - data: [ - "Time: {time}", - "Date: {date}", - "{seperator}", - "OS: {osName} {osVersion}", - "Browser: {browser} {browserVersion}", - "Engine: {engineName}", - "{seperator}", - "{colors}" - ] - }, - urlLaunch: { - target: "_self", - defaultColor: "white", - hoverColor: "violet" - }, - search: { - default: "https://google.com/search?q=", - target: "_self", - shortcutRegex: "([A-Za-z0-9]+) (.*)", - shortcuts: [ - { - alias: "g", - name: "Google Search", - url: "https://google.com/search?q={}" - }, - { - alias: "d", - name: "DuckDuckGo Search", - url: "https://duckduckgo.com/?q={}" - }, - { - alias: "b", - name: "Brave Search", - url: "https://search.brave.com/search?q={}" - }, - { - alias: "gh", - name: "Github Search", - url: "https://github.com/search?q={}" - }, - { - alias: "s", - name: "Stack Overflow Search", - url: "https://stackoverflow.com/search?q={}" - }, - { - alias: "r", - name: "Subreddit Search", - url: "https://reddit.com/r/{}" - }, - { - alias: "w", - name: "Wikipedia Search", - url: "https://en.wikipedia.org/wiki/{}" - } - ] - }, - sections: { - list: [ - { - title: "General", - color: "green", - align: "left", - links: [ - { - name: "Portfolio", - url: "https://cancellek.com", - icon: "mdi:web" - }, - { - name: "Keybase", - url: "https://keybase.io/", - icon: "fa-brands:keybase" - }, - { - name: "GPT", - url: "https://chat.openai.com/", - icon: "simple-icons:openai" - }, - { - name: "OCI", - url: "https://www.oracle.com/cloud/", - icon: "simple-icons:oracle" - } - ] - }, - { - title: "Dev", - color: "magenta", - align: "left", - links: [ - { - name: "GitHub", - url: "https://github.com", - icon: "mdi:github" - }, - { - name: "GitLab", - url: "https://gitlab.com", - icon: "ph:gitlab-logo-simple-fill" - }, - { - name: "Dev.to", - url: "https://dev.to", - icon: "material-symbols:logo-dev" - }, - { - name: "Stack Overflow", - url: "https://stackoverflow.com/", - icon: "mdi:stack-overflow" - } - ] - }, - { - title: "Social", - color: "violet", - align: "left", - links: [ - { - name: "Twitter", - url: "https://twitter.com", - icon: "mdi:twitter" - }, - { - name: "Mastodon", - url: "https://mastodon.social/", - icon: "ri:mastodon-fill" - }, - { - name: "Reddit", - url: "https://reddit.com", - icon: "mdi:reddit" - }, - { - name: "Polywork", - url: "https://polywork.com", - icon: "simple-icons:polywork" - } - ] - }, - { - title: "Gaming", - color: "cyan", - align: "left", - links: [ - { - name: "Polygon", - url: "https://polygon.com", - icon: "uil:polygon" - }, - { - name: "IGN", - url: "https://ign.com", - icon: "mdi:currency-sign" - }, - { - name: "RPS", - url: "https://rockpapershotgun.com/", - icon: "ph:toilet-paper-bold" - }, - { - name: "80lv", - url: "https://80.lv/", - icon: "tabler:hand-rock" - } - ] - }, - { - title: "Science", - color: "blue", - align: "left", - links: [ - { - name: "PopSci", - url: "https://popsci.com/", - icon: "material-symbols:science" - }, - { - name: "Space", - url: "fa6-solid:user-astronaut", - icon: "mdi:reddit" - }, - { - name: "NASA", - url: "https://blogs.nasa.gov/", - icon: "simple-icons:nasa" - }, - { - name: "ESA", - url: "https://blogs.esa.int/", - icon: "mdi:black-mesa" - } - ] - }, - { - title: "Tech", - color: "yellow", - align: "left", - links: [ - { - name: "TechCrunch", - url: "https://techcrunch.com/", - icon: "game-icons:techno-heart" - }, - { - name: "Verge", - url: "https://www.theverge.com/", - icon: "arcticons:verge" - }, - { - name: "It's Foss", - url: "https://itsfoss.com/", - icon: "ri:mastodon-fill" - }, - { - name: "9To5 Linux", - url: "https://9to5linux.com/", - icon: "uil:linux" - } - ] - } - ] - } -} - -export default defaultConfig diff --git a/tailwind.config.js b/tailwind.config.js index 53466e89..a6de4556 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -25,6 +25,7 @@ module.exports = { "window-color": "var(--window-color)", "glow-color": "var(--glow-color)", transparent: "#00000000", + textColor: "var(--text-color)", white: "var(--white)", gray: "var(--gray)", black: "var(--black)", @@ -33,9 +34,7 @@ module.exports = { yellow: "var(--yellow)", blue: "var(--blue)", cyan: "var(--cyan)", - magenta: "var(--magenta)", - violet: "var(--violet)", - orange: "var(--orange)" + magenta: "var(--magenta)" }, extend: { maxWidth: { @@ -71,7 +70,7 @@ module.exports = { "glow-color", { pattern: - /(bg|text|border|caret)-(white|gray|black|red|green|yellow|blue|cyan|magenta|violet|orange)/ + /(bg|text|border|caret)-(white|gray|black|red|green|yellow|blue|cyan|magenta|textColor)/ } ] }