From ac40ec106df733598d6d17cee3de71d93b5eebf2 Mon Sep 17 00:00:00 2001 From: Jonas Pfalzgraf Date: Fri, 17 Jan 2025 14:00:37 +0100 Subject: [PATCH] Initial commit --- .editorconfig | 35 ++ .github/workflows/codeql-analysis.yml | 71 +++ .gitignore | 694 ++++++++++++++++++++++++++ LICENSE | 21 + README.md | 38 ++ app.config.json | 29 ++ package.json | 46 ++ public/favicon.ico | Bin 0 -> 4286 bytes public/icons/icon.png | Bin 0 -> 2087 bytes public/icons/icon128.png | Bin 0 -> 3560 bytes public/icons/icon16.png | Bin 0 -> 2087 bytes public/icons/icon48.png | Bin 0 -> 2512 bytes public/manifest.json | 26 + public/options.html | 25 + public/popup.html | 25 + src/app.ts | 31 ++ src/assets/logo.afdesign | Bin 0 -> 24737 bytes src/assets/logo.png | Bin 0 -> 4540 bytes src/background.ts | 9 + src/classes/session.ts | 55 ++ src/components/button.ts | 63 +++ src/sass/_content.sass | 6 + src/sass/_mixin.sass | 93 ++++ src/sass/_root.sass | 26 + src/sass/app.sass | 49 ++ src/settings.ts | 33 ++ src/types/buttonType.ts | 1 + tooling.tsconfig.json | 31 ++ tools/parse.ts | 72 +++ tools/syncConfig.ts | 23 + tools/v2.ts | 46 ++ tsconfig.json | 75 +++ vite.config.ts | 28 ++ 33 files changed, 1651 insertions(+) create mode 100644 .editorconfig create mode 100644 .github/workflows/codeql-analysis.yml create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README.md create mode 100644 app.config.json create mode 100644 package.json create mode 100644 public/favicon.ico create mode 100644 public/icons/icon.png create mode 100644 public/icons/icon128.png create mode 100644 public/icons/icon16.png create mode 100644 public/icons/icon48.png create mode 100644 public/manifest.json create mode 100644 public/options.html create mode 100644 public/popup.html create mode 100644 src/app.ts create mode 100644 src/assets/logo.afdesign create mode 100644 src/assets/logo.png create mode 100644 src/background.ts create mode 100644 src/classes/session.ts create mode 100644 src/components/button.ts create mode 100644 src/sass/_content.sass create mode 100644 src/sass/_mixin.sass create mode 100644 src/sass/_root.sass create mode 100644 src/sass/app.sass create mode 100644 src/settings.ts create mode 100644 src/types/buttonType.ts create mode 100644 tooling.tsconfig.json create mode 100644 tools/parse.ts create mode 100644 tools/syncConfig.ts create mode 100644 tools/v2.ts create mode 100644 tsconfig.json create mode 100644 vite.config.ts diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..5c6f66e --- /dev/null +++ b/.editorconfig @@ -0,0 +1,35 @@ +root = true + +[*] +indent_style = tab +indent_size = 4 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true + +charset = utf-8 + +trim_trailing_whitespace = true +insert_final_newline = true + +[*.{js,ts,svg,md}] +charset = utf-8 + +[*.{ts,js}] +indent_style = tab +indent_size = 4 + +[package.json] +indent_style = space +indent_size = 2 + +[*.{sass,scss}] +indent_style = space +indent_size = 2 + +[*.{css,less}] +indent_style = space +indent_size = 2 + +[*.{json,yml,yaml}] +indent_style = space diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml new file mode 100644 index 0000000..bfcc659 --- /dev/null +++ b/.github/workflows/codeql-analysis.yml @@ -0,0 +1,71 @@ +# For most projects, this workflow file will not need changing; you simply need +# to commit it to your repository. +# +# You may wish to alter this file to override the set of languages analyzed, +# or to provide custom queries or build logic. +# +# ******** NOTE ******** +# We have attempted to detect the languages in your repository. Please check +# the `language` matrix defined below to confirm you have the correct set of +# supported CodeQL languages. +# +name: "CodeQL" + +on: + push: + branches: ["main"] + pull_request: + # The branches below must be a subset of the branches above + branches: ["main"] + schedule: + - cron: "37 13 * * 4" + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + language: ["javascript"] + # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] + # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v2 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + + # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs + # queries: security-extended,security-and-quality + + # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + - name: Autobuild + uses: github/codeql-action/autobuild@v2 + + # â„šī¸ Command-line programs to run using the OS shell. + # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun + + # If the Autobuild fails above, remove it and uncomment the following three lines. + # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. + + # - run: | + # echo "Run, Build Application using script" + # ./location_of_script_within_repo/buildscript.sh + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v2 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7a21a8d --- /dev/null +++ b/.gitignore @@ -0,0 +1,694 @@ +# Created by https://www.toptal.com/developers/gitignore/api/visualstudio,visualstudiocode,webstorm,phpstorm,intellij +# Edit at https://www.toptal.com/developers/gitignore?templates=visualstudio,visualstudiocode,webstorm,phpstorm,intellij + +### Intellij ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# AWS User-specific +.idea/**/aws.xml + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# SonarLint plugin +.idea/sonarlint/ + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +### Intellij Patch ### +# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 + +# *.iml +# modules.xml +# .idea/misc.xml +# *.ipr + +# Sonarlint plugin +# https://plugins.jetbrains.com/plugin/7973-sonarlint +.idea/**/sonarlint/ + +# SonarQube Plugin +# https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin +.idea/**/sonarIssues.xml + +# Markdown Navigator plugin +# https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced +.idea/**/markdown-navigator.xml +.idea/**/markdown-navigator-enh.xml +.idea/**/markdown-navigator/ + +# Cache file creation bug +# See https://youtrack.jetbrains.com/issue/JBR-2257 +.idea/$CACHE_FILE$ + +# CodeStream plugin +# https://plugins.jetbrains.com/plugin/12206-codestream +.idea/codestream.xml + +# Azure Toolkit for IntelliJ plugin +# https://plugins.jetbrains.com/plugin/8053-azure-toolkit-for-intellij +.idea/**/azureSettings.xml + +### PhpStorm ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff + +# AWS User-specific + +# Generated files + +# Sensitive or high-churn files + +# Gradle + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake + +# Mongo Explorer plugin + +# File-based project format + +# IntelliJ + +# mpeltonen/sbt-idea plugin + +# JIRA plugin + +# Cursive Clojure plugin + +# SonarLint plugin + +# Crashlytics plugin (for Android Studio and IntelliJ) + +# Editor-based Rest Client + +# Android studio 3.1+ serialized cache file + +### PhpStorm Patch ### +# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 + +# *.iml +# modules.xml +# .idea/misc.xml +# *.ipr + +# Sonarlint plugin +# https://plugins.jetbrains.com/plugin/7973-sonarlint + +# SonarQube Plugin +# https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin + +# Markdown Navigator plugin +# https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced + +# Cache file creation bug +# See https://youtrack.jetbrains.com/issue/JBR-2257 + +# CodeStream plugin +# https://plugins.jetbrains.com/plugin/12206-codestream + +# Azure Toolkit for IntelliJ plugin +# https://plugins.jetbrains.com/plugin/8053-azure-toolkit-for-intellij + +### VisualStudioCode ### +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +!.vscode/*.code-snippets + +# Local History for Visual Studio Code +.history/ + +# Built Visual Studio Code Extensions +*.vsix + +### VisualStudioCode Patch ### +# Ignore all local history of files +.history +.ionide + +# Support for Project snippet scope +.vscode/*.code-snippets + +# Ignore code-workspaces +*.code-workspace + +### WebStorm ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff + +# AWS User-specific + +# Generated files + +# Sensitive or high-churn files + +# Gradle + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake + +# Mongo Explorer plugin + +# File-based project format + +# IntelliJ + +# mpeltonen/sbt-idea plugin + +# JIRA plugin + +# Cursive Clojure plugin + +# SonarLint plugin + +# Crashlytics plugin (for Android Studio and IntelliJ) + +# Editor-based Rest Client + +# Android studio 3.1+ serialized cache file + +### WebStorm Patch ### +# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 + +# *.iml +# modules.xml +# .idea/misc.xml +# *.ipr + +# Sonarlint plugin +# https://plugins.jetbrains.com/plugin/7973-sonarlint + +# SonarQube Plugin +# https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin + +# Markdown Navigator plugin +# https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced + +# Cache file creation bug +# See https://youtrack.jetbrains.com/issue/JBR-2257 + +# CodeStream plugin +# https://plugins.jetbrains.com/plugin/12206-codestream + +# Azure Toolkit for IntelliJ plugin +# https://plugins.jetbrains.com/plugin/8053-azure-toolkit-for-intellij + +### VisualStudio ### +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Ww][Ii][Nn]32/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# ASP.NET Scaffolding +ScaffoldingReadMe.txt + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.tlog +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Coverlet is a free, cross platform Code Coverage Tool +coverage*.json +coverage*.xml +coverage*.info + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio 6 auto-generated project file (contains which files were open etc.) +*.vbp + +# Visual Studio 6 workspace and project file (working project files containing files to include in project) +*.dsw +*.dsp + +# Visual Studio 6 technical files + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# Visual Studio History (VSHistory) files +.vshistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# Fody - auto-generated XML schema +FodyWeavers.xsd + +# VS Code files for those working on multiple tools + +# Local History for Visual Studio Code + +# Windows Installer files from build outputs +*.cab +*.msi +*.msix +*.msm +*.msp + +# JetBrains Rider +*.sln.iml + +### VisualStudio Patch ### +# Additional files built by Visual Studio + +# End of https://www.toptal.com/developers/gitignore/api/visualstudio,visualstudiocode,webstorm,phpstorm,intellij + +dist + +tools/syncConfig.js +tools/parse.js +tools/v2.js +tools/clean.js +package-lock.json diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..79f09ef --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 Jonas Pfalzgraf + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..d9c9633 --- /dev/null +++ b/README.md @@ -0,0 +1,38 @@ +# BrowserExtensionTemplate + +[![GitHub issues](https://img.shields.io/github/issues/JosunLP/BrowserExtensionTemplate?style=for-the-badge)](https://github.com/JosunLP/BrowserExtensionTemplate/issues) +[![GitHub forks](https://img.shields.io/github/forks/JosunLP/BrowserExtensionTemplate?style=for-the-badge)](https://github.com/JosunLP/BrowserExtensionTemplate/network) +[![GitHub stars](https://img.shields.io/github/stars/JosunLP/BrowserExtensionTemplate?style=for-the-badge)](https://github.com/JosunLP/BrowserExtensionTemplate/stargazers) +[![GitHub license](https://img.shields.io/github/license/JosunLP/BrowserExtensionTemplate?style=for-the-badge)](https://github.com/JosunLP/BrowserExtensionTemplate) +[![Twitter URL](https://img.shields.io/twitter/url?logo=twitter&style=for-the-badge&url=https%3A%2F%2Fgithub.com%2FJosunLP%2FBrowserExtensionTemplate)](https://twitter.com/intent/tweet?text=Look+what+i+found+on+GitHub+%23Developer%2C+%23SoftwareDeveloper%3A&url=https%3A%2F%2Fgithub.com%2FJosunLP%2FBrowserExtensionTemplate) +[![CodeFactor](https://www.codefactor.io/repository/github/josunlp/browserextensiontemplate/badge?style=for-the-badge)](https://www.codefactor.io/repository/github/josunlp/browserextensiontemplate) +[![Known Vulnerabilities](https://snyk.io/test/github/JosunLP/BrowserExtensionTemplate/badge.svg?style=for-the-badge)](https://snyk.io/test/github/JosunLP/BrowserExtensionTemplate) + +## Description + +A basic template based on SASS and TypeScript to create browser extensions without directly relying on a larger framework. + +## Installation + +You can download the source code from [GitHub](https://github.com/JosunLP/BrowserExtensionTemplate). Just copy it in your project and run `npm install` to install the dependencies. +The basic configuration, wich will sync with `npm run sync` with the `package.json` file and the `manifest.json` file, is in `app.config.json`. +Alternatively, you can fork the project and run `npm install` in the forked project. + +## Usage + +Your sourcecode can be written in the `src` folder. The `public` folder contains static files like images, html and the manifest.json. +With the `npm run deploy-v3` command you can deploy the extension to the dist folder, ready to be published to the chrome web store. +With the `npm run deploy-v2` command you can deploy the extension to the dist folder, ready to be published to the firefox web store. +This is necessary because the firefox web store needs the `manifest.json` file to be present in the version v2. + +## License + +This project is licensed under the [MIT license](https://opensource.org/licenses/MIT). + +## Contributing + +This project is open source. Feel free to fork and contribute! + +## Author + +Jonas Pfalzgraf diff --git a/app.config.json b/app.config.json new file mode 100644 index 0000000..e5ceac0 --- /dev/null +++ b/app.config.json @@ -0,0 +1,29 @@ +{ + "AppData": { + "id": "browser_extension_template", + "name": "Browser Extension Template", + "version": "0.0.1", + "description": "A basic template based on SASS and TypeScript to create browser extensions without directly relying on a larger framework.", + "repository": { + "type": "git", + "url": "git+ssh://git@github.com:JosunLP/BrowserExtensionTemplate.git" + }, + "license": "MIT", + "homepage": "https://github.com/JosunLP/BrowserExtensionTemplate", + "bugs": { + "url": "https://github.com/JosunLP/BrowserExtensionTemplate/issues" + }, + "authors": [ + { + "name": "Jonas Pfalzgraf", + "email": "info@josunlp.de" + } + ] + }, + "htmlTemplatePairs": [ + { + "key": "{{BET}}", + "value": "Browser Extension Template" + } + ] +} \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..933d358 --- /dev/null +++ b/package.json @@ -0,0 +1,46 @@ +{ + "name": "browser_extension_template", + "version": "0.0.1", + "private": true, + "scripts": { + "deploy-v3": "npx rimraf ./dist/ && npm run build-tooling && npm run sync && npm run build && node ./tools/parse.js", + "deploy-v2": "npm run deploy-v3 && node ./tools/v2.js", + "build": "vite build", + "build-tooling": "tsc --project ./tooling.tsconfig.json", + "watch": "vite build --watch", + "sync": "npm run build-tooling && node ./tools/syncConfig.js" + }, + "devDependencies": { + "@types/chrome": "^0.0.268", + "@types/node": "^20.13.0", + "@webcomponents/webcomponentsjs": "^2.8.0", + "sass": "^1.77.4", + "vite": "^5.2.12", + "vite-tsconfig-paths": "^4.3.2" + }, + "browserslist": [ + "> 1%", + "last 2 versions", + "not dead" + ], + "authors": [ + { + "name": "Jonas Pfalzgraf", + "email": "info@josunlp.de" + } + ], + "description": "A basic template based on SASS and TypeScript to create browser extensions without directly relying on a larger framework.", + "homepage": "https://github.com/JosunLP/BrowserExtensionTemplate", + "license": "MIT", + "repository": { + "type": "git", + "url": "git+ssh://git@github.com:JosunLP/BrowserExtensionTemplate.git" + }, + "bugs": { + "url": "https://github.com/JosunLP/BrowserExtensionTemplate/issues" + }, + "dependencies": { + "@webcomponents/custom-elements": "^1.6.0", + "bootstrap": "^5.3.3" + } +} diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..085cbce2aab8237ec8eab6b085194b3aa5ca167a GIT binary patch literal 4286 zcmdUy&r4KM6vywRp<>Vv!HvpHQsgFRA-J5R{(w*jbZ1dJ5h7O+J}3&^v`kRj8P&o~ zD|Mmm+}N%Xs&y$O3StS<+&*XKT{u0S<@4Qn;u$`3?mhS3@Atm@9`hJua(oH}Bj4uL z8Dm=KemZ}^H1FFu)XS0OJ)`}0 z=nJ~`mi`5(MZ82*d-a4S?c|SY>|I#*oraW$>|+*g!9Dl}Zf|AUdGH+_2s)4dWtm%x zSVQz*-g50vYGE8|-X^>Mnl8^!n}89x1jjP0#IK^YrTpKNPh0EY>^b}@ff=)pSQ9Y5 gEHqRonL4W}pUT^^5sH;;>(MvN7XsQr5fN+u0Gl&OumAu6 literal 0 HcmV?d00001 diff --git a/public/icons/icon.png b/public/icons/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..e6836fcd4efc53acad8a567422f070bdeb51fa6b GIT binary patch literal 2087 zcmah~c~BE)6c1CW5K19~wu-3fVk<2sn}l*?%?dF=2oS^&F16K7vPo7p*_ho(Ku0Y@ zMYIlzXe|Xg9@9=WXs}2fqaatS;xJmLD#eaxwX|5Bkx@Ka`XxaQ(e{r`_IFlVA_0~;c9RLVMd{Qu4af!=ujvpS}9OkWVn$?F0$g8Md@m7QNC8JgW~70C3XZ5 zm~aN;+D)XHM(il$;YGl^`xu6}9u+1Zg>sb{Tp4A>xzYS+z5trX=1QzOJ))8?7%~PD z3K<#3g1~S=K>@!YiceV$uuv=(!vYa367hfrk1jMbn4M>)!`&2K4mnP1t%QXkC^Og1 ziD@VsgF+C%xkKa(p&wy4(?iz>iGbY*EaVH|(ds(wa3~g=mGlIn)5183n{YEj1AXBb zecVpGg5Hgcqqi6-hN6v>W!TG@kcQ!Lh9KQ9H?d=umnkci|E+E^jkyNRB(4I98!Ez> zn4UYJ)rA%uR^c>dvug3gRUjSVp3J%Ph$u{$0Z+rt2F8d&!k8C;976ZvxtoZzxht1t zCV*?vh)M1UhC;C-SU3XZy%ch%8p$B+IGHOaK%Hp7Ivm9anD+`$Mv;^?6N~{4j6A-E z>wtJLC8YyU)C-V8Cd;r=dV&NPtxB5DRV2z{gyNVOo`~Fb)w$M+!uUK;U8aXoK0&VGQz`kFMjw0YJhX*j3B>GazZv!IMq9P^fcQv$ETuc_+R zS=p(ZM_t^OY-eD^#+WAd-vJGi9lp{<6Y3}BCr%5zQ_X=itGa8bb#tT6b`_&liw+mn z1RWO30?H?%^n-xf4YR9{;ty|>zkdvwK6SH-K7Lj6U7zi%9S0h(Ht!|GyCav=?DB~H zdcTE!3+qDncPwc&r67%S)}Z;7Aaq_fZG$^8rP>=o@#n{FCTr8*#2 z3TMk?)jr|d>#}oc_O65NpGB=^qD& z1zBnj{BY+H_PerQmG$Q@uRTJlKUN6j&iSadi{HPod3A3}oulJjU5D|OR`%Cl5I(6_fp8(#R z_L);QpFSDa<;V#tDOtUpw$j@lm(9?JuWiX2&^f{nU7nrYQ}JVH*X_1L*ZgkoQe@q` zfmWujTc7?xi)K=wb3<%L$k!_W#^P&YM|GR{;g{?`qU^0R{t}cWElpa}Bj0+2u{x&Q z#)5BM&XDxxF!whFT`8?;d^gr6D1GOkGt<>sUpKHjn|#ugoaIXET^<_3DOg!NaZW_` z6MbdT^wizQdv=~{IFnVGk3QSHV)EPPrT5}HrtUmHb4S0gGZyV?i(TL2{=O>`)8z-} HFE9QV$$aAI literal 0 HcmV?d00001 diff --git a/public/icons/icon128.png b/public/icons/icon128.png new file mode 100644 index 0000000000000000000000000000000000000000..6eb06a0ebb370dc38829cff58852b13f473b402e GIT binary patch literal 3560 zcmai13p|ti8-ESuey1{Jn?ht4qPEO!$dbz>q>|aqGTUrpth6q=T^kJ33uX=luVl&--rg^M0S_`#itrw$CyG{WoiCm}&q3 zpiT7g33sWlBt|18d4O?1#tjyb`^2Slo*-- z7DZz)*?7bs^-=_kNyQ^V9sSULTmmhc>665xZAf zmc{1dM0mskFAjWGyhb5l3o3#bJR;055JuqeXfQ{lBNB~p(SSMgsB~PAr}ukfP{Jdk z1p+P(g%S#dNTCCg!(*T@SS%KWwny39+kqN({6w~ZEV5(sEff@s9G)~jg~#Lym>f1t z!AXwd#0&5U1c<}llM9&ikL+yz`{RR&Kq(Lu28l*}s!pYR2!$KZV=V+irJ!gmS}ct% z;Dh>@&-7^`=D*M@kT2-D(HsGXAI;%@@bXzmAK)(x!F2z#iHOYoCuKjs|E(Sy``I!0 z0+%nRD!{7}gP^8eFU zOaaW9O2G-30v2sSTAT`ZmS9m@6j@BgDBxIRHUp0k*->e9ay(0baA9#69A}hbY*@_q z)(g^K=FX_kxShe+{QPh}Oum4_OZ>#$Hd@^0?iQvG23zPU9GRl1Fdo6>aj5YW8g=2A zVBm`=pF8(8vO_?-#dqLt0+$@>!wW|Pr4%OE17Fl00IYN4 zbL)MT-SyI?s}H$b=-1ZNsGV~1&d^VFGEr}Ev&&O!IR4}wp=G459`{n-yD>F zHL6)_8m*pqeQN>ubEeMq*}(?@O+&@41RO734ggDPh@S4jqT&}NyAKw688wURW3E%p z!_JhgsVqy*u{H=noIcl;rKb|^uAiuHBnTy`?DlrwEyxW_Sf-+C91nk3NZn&fWU5() z?e&U0bCpi-yS_V9^Z85z=Z}fvV_1jwVsovC%87f&g!Ap#iOG8s^71tBBAMYIEvX&S z7<6fGH&gXj5<{BXXE&*gE!9avw(7mMuKqddw5tS_5}HyknIGT29{0*6P8E&OR=S{k z*dw%SV-a!VvZ}R`i|ZjQ0I42M8Ch3YHZX2@KO!=rWlK!Q`B*p2kQnGMsfjWx8SPP8 zfo$0`>8lr=wwdL5FgBZ0JZF@ zk_$#D_$&$HlYxEKJjL!D3Urk$$* zu~7ZBX{;!7204w2+;Z#n)d%Zhbh!~XY)ngfw6B=Q!nwBpP_Gd<>t*~y%sH_ zZM`*#aZ@d~wj=fFxP!Z)jdm2& zBlVK9g|{V0PWyRk@~|M z56j$oe>XLJn7w}_Kp;QC{R+sE-6KeU?mKPK5@MBw6fK`ids&c{QvY@L<2W?qwq*rNErAQWB$`U`=dSa5 zb^GDgx~9&klW@t-+!qz+5R$dB6D`njA$gwK5jL7vCpS+?UOE3b{axm}ErSVz@W`~2 zKVfu4BJaE>mJ%ggvwT$}i#H7wvPs+1qNO`d`_Ri=Z!UAdKYPm1+&t#u#+-PbSH&hB zw3Fl|biagujX&@orN-RZKHWIg8lcWOD)G@#z%sD2&sB@n;dBZbrE#)+N~$e1ihbQ= zAfxtcMXsd|&BIH9Ld2e&I~f%NwiWumz?xg~iq`ImCIdI(D`^|=is{83NZ}CU+x@ul z%V!ziAfSKhzEVMtjbg9CVrDJsZph(x&M2!*Cr??A`f4dT#4Yt)af~OKvE~1ypJ91if5=6>Yx^@H=BlDrNvj1wsSI#<{cL z!-&|zq$ud|4&`y%Nmt7iDy46tTSeY+>ARpxJ0z9)Z+-UVPgwk>2aA`Ri!L?>5_mU! z&u;U~)H<5EY4UP4;MBAM8xT~|lCz&!yIg3VLsxKuDb_pUlweoegeZkDbk;MXb%EY}=4VU`wzmo&|pLlUO# zJ)wqf@k`<&WGw}U6fqSH{epNNkASyoi($8 z{~s7%9y5G`HATfwLZ!J+Aw?#T)vv_Rm%|y}q&y%=awK)uMDD=AT$&Q|_vPQ*mbWyg zP7WWm-IG@CNrdER$*<;gB+Is2djWHG#B^_W-pw#}@5|TzuAeY>+kDgQ(-#Z$AggamIdKdVr$Pgj$XC+HYHz1m-|034 usyr#I7BwR|t1^9Ax}poMbyIP*4Y@XDvFlVA_0~;c9RLVMd|*u4af!=ujvpS}9OkWVn$?F0$g8Md@m7QNC8JgW?ykC3XZ5 zm~aN;+D)XHM(il$;YGl^`xu6}9u+1Zg>sb{Tp4A>xzYS+z5rUl=1QzOJ))8?8ZrhF z3K<#3g1~S=K>@!YiceV$uuv=(!vYa367hfrk1jMbn4M>)!`&2K4mnP1t%QXkC^Og1 ziD@VsgF+C%xkKa(p&wy4(?iz>iGbY*EaVH|(ds(wa3~g=mGlIn)5183n{YEj1AXBb zecVpGhTe^gqqi6-hN6v>W!TG@kcQ!Lh9KRqHnC%tS1Bu%|E+E^jkyNRB(4F88!Ez> zm|i%b)rA%uR^c>dvug3gH6R_~p3J%Ph$u{$0Z+rt2F8d&!kCwU976ZwtJLC8YyU)Ju>;Cd;r=dV&NPtx8(RRV2z{gyNVOo`~AQp*=ixh|ufxyG;(FU`l!x-%U^xYG{mFTnxLog)nk-QTjB^j^e6-mN9 z1_+6n4Jc&i>2N(}BN=D`Nf{^!>>e4C7_#>**&1)4J=6LKZ8fvuSai=b9jJFG^E-rU0|zQUhou6q4%o1n7i9E*NUUV8*Rn zFlz#S^jM<;BMt~(pGbK0ZC*qtfyc$~z5J;C0qneIU@w6M2M=#JAPnzF+zb|@6>Pwr z&!=8uv3x!zKo5+oJ&P5w!?wefIzbv-+B|2AG@R4mbo%X!SyaX;j(OX^DS=nv*Hm@; zyzI=a<1TJXwlgqdb4(Na?|_CW4qxe#iS?876K4e8t>!?QHQhDThWSzFyNc1OB}a>D zf{u!10p*iW`e8urrn%L}@kcky-#dZKoW4~>pS-5|w$JwEu0xI2n)egpy^$+vc6r1> zz29QL#dV!7}$4kX-|ky@TJ(0x<=Nj&fJz8k?;mX)C2tOy(INLkv+ z?(FP6r)QsAj9yM%UUrp~lV3aDVp=~9X>Xgezr;^)+S%vx^uZ-~?yB}@O}7kZQXP;h zg|lsn>VWXUmZjVK{Oc7LLqt)N>;4(Mcc?b3Kj)~cIAhRQb>!CzPXFI3i1lH6^iP7r zf-JR%zQ6kz`(4?u%KGz{36GKLkJQHoVyaCE&rXG2(p;;M?M6Q@`Alo9ExjCRT<+-a zyIZ?|{^rAHb3SbC;`eWEUfUbc5Nh-rUH|>wdTPD6;O~ zL@QG_Y)pT@MKd|jxhb|Iu zV8OSqWJr2*mHYYQ>ANq^-qr8xj77WJVmJ1fsWu9gqOw4#uDgD~;(}=FvG6HvSu0-bBp^uD?jMun{l4#g-}}95 zQjwe(ZBOzb5eNkP*ciSL|7MsTTWkC?&2nxJfnX(oMd?_&AOVyhDw>(aPyT5fKq|29wTYQgIC`YEWZhJynhRnkW`H_z)`5!Ws-l z)D#n^I2+MnYyiM<$|5-i%a+;I=;HP9M9@tLJ&4AjKdUa4EQO-cX_e+cq!K!$gjA3k zLvj6}75b1KehIw^d5&JAKrjSVAetpFD?(a=pEJbM{bCcnSo0!ff#CPmRjL)&K(VM? zJaLOfSP|1x2dK!Pf#^aAMRZyT6qSpo!`GZSQy#%sl~@iXKx#RrU;`oHzX8%<>2J?X zL`t2ha$D3e?wYx5lD>?wf%Qy!&@zHwNC0V2hrU~?GDP%Mv^tVLw7630+sbR;Dp^Bn0D}oK7-nX(Hau%r?YgE1v! zmKG+0tHc(h1yKo`#sE}`)p9nVr%EB2Sf|7QjuMe0EV^lAl<=apS$b;DqOag);jsw> zU<{06h}Q6oyHx1SmF~=wN1>Se2^33A^<@JZEh5!PAgTG9c;E{tipa1$u@>UW@m693 zT$v2UJHbG~hZ>fvAuR=;H3ltorBR*&!UZcn>^Rdji4#9o;*|cp}$nPtU8BTwmwT_IBID zVVf(QOT*mkx;Ihx+IH999OCugIBBGu-P&&NUmn&?dQ9$gZMEX2tm;^u6Xj`ttHBw_ z&K-RZDcTS`G+fHAPdSnQp5uuK9=XyCAe_q4zf%>h{`sn-7!nQfA7(s7SZ&TZ>e#erRc1;Et5j%>Q_8DE=re{@n*wvl?} z@O7t=ZM)eY|JiH)TwLZLX>jn)fQ&Sd#J&>0y<$wsSANv$fb~|p^nLwm%Whl7SM5_4 z4`wdY{r=MTu=|F*KfV<(#yQux3XcTMRA#0^yvvuP)crOES( z`cJQ2Y`6PqcK+6{;@g5*;g=1o^KdeEu>ERiV%h}_5 zc?Z~scK=PHXxwupK(W1bcIsAR%ZBo{zo)&?J4~A`@7Z~W-07jPGklq_AAgnEX<{-^ z0)gyedMpU_4QmJl%Z6A!SEMMJK4i=haaZr@sdIHs`N_Tdh!bDp>r-=>TXnQ-YqkU= zPj|XJBKIZsMV2~cUvKGfy6oRpQhe3=Y-GWOzG8ombxo@TfwHo9Ps!D}!$V_LdmXLA z_My3j!RQYX31Qe35E_YTB|*^6OLwe)U=7kIgs$1^hOdv8^n5R} z%11*k=1mkP_~^>M)iy;R^(MYm|9+aKu{iN;o8Xd67yTT4gKZG+E4|i~wcci`mfG*PyNkFsa@W@n zjMhrD;DWgJUS_Y{lYRT>kA2nQ2M6D9Y#$bX=it?G_ru}~uYms?;}-4m{}T}^|CBiX z2i=6;eeKA;m`8!_9De$-oF>oD*m13vTLZrHw&DcrDhYr1v!E&IXitfu&Naw#tg?wK zu)iO}3E`ytqxpkVHL}r)Q{JOPgvaFjJ1iQlyS#@oX36?gJV#?Zzb6@i7TsT53LGrn z6*q2UF+J=nwVyh7x>mI#B;NT8;?}W<#-y#rTHmQ(b-#ReRoN z^XQ~6-&nqrSWu<6KmFe~FIH;GlKk)8@OMXITsRjB$`q+poIkb)9~4Orz!N939ws;Y zy{_sv9BBspyoG^%fo&{d=e}^A&Gw37#oCx}R?W>EDj(f6;67rZbi3Q~*TMmZlf55x zp5g2__Ggq26g9(T;A_6BJjw$X<`&!O*NXJq(rbQOY@W + + + + + + + {{BET}} Options + + + +
+ +

Settings

+
+
+
+
+ +
+ + diff --git a/public/popup.html b/public/popup.html new file mode 100644 index 0000000..28cdf54 --- /dev/null +++ b/public/popup.html @@ -0,0 +1,25 @@ + + + + + + + + {{BET}} + + + +
+ +

{{BET}}

+
+
+
+
+ +
+ + diff --git a/src/app.ts b/src/app.ts new file mode 100644 index 0000000..b1d8653 --- /dev/null +++ b/src/app.ts @@ -0,0 +1,31 @@ +import { Session } from "./classes/session" +import "./sass/app.sass" + +class App { + + private static contentEntry: string = "content" + + constructor() { + this.drawData() + this.main() + } + + async main(): Promise { + console.log("Hello World") + } + + async drawData(): Promise { + const session = Session.getInstance() + const contentRoot = document.getElementById(App.contentEntry) + const body = document.createElement("div") + const title = document.createElement("h1") + const text = document.createElement("p") + title.innerText = "Hello World" + text.innerText = session.contentTest + body.appendChild(title) + body.appendChild(text) + contentRoot.appendChild(body) + } +} + +new App(); diff --git a/src/assets/logo.afdesign b/src/assets/logo.afdesign new file mode 100644 index 0000000000000000000000000000000000000000..704c43b45d7fdf0faf95360747670a7ef360da08 GIT binary patch literal 24737 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 literal 0 HcmV?d00001 diff --git a/src/background.ts b/src/background.ts new file mode 100644 index 0000000..4941b1c --- /dev/null +++ b/src/background.ts @@ -0,0 +1,9 @@ +class Background { + constructor() { + this.main(); + } + + async main(): Promise {} +} + +new Background(); diff --git a/src/classes/session.ts b/src/classes/session.ts new file mode 100644 index 0000000..3443233 --- /dev/null +++ b/src/classes/session.ts @@ -0,0 +1,55 @@ +export class Session { + + private static instance: Session; + + private constructor() { + } + + static getInstance() { + if (!Session.instance && !Session.load()) { + Session.instance = new Session(); + } + if (!Session.instance && Session.load()) { + Session.instance = Session.load(); + } + Session.save(); + return Session.instance; + } + + public static save() { + localStorage.setItem('session', JSON.stringify(this.instance)); + } + + public static load(): Session | null { + const session = localStorage.getItem('session'); + if (session) { + const obj = JSON.parse(session); + const result = new Session(); + result.contentTest = obj.contentTest; + return result; + } + return null; + } + + public static reloadSession() { + const session = localStorage.getItem('session'); + if (session) { + const obj = JSON.parse(session); + const result = new Session(); + result.contentTest = obj.contentTest; + Session.instance = result; + } + } + + public static resetSession() { + localStorage.removeItem('session'); + sessionStorage.removeItem('session'); + this.instance = new Session(); + Session.save(); + location.reload(); + } + + public readonly sessionId: string = crypto.randomUUID(); + + public contentTest: string = 'This is a simple example of a web application'; +} \ No newline at end of file diff --git a/src/components/button.ts b/src/components/button.ts new file mode 100644 index 0000000..33f8a28 --- /dev/null +++ b/src/components/button.ts @@ -0,0 +1,63 @@ +import { customButton } from "../types/buttonType"; + +export class BasicButton { + + private type: customButton; + + private text: string; + + private id: string | undefined; + + private className: string | undefined; + + constructor(type: customButton, text: string, id?: string, className?: string) { + this.type = type; + this.text = text; + this.id = id; + this.className = className; + } + + public render(): string { + const result = document.createElement('button'); + result.type = "button"; + result.className = this.type; + result.textContent = this.text; + + switch (this.type) { + case "primary": + result.className = "btn btn-primary"; + break; + case "success": + result.className = "btn btn-success"; + break; + case "danger": + result.className = "btn btn-danger"; + break; + case "warning": + result.className = "btn btn-warning"; + break; + case "info": + result.className = "btn btn-info"; + break; + case "light": + result.className = "btn btn-light"; + break; + case "dark": + result.className = "btn btn-dark"; + break; + default: + result.className = "btn btn-primary"; + break; + } + + if (this.id) { + result.id = this.id; + } + if (this.className) { + result.className += ' ' + this.className; + } + + return result.outerHTML; + } + +} diff --git a/src/sass/_content.sass b/src/sass/_content.sass new file mode 100644 index 0000000..78b7aea --- /dev/null +++ b/src/sass/_content.sass @@ -0,0 +1,6 @@ +@import mixin + +#textbox + text-align: left + padding: 2rem + @include noselect \ No newline at end of file diff --git a/src/sass/_mixin.sass b/src/sass/_mixin.sass new file mode 100644 index 0000000..ddd1d41 --- /dev/null +++ b/src/sass/_mixin.sass @@ -0,0 +1,93 @@ +@import "root" + +@mixin respond-to($media) + @if $media == handhelds + @media only screen and (max-device-width: 40rem) + @content + + @else if $media == medium-screens + @media only screen and (min-device-width: 40rem) + @content + + @else if $media == wide-screens + @media only screen and (min-width: 1000px) + @content + +@mixin partialButton + width: 5rem !important + height: 2rem !important + text-align: center !important + margin: 0.5rem !important + border-color: $seccond-color !important + border-radius: 0.5rem !important + + @include respond-to(handhelds) + font-size: 3rem + + @include respond-to(medium-screens) + font-size: 1.5rem + +@mixin hoverMe + &:hover + button + color: grey !important + +@mixin shadow + box-shadow: 0px 0px 30px silver + +@mixin noselect + -webkit-touch-callout: none + -webkit-user-select: none + -khtml-user-select: none + -moz-user-select: none + -ms-user-select: none + user-select: none + pointer-events: none + +.form-group + margin-left: 2rem + margin-right: 2rem + margin-bottom: 0.5rem + flex-wrap: wrap + justify-content: center + display: flex + +@mixin formBasic + display: block + padding: 2rem + background: $background-color-content + border-radius: 0.7rem + min-height: 20rem + margin: auto + margin-top: 2rem + margin-bottom: 2rem + @include shadow + + @include respond-to(handhelds) + font-size: 2.5em + margin-left: -0.8em + margin-right: -0.8em + border-radius: 0 + + @include respond-to(medium-screens) + max-width: 40rem + + @include respond-to(wide-screens) + max-width: 40rem + + input + @include respond-to(handhelds) + font-size: 3rem + border-radius: 0.5rem + + .check + position: static + @include respond-to(handhelds) + width: 2rem !important + height: 2rem !important + + button + @include respond-to(handhelds) + font-size: 3rem + padding: 1rem + border-radius: 1rem diff --git a/src/sass/_root.sass b/src/sass/_root.sass new file mode 100644 index 0000000..3a50f8d --- /dev/null +++ b/src/sass/_root.sass @@ -0,0 +1,26 @@ +$main-font: 'Ubuntu', 'Staatliches' +$main-font-color: white +$main-font-color-hover: lightgrey +$main-font-color-focus: lightgrey +$main-font-color-disabled: lightgrey +$main-font-color-active: lightgrey +$main-uschrift-font: 'Ubuntu', Arial +$primary-color: #007bff +$primary-color-hover: #0069d9 +$primary-color-focus: #0062cc +$primary-color-disabled: #0069d9 +$primary-color-active: #0062cc +$background-color-content-shadow: #0069d9 +$seccond-color: #6c757d +$seccondary-color: darkgrey +$seccondary-color-hover: black +$seccondary-color-focus: black +$seccondary-color-disabled: black +$seccondary-color-active: black +$background-color: #77B2FF +$background-color-content: rgb(198, 223, 255) +$background-color-content-hover: rgb(198, 223, 255) +$background-color-content-focus: rgb(198, 223, 255) +$background-color-content-active: rgb(198, 223, 255) +$background-color-content-disabled: rgb(198, 223, 255) +$logo-image: url('../icons/icon128.png') diff --git a/src/sass/app.sass b/src/sass/app.sass new file mode 100644 index 0000000..f953809 --- /dev/null +++ b/src/sass/app.sass @@ -0,0 +1,49 @@ +@import 'root' +@import 'mixin' +@import 'content' +@import "../../node_modules/bootstrap/scss/bootstrap" + +body + height: 30rem + width: 30rem + background-color: $background-color + text-align: center + margin: auto + padding: 1rem + color: $main-font-color + + p + font-size: 1rem + font-weight: bold + margin: auto + padding: auto + color: $main-font-color + text-align: center + font-family: 'Roboto', sans-serif + +h1, h2 + @include noselect + +form + @include formBasic + +.logo + width: 5rem + height: auto + padding-top: 2rem + @include noselect + +svg + @include noselect + +table + color: $main-font-color !important + + th + @include noselect + +a + color: $main-font-color + + &:hover + color: $background-color-content diff --git a/src/settings.ts b/src/settings.ts new file mode 100644 index 0000000..10065b7 --- /dev/null +++ b/src/settings.ts @@ -0,0 +1,33 @@ +import { Session } from "./classes/session"; +import { BasicButton } from "./components/button"; +import "./sass/app.sass"; + +class Settings { + + private session = Session.getInstance(); + + constructor() { + this.renderSettings(); + } + + private async renderSettings(): Promise { + const settings = document.getElementById('settings'); + const saveButton = new BasicButton('success', 'Save', 'saveSettings').render(); + settings.innerHTML = ` +
+ + +
+ `; + settings.innerHTML += saveButton; + + const saveSettings = document.getElementById('saveSettings'); + saveSettings.addEventListener('click', () => { + this.session.contentTest = (document.getElementById('contentTest')).value; + Session.save(); + Session.reloadSession(); + }); + } +} + +new Settings(); diff --git a/src/types/buttonType.ts b/src/types/buttonType.ts new file mode 100644 index 0000000..660d7f9 --- /dev/null +++ b/src/types/buttonType.ts @@ -0,0 +1 @@ +export type customButton = "neutral" | "primary" | "secondary" | "success" | "danger" | "warning" | "info" | "light" | "dark"; \ No newline at end of file diff --git a/tooling.tsconfig.json b/tooling.tsconfig.json new file mode 100644 index 0000000..72730da --- /dev/null +++ b/tooling.tsconfig.json @@ -0,0 +1,31 @@ +{ + "compilerOptions": { + "target": "ESNext", + "lib": [ + "ESNext" + ], + "module": "ESNext", + "outDir": "./tools/", + "rootDir": "./tools/", + "removeComments": true, + "moduleResolution": "node", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "types": [ + "node", + "chrome" + ] + }, + "include": [ + "./tools/*.ts" + ], + "exclude": [ + "node_modules", + "./src/*.ts", + "./src/**/*.ts", + "./dist/*.ts", + "./dist/*.js" + ] +} diff --git a/tools/parse.ts b/tools/parse.ts new file mode 100644 index 0000000..f717693 --- /dev/null +++ b/tools/parse.ts @@ -0,0 +1,72 @@ +import * as fs from "fs"; +import * as path from "path"; +const appConfig = JSON.parse(fs.readFileSync("./app.config.json", "utf8")); +const DEPLOY_TARGET = "./dist/"; + +function findCssFileNames(source: string): string[] { + let files: string[] = []; + const dir = fs.readdirSync(source); + dir.forEach(function (file: string) { + const sourceFile = path.join(source, file); + const stat = fs.lstatSync(sourceFile); + if (stat.isDirectory()) { + files = files.concat(findCssFileNames(sourceFile)); + } else { + if (path.extname(sourceFile) == ".css") { + files.push(file); + } + } + }); + return files; +} + +function findHtmlFilesRecursive(source: string): string[] { + let files: string[] = []; + const dir = fs.readdirSync(source); + dir.forEach(function (file: string) { + const sourceFile = path.join(source, file); + const stat = fs.lstatSync(sourceFile); + if (stat.isDirectory()) { + files = files.concat(findHtmlFilesRecursive(sourceFile)); + } else { + if (path.extname(sourceFile) == ".html") { + files.push(sourceFile); + } + } + }); + return files; +} + +function replaceKeywordsInHtmlFile(file: string) { + let content = fs.readFileSync(file, "utf8"); + const pairs: { key: string; value: string }[] = appConfig.htmlTemplatePairs; + pairs.forEach(function (pair: { key: string; value: string }) { + //@ts-ignore + content = content.replaceAll(pair.key, pair.value); + }); + file = file.replace("public\\", DEPLOY_TARGET); + fs.writeFileSync(file, content); +} + +function buildHtmlFiles(source: string) { + const files = findHtmlFilesRecursive(source); + files.forEach(function (file: string) { + replaceKeywordsInHtmlFile(file); + }); +} + +findCssFileNames(DEPLOY_TARGET).forEach((file: string) => { + const files = findHtmlFilesRecursive(DEPLOY_TARGET); + files.forEach(function (htmlFile: string) { + let content = fs.readFileSync(htmlFile, "utf8"); + content = content.replace( + "", + `\n` + ); + fs.writeFileSync(htmlFile, content); + }); +}); + +buildHtmlFiles(DEPLOY_TARGET); + +console.log("Parsed Files: ", findHtmlFilesRecursive(DEPLOY_TARGET)); diff --git a/tools/syncConfig.ts b/tools/syncConfig.ts new file mode 100644 index 0000000..a9eeaa5 --- /dev/null +++ b/tools/syncConfig.ts @@ -0,0 +1,23 @@ +// @ts-ignore +const fs = require('fs'); + +const appConfig = JSON.parse(fs.readFileSync('./app.config.json', 'utf8')); +const pkg = JSON.parse(fs.readFileSync('./package.json', 'utf8')); +const manifestJson = JSON.parse(fs.readFileSync('./public/manifest.json', 'utf8')); + +pkg.version = appConfig.AppData.version; +pkg.name = appConfig.AppData.id; +pkg.authors = appConfig.AppData.authors; +pkg.description = appConfig.AppData.description; +pkg.homepage = appConfig.AppData.homepage; +pkg.license = appConfig.AppData.license; +pkg.repository = appConfig.AppData.repository; +pkg.bugs = appConfig.AppData.bugs; + +manifestJson.version = appConfig.AppData.version; +manifestJson.name = appConfig.AppData.name; +manifestJson.description = appConfig.AppData.description; +manifestJson.homepage_url = appConfig.AppData.homepage; + +fs.writeFileSync('./package.json', JSON.stringify(pkg, null, 2)); +fs.writeFileSync('./public/manifest.json', JSON.stringify(manifestJson, null, 2)); diff --git a/tools/v2.ts b/tools/v2.ts new file mode 100644 index 0000000..d958bbe --- /dev/null +++ b/tools/v2.ts @@ -0,0 +1,46 @@ +// @ts-ignore +const fs = require('fs'); + +const manifest = JSON.parse(fs.readFileSync('./dist/manifest.json', 'utf8')); + +manifest.manifest_version = 2 + +manifest.background.scripts = [] + +manifest.background.scripts.push(manifest.background.service_worker) +delete manifest.background.type +delete manifest.background.service_worker +manifest.background.persistent = true +if (manifest.host_permissions) { + manifest.permissions.push(manifest.host_permissions) +} +if (manifest.optional_host_permissions) { + manifest.permissions.push(manifest.optional_host_permissions) +} +delete manifest.host_permissions +delete manifest.optional_host_permissions + +let newContentSecurityPolicy = "" + +try { + for (const policy of manifest.content_security_policy) { + newContentSecurityPolicy += policy.key + "'" + policy.value + "'" + " " + } +} catch (e) { + newContentSecurityPolicy = "default-src 'self'" +} + +manifest.content_security_policy = newContentSecurityPolicy + +try { + manifest.web_accessible_resources = manifest.web_accessible_resources.resources +} catch (e) { + manifest.web_accessible_resources = [] +} + +if (manifest.action) { + manifest.browser_action = manifest.action +} +delete manifest.action + +fs.writeFileSync('./dist/manifest.json', JSON.stringify(manifest, null, 2)); diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..a691321 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,75 @@ +{ + "compilerOptions": { + /* Visit https://aka.ms/tsconfig.json to read more about this file */ + + /* Basic Options */ + // "incremental": true, /* Enable incremental compilation */ + "target": "ESNext" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', 'ES2021', or 'ESNEXT'. */, + "module": "ESNext" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */, + "lib": ["ESNext", "DOM"], /* Specify library files to be included in the compilation. */ + // "allowJs": true, /* Allow javascript files to be compiled. */ + // "checkJs": true, /* Report errors in .js files. */ + // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', 'react', 'react-jsx' or 'react-jsxdev'. */ + "declaration": true, /* Generates corresponding '.d.ts' file. */ + "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ + "sourceMap": true /* Generates corresponding '.map' file. */, + // "outFile": "./public/js/app.js", /* Concatenate and emit output to single file. */ + "outDir": "./dist/js/" /* Redirect output structure to the directory. */, + // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ + // "composite": true, /* Enable project compilation */ + // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ + "removeComments": true /* Do not emit comments to output. */, + // "noEmit": true, /* Do not emit outputs. */ + // "importHelpers": true, /* Import emit helpers from 'tslib'. */ + // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ + // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ + "moduleResolution": "node", + /* Strict Type-Checking Options */ + "strict": true /* Enable all strict type-checking options. */, + "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* Enable strict null checks. */ + // "strictFunctionTypes": true, /* Enable strict checking of function types. */ + // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ + // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ + // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ + // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ + + /* Additional Checks */ + // "noUnusedLocals": true, /* Report errors on unused locals. */ + // "noUnusedParameters": true, /* Report errors on unused parameters. */ + // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ + // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ + // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ + // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an 'override' modifier. */ + // "noPropertyAccessFromIndexSignature": true, /* Require undeclared properties from index signatures to use element accesses. */ + + /* Module Resolution Options */ + // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ + // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ + // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ + // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ + // "typeRoots": [], /* List of folders to include type definitions from. */ + // "types": [], /* Type declaration files to be included in compilation. */ + // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ + "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */, + // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + + /* Source Map Options */ + // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ + // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ + + /* Experimental Options */ + // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ + // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ + + /* Advanced Options */ + "skipLibCheck": true /* Skip type checking of declaration files. */, + "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ + }, + "include": ["./src/**/*.ts", "./src/*.ts"], + "exclude": ["node_modules", "./tools/*.ts", "./dist/*.ts", "./dist/*.js"], + "types": ["node", "chrome"] +} diff --git a/vite.config.ts b/vite.config.ts new file mode 100644 index 0000000..afb89cb --- /dev/null +++ b/vite.config.ts @@ -0,0 +1,28 @@ +import { defineConfig } from "vite"; +import { resolve } from "path"; +import tsconfigPaths from "vite-tsconfig-paths"; + +export default defineConfig({ + build: { + rollupOptions: { + input: { + app: resolve(__dirname, "src/app.ts"), + settings: resolve(__dirname, "src/settings.ts"), + background: resolve(__dirname, "src/background.ts"), + }, + output: { + entryFileNames: "[name].js", + dir: resolve(__dirname, "dist"), + }, + }, + sourcemap: true, + }, + plugins: [tsconfigPaths()], + resolve: { + extensions: [".tsx", ".ts", ".scss", ".sass"], + }, + esbuild: { + include: /.*\.tsx?$/, + exclude: [/node_modules/, /dist/], + }, +});