diff --git a/.eslintignore b/.eslintignore
deleted file mode 100644
index 3897265..0000000
--- a/.eslintignore
+++ /dev/null
@@ -1,13 +0,0 @@
-.DS_Store
-node_modules
-/build
-/.svelte-kit
-/package
-.env
-.env.*
-!.env.example
-
-# Ignore files for PNPM, NPM and YARN
-pnpm-lock.yaml
-package-lock.json
-yarn.lock
diff --git a/.eslintrc.cjs b/.eslintrc.cjs
deleted file mode 100644
index ebc1958..0000000
--- a/.eslintrc.cjs
+++ /dev/null
@@ -1,30 +0,0 @@
-module.exports = {
- root: true,
- extends: [
- 'eslint:recommended',
- 'plugin:@typescript-eslint/recommended',
- 'plugin:svelte/recommended',
- 'prettier'
- ],
- parser: '@typescript-eslint/parser',
- plugins: ['@typescript-eslint'],
- parserOptions: {
- sourceType: 'module',
- ecmaVersion: 2020,
- extraFileExtensions: ['.svelte']
- },
- env: {
- browser: true,
- es2017: true,
- node: true
- },
- overrides: [
- {
- files: ['*.svelte'],
- parser: 'svelte-eslint-parser',
- parserOptions: {
- parser: '@typescript-eslint/parser'
- }
- }
- ]
-};
diff --git a/.forgejo/workflows/build.yaml b/.forgejo/workflows/build.yaml
new file mode 100644
index 0000000..a1e59a8
--- /dev/null
+++ b/.forgejo/workflows/build.yaml
@@ -0,0 +1,121 @@
+on: [push]
+jobs:
+ check-changes:
+ runs-on: docker
+ outputs:
+ all_changed_and_modified_files_count: ${{ steps.changed-files.outputs.all_changed_and_modified_files_count }}
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - name: Get changed files count
+ id: changed-files
+ uses: tj-actions/changed-files@v45
+ with:
+ files_ignore: 'doc/**,README.md,Dockerfile,.*'
+ files_ignore_separator: ','
+ - name: Print changed files count
+ run: >
+ echo "Changed files count: ${{
+ steps.changed-files.outputs.all_changed_and_modified_files_count }}"
+
+ build:
+ needs: check-changes
+ runs-on: docker
+ container:
+ image: ghcr.io/catthehacker/ubuntu:js-22.04
+ if: ${{ needs.check-changes.outputs.all_changed_and_modified_files_count >= 1 }}
+ permissions:
+ contents: write
+
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ submodules: recursive
+ - uses: actions/setup-node@v4
+ with:
+ node-version: lts/*
+ cache: yarn
+ cache-dependency-path: '**/yarn.lock'
+ - uses: actions/cache@v4
+ with:
+ path: |
+ ~/.cache/pip
+ ~/node_modules
+ key: ${{ runner.os }}-pio
+ - uses: actions/setup-python@v5
+ with:
+ python-version: '>=3.10'
+ - name: Get current date
+ id: dateAndTime
+ run: echo "dateAndTime=$(date +'%Y-%m-%d-%H:%M')" >> $GITHUB_OUTPUT
+ - name: Install mklittlefs
+ run: >
+ git clone https://github.com/earlephilhower/mklittlefs.git /tmp/mklittlefs &&
+ cd /tmp/mklittlefs &&
+ git submodule update --init &&
+ make dist
+ - name: Install yarn
+ run: yarn && yarn postinstall
+ - name: Run linter
+ run: yarn lint
+ - name: Run vitest tests
+ run: yarn vitest run
+ - name: Install Playwright Browsers
+ run: npx playwright install --with-deps
+ - name: Run Playwright tests
+ run: npx playwright test
+ - name: Build WebUI
+ run: yarn build
+ - name: Get current block
+ id: getBlockHeight
+ run: echo "blockHeight=$(curl -s https://mempool.space/api/blocks/tip/height)" >> $GITHUB_OUTPUT
+ - name: Write block height to file
+ env:
+ BLOCK_HEIGHT: ${{ steps.getBlockHeight.outputs.blockHeight }}
+ run: mkdir -p output && echo "$BLOCK_HEIGHT" > output/version.txt
+ - name: gzip build for LittleFS
+ run: find dist -type f ! -name ".*" -exec sh -c 'mkdir -p "build_gz/$(dirname "${1#dist/}")" && gzip -k "$1" -c > "build_gz/${1#dist/}".gz' _ {} \;
+ - name: Write git rev to file
+ run: echo "$GITHUB_SHA" > build_gz/fs_hash.txt && echo "$GITHUB_SHA" > output/commit.txt
+ - name: Check GZipped directory size
+ run: |
+ # Set the threshold size in bytes
+ THRESHOLD=410000
+
+ # Calculate the total size of files in the directory
+ DIRECTORY_SIZE=$(du -b -s build_gz | awk '{print $1}')
+
+ # Fail the workflow if the size exceeds the threshold
+ if [ "$DIRECTORY_SIZE" -gt "$THRESHOLD" ]; then
+ echo "Directory size exceeds the threshold of $THRESHOLD bytes"
+ exit 1
+ else
+ echo "Directory size is within the threshold $DIRECTORY_SIZE"
+ fi
+ - name: Create tarball
+ run: tar czf webui.tgz --strip-components=1 dist
+ - name: Build LittleFS
+ run: |
+ set -e
+ /tmp/mklittlefs/mklittlefs -c build_gz -s 410000 output/littlefs.bin
+ - name: Upload artifacts
+ uses: https://code.forgejo.org/forgejo/upload-artifact@v4
+ with:
+ path: |
+ webui.tgz
+ output/littlefs.bin
+ - name: Create release
+ if: github.event_name == 'push' && github.ref == 'refs/heads/main'
+ uses: https://code.forgejo.org/actions/forgejo-release@v2.4.0
+ with:
+ url: 'https://git.btclock.dev/'
+ repo: '${{ github.repository }}'
+ direction: upload
+ tag: ${{ steps.getBlockHeight.outputs.blockHeight }}
+ sha: '${{ github.sha }}'
+ release-dir: output
+ token: ${{ secrets.TOKEN }}
+ override: false
+ verbose: false
+ release-notes-assistant: false
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 0000000..c44914e
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,15 @@
+# To get started with Dependabot version updates, you'll need to specify which
+# package ecosystems to update and where the package manifests are located.
+# Please see the documentation for all configuration options:
+# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
+
+version: 2
+updates:
+ - package-ecosystem: 'npm' # See documentation for possible values
+ directory: '/' # Location of package manifests
+ schedule:
+ interval: 'daily'
+ versioning-strategy: 'increase-if-necessary'
+ ignore:
+ - dependency-name: '*'
+ update-types: ['version-update:semver-major']
diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml
index e3f9211..310777b 100644
--- a/.github/workflows/workflow.yml
+++ b/.github/workflows/workflow.yml
@@ -3,35 +3,56 @@ name: BTClock WebUI CI
on: [push]
env:
- PUBLIC_BASE_URL: ""
+ PUBLIC_BASE_URL: ''
jobs:
- build:
+ check-changes:
runs-on: ubuntu-latest
+ outputs:
+ all_changed_and_modified_files_count: ${{ steps.changed-files.outputs.all_changed_and_modified_files_count }}
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - name: Get changed files count
+ id: changed-files
+ uses: tj-actions/changed-files@v45
+ with:
+ files_ignore: 'doc/**,README.md,Dockerfile,.*'
+ files_ignore_separator: ','
+ - name: Print changed files count
+ run: >
+ echo "Changed files count: ${{
+ steps.changed-files.outputs.all_changed_and_modified_files_count }}"
+
+ build:
+ needs: check-changes
+ runs-on: ubuntu-latest
+ if: ${{ needs.check-changes.outputs.all_changed_and_modified_files_count >= 1 }}
permissions:
contents: write
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
with:
submodules: recursive
- - uses: actions/setup-node@v3
+ - uses: actions/setup-node@v4
with:
node-version: lts/*
cache: yarn
cache-dependency-path: '**/yarn.lock'
- - uses: actions/cache@v3
+ - uses: actions/cache@v4
with:
path: |
~/.cache/pip
~/node_modules
key: ${{ runner.os }}-pio
- - uses: actions/setup-python@v4
+ - uses: actions/setup-python@v5
with:
python-version: '3.9'
- name: Get current date
id: dateAndTime
- run: echo "dateAndTime=$(date +'%Y-%m-%d-%H-%M')" >> $GITHUB_OUTPUT
+ run: echo "dateAndTime=$(date +'%Y-%m-%d-%H:%M')" >> $GITHUB_OUTPUT
- name: Install mklittlefs
run: >
git clone https://github.com/earlephilhower/mklittlefs.git /tmp/mklittlefs &&
@@ -40,27 +61,76 @@ jobs:
make dist
- name: Install yarn
run: yarn && yarn postinstall
+ - name: Run linter
+ run: yarn lint
+ - name: Run vitest tests
+ run: yarn vitest run
+ - name: Install Playwright Browsers
+ run: npx playwright install --with-deps
+ - name: Run Playwright tests
+ run: npx playwright test
- name: Build WebUI
run: yarn build
+ - name: Get current block
+ id: getBlockHeight
+ run: echo "blockHeight=$(curl -s https://mempool.space/api/blocks/tip/height)" >> $GITHUB_OUTPUT
+ - name: Write block height to file
+ env:
+ BLOCK_HEIGHT: ${{ steps.getBlockHeight.outputs.blockHeight }}
+ run: mkdir -p output && echo "$BLOCK_HEIGHT" > output/version.txt
- name: gzip build for LittleFS
run: find dist -type f ! -name ".*" -exec sh -c 'mkdir -p "build_gz/$(dirname "${1#dist/}")" && gzip -k "$1" -c > "build_gz/${1#dist/}".gz' _ {} \;
+ - name: Write git rev to file
+ run: echo "$GITHUB_SHA" > build_gz/fs_hash.txt && echo "$GITHUB_SHA" > output/commit.txt
+ - name: Check GZipped directory size
+ run: |
+ # Set the threshold size in bytes
+ THRESHOLD=409600
+
+ # Calculate the total size of files in the directory
+ DIRECTORY_SIZE=$(du -b -s build_gz | awk '{print $1}')
+
+ # Fail the workflow if the size exceeds the threshold
+ if [ "$DIRECTORY_SIZE" -gt "$THRESHOLD" ]; then
+ echo "Directory size exceeds the threshold of $THRESHOLD bytes"
+ exit 1
+ else
+ echo "Directory size is within the threshold $DIRECTORY_SIZE"
+ fi
- name: Create tarball
run: tar czf webui.tgz --strip-components=1 dist
- name: Build LittleFS
- run: /tmp/mklittlefs/mklittlefs -c build_gz -s 409600 littlefs.bin
+ run: |
+ set -e
+ /tmp/mklittlefs/mklittlefs -c build_gz -s 409600 output/littlefs.bin
- name: Upload artifacts
- uses: actions/upload-artifact@v3
+ uses: actions/upload-artifact@v4
with:
path: |
webui.tgz
- littlefs.bin
+ output/littlefs.bin
- name: Create release
+ if: github.event_name == 'push' && github.ref == 'refs/heads/main'
uses: ncipollo/release-action@v1
with:
- tag: main
+ tag: ${{ steps.getBlockHeight.outputs.blockHeight }}
commit: main
- name: release-${{ steps.date.outputs.dateAndTime }}
- artifacts: "littlefs.bin,webui.tgz"
+ name: release-${{ steps.getBlockHeight.outputs.blockHeight }}
+ artifacts: 'output/littlefs.bin,webui.tgz'
allowUpdates: true
removeArtifacts: true
- makeLatest: true
\ No newline at end of file
+ makeLatest: true
+ - name: Pushes littlefs.bin to web flasher
+ if: github.event_name == 'push' && github.ref == 'refs/heads/main'
+ id: push_directory
+ uses: cpina/github-action-push-to-another-repository@main
+ env:
+ SSH_DEPLOY_KEY: ${{ secrets.SSH_DEPLOY_KEY }}
+ with:
+ source-directory: output/
+ target-directory: webui/
+ destination-github-username: 'btclock'
+ destination-repository-name: 'web-flasher'
+ target-branch: main
+ user-name: ${{github.actor}}
+ user-email: ${{github.actor}}@users.noreply.github.com
diff --git a/.gitignore b/.gitignore
index 5a5ed98..441fdc9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,3 +12,4 @@ dist
vite.config.js.timestamp-*
vite.config.ts.timestamp-*
yarn-error.log
+test-results/
\ No newline at end of file
diff --git a/.prettierignore b/.prettierignore
index 297772e..96ca2e4 100644
--- a/.prettierignore
+++ b/.prettierignore
@@ -8,7 +8,7 @@ node_modules
!.env.example
dist/
build_gz
-
+dist/**
# Ignore files for PNPM, NPM and YARN
pnpm-lock.yaml
diff --git a/.vscode/settings.json b/.vscode/settings.json
index c0772af..2fcadf2 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -1,6 +1,5 @@
{
- "i18n-ally.localesPaths": [
- "src/lib/locales"
- ],
- "i18n-ally.keystyle": "nested"
-}
\ No newline at end of file
+ "i18n-ally.localesPaths": ["src/lib/locales"],
+ "i18n-ally.keystyle": "nested",
+ "i18n-ally.sourceLanguage": "en"
+}
diff --git a/README.md b/README.md
index 5c91169..805bfa1 100644
--- a/README.md
+++ b/README.md
@@ -1,38 +1,41 @@
-# create-svelte
+# BTClock WebUI
-Everything you need to build a Svelte project, powered by [`create-svelte`](https://github.com/sveltejs/kit/tree/master/packages/create-svelte).
+[![BTClock CI](https://github.com/btclock/webui/actions/workflows/workflow.yml/badge.svg)](https://github.com/btclock/webui2/actions/workflows/workflow.yml)
-## Creating a project
+The web user-interface for the BTClock, based on Svelte-kit. It uses Bootstrap for the lay-out.
-If you're seeing this, you've probably already done this step. Congrats!
-
-```bash
-# create a new project in the current directory
-npm create svelte@latest
-
-# create a new project in my-app
-npm create svelte@latest my-app
-```
+![Screenshot](doc/screenshot.webp)
+![Screenshot Dark](doc/screenshot-dark.webp)
## Developing
-Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server:
+After installed dependencies with `yarn`, start a development server:
```bash
-npm run dev
+yarn dev
# or start the server and open the app in a new browser tab
-npm run dev -- --open
+yarn dev -- --open
```
## Building
-To create a production version of your app:
+To create a production version of the WebUI:
```bash
-npm run build
+yarn build
```
-You can preview the production build with `npm run preview`.
+Make sure the postinstall script is ran, because otherwise the filenames are to long for the LittleFS filesystem.
-> To deploy your app, you may need to install an [adapter](https://kit.svelte.dev/docs/adapters) for your target environment.
+## Deploying
+
+To upload the firmware to the BTClock, you need to GZIP all the files. You can use the python script `gzip_build.py` for that.
+
+Then you can make a `LittleFS.bin` with mklittlefs:
+
+```bash
+mklittlefs -c build_gz -s 409600 littlefs.bin
+```
+
+You can preview the production build with `yarn preview`.
diff --git a/doc/LICENSE.txt b/doc/LICENSE.txt
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/doc/LICENSE.txt
@@ -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.
diff --git a/doc/screenshot-dark.webp b/doc/screenshot-dark.webp
new file mode 100644
index 0000000..a225afe
Binary files /dev/null and b/doc/screenshot-dark.webp differ
diff --git a/doc/screenshot.webp b/doc/screenshot.webp
new file mode 100644
index 0000000..0a5d87e
Binary files /dev/null and b/doc/screenshot.webp differ
diff --git a/eslint.config.js b/eslint.config.js
new file mode 100644
index 0000000..62dbd03
--- /dev/null
+++ b/eslint.config.js
@@ -0,0 +1,33 @@
+import js from '@eslint/js';
+import ts from 'typescript-eslint';
+import svelte from 'eslint-plugin-svelte';
+import prettier from 'eslint-config-prettier';
+import globals from 'globals';
+
+/** @type {import('eslint').Linter.Config[]} */
+export default [
+ js.configs.recommended,
+ ...ts.configs.recommended,
+ ...svelte.configs['flat/recommended'],
+ prettier,
+ ...svelte.configs['flat/prettier'],
+ {
+ languageOptions: {
+ globals: {
+ ...globals.browser,
+ ...globals.node
+ }
+ }
+ },
+ {
+ files: ['**/*.svelte'],
+ languageOptions: {
+ parserOptions: {
+ parser: ts.parser
+ }
+ }
+ },
+ {
+ ignores: ['build/', '.svelte-kit/', 'dist/']
+ }
+];
diff --git a/extra/icons/flash.svg b/extra/icons/flash.svg
new file mode 100644
index 0000000..23ca832
--- /dev/null
+++ b/extra/icons/flash.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/extra/icons/lightning-bolt.svg b/extra/icons/lightning-bolt.svg
new file mode 100644
index 0000000..78cb2c5
--- /dev/null
+++ b/extra/icons/lightning-bolt.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/extra/icons/pickaxe.svg b/extra/icons/pickaxe.svg
new file mode 100644
index 0000000..2c85559
--- /dev/null
+++ b/extra/icons/pickaxe.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/extra/icons/rocket.svg b/extra/icons/rocket.svg
new file mode 100644
index 0000000..cf9b58f
--- /dev/null
+++ b/extra/icons/rocket.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/package.json b/package.json
index 744d61e..930fc00 100644
--- a/package.json
+++ b/package.json
@@ -10,37 +10,63 @@
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
"lint": "prettier --check . && eslint .",
"format": "prettier --write .",
- "postinstall": "patch-package"
+ "postinstall": "patch-package",
+ "test": "npm run test:integration && npm run test:unit",
+ "test:integration": "playwright test",
+ "test:unit": "vitest"
},
"devDependencies": {
"@rollup/plugin-json": "^6.0.1",
- "@sveltejs/adapter-auto": "^2.0.0",
- "@sveltejs/adapter-static": "^2.0.3",
- "@sveltejs/kit": "^1.27.4",
+ "@sveltejs/adapter-auto": "^3.0.0",
+ "@sveltejs/adapter-static": "^3.0.0",
+ "@sveltejs/kit": "^2.0.0",
+ "@sveltejs/vite-plugin-svelte": "^3.0.1",
+ "@testing-library/svelte": "^5.2.1",
"@types/swagger-ui": "^3.52.4",
- "@typescript-eslint/eslint-plugin": "^6.0.0",
- "@typescript-eslint/parser": "^6.0.0",
- "eslint": "^8.28.0",
- "eslint-config-prettier": "^9.0.0",
- "eslint-plugin-svelte": "^2.30.0",
- "prettier": "^3.0.0",
- "prettier-plugin-svelte": "^3.0.0",
- "sass": "^1.69.5",
- "svelte": "^4.0.5",
- "svelte-check": "^3.6.0",
- "tslib": "^2.4.1",
- "typescript": "^5.0.0",
- "vite": "^4.4.2"
+ "@typescript-eslint/eslint-plugin": "^8.7.0",
+ "@typescript-eslint/parser": "^8.7.0",
+ "@vitest/ui": "^2.0.5",
+ "eslint": "^9.11.0",
+ "eslint-config-prettier": "^9.1.0",
+ "eslint-plugin-svelte": "^2.36.0",
+ "jsdom": "^25.0.0",
+ "prettier": "^3.3.3",
+ "prettier-plugin-svelte": "^3.2.6",
+ "sass": "^1.79.3",
+ "svelte": "^4.2.19",
+ "svelte-check": "^4.0.2",
+ "svelte-preprocess": "^6.0.2",
+ "tslib": "^2.7.0",
+ "typescript": "^5.5.4",
+ "typescript-eslint": "^8.7.0",
+ "vite": "^5.4.7",
+ "vitest": "^2.1.1",
+ "vitest-github-actions-reporter": "^0.11.0"
},
"type": "module",
"dependencies": {
- "@fontsource/antonio": "^5.0.17",
- "@fontsource/oswald": "^5.0.17",
- "@fontsource/ubuntu": "^5.0.8",
- "bootstrap": "^5.3.2",
+ "@fontsource/antonio": "^5.1.0",
+ "@fontsource/oswald": "^5.1.0",
+ "@fontsource/ubuntu": "^5.1.0",
+ "@noble/secp256k1": "^2.1.0",
+ "@playwright/test": "^1.46.0",
+ "@popperjs/core": "^2.11.8",
+ "@sveltestrap/sveltestrap": "^6.2.7",
+ "@testing-library/jest-dom": "^6.5.0",
+ "bootstrap": "^5.3.3",
+ "bootstrap-icons": "^1.11.3",
+ "msgpack-es": "^0.0.5",
+ "nostr-tools": "^2.7.1",
"patch-package": "^8.0.0",
- "svelte-i18n": "^4.0.0",
- "sveltestrap": "^5.11.2",
- "swagger-ui": "^5.10.0"
- }
+ "svelte-bootstrap-icons": "^3.1.1",
+ "svelte-i18n": "^4.0.0"
+ },
+ "resolutions": {
+ "es5-ext": ">=0.10.64",
+ "undici": ">=5.28.4",
+ "ws": ">=8.18.0",
+ "axios": ">=1.7.7",
+ "micromatch": ">=4.0.8"
+ },
+ "packageManager": "yarn@1.22.21+sha1.1959a18351b811cdeedbd484a8f86c3cc3bbaf72"
}
diff --git a/patches/@sveltejs+kit+1.27.6.patch b/patches/@sveltejs+kit+2.7.5.patch
similarity index 90%
rename from patches/@sveltejs+kit+1.27.6.patch
rename to patches/@sveltejs+kit+2.7.5.patch
index a008413..e25e1e5 100644
--- a/patches/@sveltejs+kit+1.27.6.patch
+++ b/patches/@sveltejs+kit+2.7.5.patch
@@ -1,8 +1,8 @@
diff --git a/node_modules/@sveltejs/kit/src/exports/vite/index.js b/node_modules/@sveltejs/kit/src/exports/vite/index.js
-index a7a886d..d3433b5 100644
+index 40fa4c6..738cabf 100644
--- a/node_modules/@sveltejs/kit/src/exports/vite/index.js
+++ b/node_modules/@sveltejs/kit/src/exports/vite/index.js
-@@ -561,9 +561,9 @@ function kit({ svelte_config }) {
+@@ -655,9 +655,9 @@ async function kit({ svelte_config }) {
input,
output: {
format: 'esm',
diff --git a/patches/@sveltejs+kit+2.8.5.patch b/patches/@sveltejs+kit+2.8.5.patch
new file mode 100644
index 0000000..80f5dba
--- /dev/null
+++ b/patches/@sveltejs+kit+2.8.5.patch
@@ -0,0 +1,17 @@
+diff --git a/node_modules/@sveltejs/kit/src/exports/vite/index.js b/node_modules/@sveltejs/kit/src/exports/vite/index.js
+index e6521e9..f31c28b 100644
+--- a/node_modules/@sveltejs/kit/src/exports/vite/index.js
++++ b/node_modules/@sveltejs/kit/src/exports/vite/index.js
+@@ -639,9 +639,9 @@ async function kit({ svelte_config }) {
+ input,
+ output: {
+ format: 'esm',
+- entryFileNames: ssr ? '[name].js' : `${prefix}/[name].[hash].${ext}`,
+- chunkFileNames: ssr ? 'chunks/[name].js' : `${prefix}/chunks/[name].[hash].${ext}`,
+- assetFileNames: `${prefix}/assets/[name].[hash][extname]`,
++ entryFileNames: ssr ? '[name].js' : `${prefix}/[hash].${ext}`,
++ chunkFileNames: ssr ? 'chunks/[name].js' : `${prefix}/chunks/[hash].${ext}`,
++ assetFileNames: `${prefix}/assets/[hash][extname]`,
+ hoistTransitiveImports: false,
+ sourcemapIgnoreList
+ },
diff --git a/playwright.config.ts b/playwright.config.ts
new file mode 100644
index 0000000..d60d61c
--- /dev/null
+++ b/playwright.config.ts
@@ -0,0 +1,17 @@
+import type { PlaywrightTestConfig } from '@playwright/test';
+
+const config: PlaywrightTestConfig = {
+ use: {
+ locale: 'en-GB',
+ timezoneId: 'Europe/Amsterdam'
+ },
+ webServer: {
+ command: 'npm run build && npm run preview',
+ port: 4173
+ },
+ reporter: process.env.CI ? 'github' : 'list',
+ testDir: 'tests',
+ testMatch: /(.+\.)?(test|spec)\.[jt]s/
+};
+
+export default config;
diff --git a/src/components/ColorSchemeSwitcher.svelte b/src/components/ColorSchemeSwitcher.svelte
new file mode 100644
index 0000000..9d7e32a
--- /dev/null
+++ b/src/components/ColorSchemeSwitcher.svelte
@@ -0,0 +1,53 @@
+
+
+
+
+ {theme === 'auto' ? '🌗' : theme === 'dark' ? '🌙' : '☀️'}
+
+
+ setTheme('light')}
+ >☀️ Light
+ setTheme('dark')}>🌙 Dark
+ setTheme('auto')}>🌗 Auto
+
+
diff --git a/src/components/ToggleHeader.svelte b/src/components/ToggleHeader.svelte
new file mode 100644
index 0000000..acc8d3b
--- /dev/null
+++ b/src/components/ToggleHeader.svelte
@@ -0,0 +1,28 @@
+
+
+