diff --git a/src/pnpm-lock.yaml b/src/pnpm-lock.yaml
index 85fc0d1..1e1fbad 100644
--- a/src/pnpm-lock.yaml
+++ b/src/pnpm-lock.yaml
@@ -36,6 +36,88 @@ importers:
specifier: ^5.2.0
version: 5.3.4(@types/node@20.14.12)
+ webPage/phraseFloatingPanel:
+ dependencies:
+ pinia:
+ specifier: ^2.1.7
+ version: 2.1.7(typescript@5.4.5)(vue@3.4.33)
+ vue:
+ specifier: ^3.4.29
+ version: 3.4.33(typescript@5.4.5)
+ vue-router:
+ specifier: ^4.3.3
+ version: 4.4.0(vue@3.4.33)
+ devDependencies:
+ '@rushstack/eslint-patch':
+ specifier: ^1.8.0
+ version: 1.10.3
+ '@tsconfig/node20':
+ specifier: ^20.1.4
+ version: 20.1.4
+ '@types/jsdom':
+ specifier: ^21.1.7
+ version: 21.1.7
+ '@types/node':
+ specifier: ^20.14.5
+ version: 20.14.12
+ '@vitejs/plugin-vue':
+ specifier: ^5.0.5
+ version: 5.1.0(vite@5.3.4)(vue@3.4.33)
+ '@vitejs/plugin-vue-jsx':
+ specifier: ^4.0.0
+ version: 4.0.0(vite@5.3.4)(vue@3.4.33)
+ '@vue/eslint-config-prettier':
+ specifier: ^9.0.0
+ version: 9.0.0(eslint@8.57.0)(prettier@3.3.3)
+ '@vue/eslint-config-typescript':
+ specifier: ^13.0.0
+ version: 13.0.0(eslint-plugin-vue@9.27.0)(eslint@8.57.0)(typescript@5.4.5)
+ '@vue/test-utils':
+ specifier: ^2.4.6
+ version: 2.4.6
+ '@vue/tsconfig':
+ specifier: ^0.5.1
+ version: 0.5.1
+ cypress:
+ specifier: ^13.12.0
+ version: 13.13.1
+ eslint:
+ specifier: ^8.57.0
+ version: 8.57.0
+ eslint-plugin-cypress:
+ specifier: ^3.3.0
+ version: 3.3.0(eslint@8.57.0)
+ eslint-plugin-vue:
+ specifier: ^9.23.0
+ version: 9.27.0(eslint@8.57.0)
+ jsdom:
+ specifier: ^24.1.0
+ version: 24.1.1
+ npm-run-all2:
+ specifier: ^6.2.0
+ version: 6.2.2
+ prettier:
+ specifier: ^3.2.5
+ version: 3.3.3
+ start-server-and-test:
+ specifier: ^2.0.4
+ version: 2.0.4
+ typescript:
+ specifier: ~5.4.0
+ version: 5.4.5
+ vite:
+ specifier: ^5.3.1
+ version: 5.3.4(@types/node@20.14.12)
+ vite-plugin-vue-devtools:
+ specifier: ^7.3.1
+ version: 7.3.7(vite@5.3.4)(vue@3.4.33)
+ vitest:
+ specifier: ^1.6.0
+ version: 1.6.0(@types/node@20.14.12)(jsdom@24.1.1)
+ vue-tsc:
+ specifier: ^2.0.21
+ version: 2.0.28(typescript@5.4.5)
+
webPage/tedVideoPage:
dependencies:
plyr:
diff --git a/src/webPage/phraseFloatingPanel/.eslintrc.cjs b/src/webPage/phraseFloatingPanel/.eslintrc.cjs
new file mode 100644
index 0000000..6617ed2
--- /dev/null
+++ b/src/webPage/phraseFloatingPanel/.eslintrc.cjs
@@ -0,0 +1,26 @@
+/* eslint-env node */
+require('@rushstack/eslint-patch/modern-module-resolution')
+
+module.exports = {
+ root: true,
+ 'extends': [
+ 'plugin:vue/vue3-essential',
+ 'eslint:recommended',
+ '@vue/eslint-config-typescript',
+ '@vue/eslint-config-prettier/skip-formatting'
+ ],
+ overrides: [
+ {
+ files: [
+ 'cypress/e2e/**/*.{cy,spec}.{js,ts,jsx,tsx}',
+ 'cypress/support/**/*.{js,ts,jsx,tsx}'
+ ],
+ 'extends': [
+ 'plugin:cypress/recommended'
+ ]
+ }
+ ],
+ parserOptions: {
+ ecmaVersion: 'latest'
+ }
+}
diff --git a/src/webPage/phraseFloatingPanel/.gitignore b/src/webPage/phraseFloatingPanel/.gitignore
new file mode 100644
index 0000000..8ee54e8
--- /dev/null
+++ b/src/webPage/phraseFloatingPanel/.gitignore
@@ -0,0 +1,30 @@
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+lerna-debug.log*
+
+node_modules
+.DS_Store
+dist
+dist-ssr
+coverage
+*.local
+
+/cypress/videos/
+/cypress/screenshots/
+
+# Editor directories and files
+.vscode/*
+!.vscode/extensions.json
+.idea
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?
+
+*.tsbuildinfo
diff --git a/src/webPage/phraseFloatingPanel/.prettierrc.json b/src/webPage/phraseFloatingPanel/.prettierrc.json
new file mode 100644
index 0000000..66e2335
--- /dev/null
+++ b/src/webPage/phraseFloatingPanel/.prettierrc.json
@@ -0,0 +1,8 @@
+{
+ "$schema": "https://json.schemastore.org/prettierrc",
+ "semi": false,
+ "tabWidth": 2,
+ "singleQuote": true,
+ "printWidth": 100,
+ "trailingComma": "none"
+}
\ No newline at end of file
diff --git a/src/webPage/phraseFloatingPanel/.vscode/extensions.json b/src/webPage/phraseFloatingPanel/.vscode/extensions.json
new file mode 100644
index 0000000..93ea3e7
--- /dev/null
+++ b/src/webPage/phraseFloatingPanel/.vscode/extensions.json
@@ -0,0 +1,7 @@
+{
+ "recommendations": [
+ "Vue.volar",
+ "dbaeumer.vscode-eslint",
+ "esbenp.prettier-vscode"
+ ]
+}
diff --git a/src/webPage/phraseFloatingPanel/README.md b/src/webPage/phraseFloatingPanel/README.md
new file mode 100644
index 0000000..0f6d417
--- /dev/null
+++ b/src/webPage/phraseFloatingPanel/README.md
@@ -0,0 +1 @@
+# Phrase floating panel/icon
\ No newline at end of file
diff --git a/src/webPage/phraseFloatingPanel/cypress.config.ts b/src/webPage/phraseFloatingPanel/cypress.config.ts
new file mode 100644
index 0000000..0f66080
--- /dev/null
+++ b/src/webPage/phraseFloatingPanel/cypress.config.ts
@@ -0,0 +1,8 @@
+import { defineConfig } from 'cypress'
+
+export default defineConfig({
+ e2e: {
+ specPattern: 'cypress/e2e/**/*.{cy,spec}.{js,jsx,ts,tsx}',
+ baseUrl: 'http://localhost:4173'
+ }
+})
diff --git a/src/webPage/phraseFloatingPanel/cypress/e2e/example.cy.ts b/src/webPage/phraseFloatingPanel/cypress/e2e/example.cy.ts
new file mode 100644
index 0000000..7554c35
--- /dev/null
+++ b/src/webPage/phraseFloatingPanel/cypress/e2e/example.cy.ts
@@ -0,0 +1,8 @@
+// https://on.cypress.io/api
+
+describe('My First Test', () => {
+ it('visits the app root url', () => {
+ cy.visit('/')
+ cy.contains('h1', 'You did it!')
+ })
+})
diff --git a/src/webPage/phraseFloatingPanel/cypress/e2e/tsconfig.json b/src/webPage/phraseFloatingPanel/cypress/e2e/tsconfig.json
new file mode 100644
index 0000000..c94f1d4
--- /dev/null
+++ b/src/webPage/phraseFloatingPanel/cypress/e2e/tsconfig.json
@@ -0,0 +1,8 @@
+{
+ "extends": "@vue/tsconfig/tsconfig.dom.json",
+ "include": ["./**/*", "../support/**/*"],
+ "compilerOptions": {
+ "isolatedModules": false,
+ "types": ["cypress"]
+ }
+}
diff --git a/src/webPage/phraseFloatingPanel/cypress/fixtures/example.json b/src/webPage/phraseFloatingPanel/cypress/fixtures/example.json
new file mode 100644
index 0000000..02e4254
--- /dev/null
+++ b/src/webPage/phraseFloatingPanel/cypress/fixtures/example.json
@@ -0,0 +1,5 @@
+{
+ "name": "Using fixtures to represent data",
+ "email": "hello@cypress.io",
+ "body": "Fixtures are a great way to mock data for responses to routes"
+}
diff --git a/src/webPage/phraseFloatingPanel/cypress/support/commands.ts b/src/webPage/phraseFloatingPanel/cypress/support/commands.ts
new file mode 100644
index 0000000..9b7bb8e
--- /dev/null
+++ b/src/webPage/phraseFloatingPanel/cypress/support/commands.ts
@@ -0,0 +1,39 @@
+///
+// ***********************************************
+// This example commands.ts shows you how to
+// create various custom commands and overwrite
+// existing commands.
+//
+// For more comprehensive examples of custom
+// commands please read more here:
+// https://on.cypress.io/custom-commands
+// ***********************************************
+//
+//
+// -- This is a parent command --
+// Cypress.Commands.add('login', (email, password) => { ... })
+//
+//
+// -- This is a child command --
+// Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... })
+//
+//
+// -- This is a dual command --
+// Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... })
+//
+//
+// -- This will overwrite an existing command --
+// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... })
+//
+// declare global {
+// namespace Cypress {
+// interface Chainable {
+// login(email: string, password: string): Chainable
+// drag(subject: string, options?: Partial): Chainable
+// dismiss(subject: string, options?: Partial): Chainable
+// visit(originalFn: CommandOriginalFn, url: string, options: Partial): Chainable
+// }
+// }
+// }
+
+export {}
diff --git a/src/webPage/phraseFloatingPanel/cypress/support/e2e.ts b/src/webPage/phraseFloatingPanel/cypress/support/e2e.ts
new file mode 100644
index 0000000..d68db96
--- /dev/null
+++ b/src/webPage/phraseFloatingPanel/cypress/support/e2e.ts
@@ -0,0 +1,20 @@
+// ***********************************************************
+// This example support/index.js is processed and
+// loaded automatically before your test files.
+//
+// This is a great place to put global configuration and
+// behavior that modifies Cypress.
+//
+// You can change the location of this file or turn off
+// automatically serving support files with the
+// 'supportFile' configuration option.
+//
+// You can read more here:
+// https://on.cypress.io/configuration
+// ***********************************************************
+
+// Import commands.js using ES2015 syntax:
+import './commands'
+
+// Alternatively you can use CommonJS syntax:
+// require('./commands')
diff --git a/src/webPage/phraseFloatingPanel/env.d.ts b/src/webPage/phraseFloatingPanel/env.d.ts
new file mode 100644
index 0000000..11f02fe
--- /dev/null
+++ b/src/webPage/phraseFloatingPanel/env.d.ts
@@ -0,0 +1 @@
+///
diff --git a/src/webPage/phraseFloatingPanel/index.html b/src/webPage/phraseFloatingPanel/index.html
new file mode 100644
index 0000000..a888544
--- /dev/null
+++ b/src/webPage/phraseFloatingPanel/index.html
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+ Vite App
+
+
+
+
+
+
diff --git a/src/webPage/phraseFloatingPanel/package.json b/src/webPage/phraseFloatingPanel/package.json
new file mode 100644
index 0000000..6b3ad66
--- /dev/null
+++ b/src/webPage/phraseFloatingPanel/package.json
@@ -0,0 +1,48 @@
+{
+ "name": "phrasefloatingpanel",
+ "version": "0.0.0",
+ "private": true,
+ "type": "module",
+ "scripts": {
+ "dev": "vite",
+ "build": "run-p type-check \"build-only {@}\" --",
+ "preview": "vite preview",
+ "test:unit": "vitest",
+ "test:e2e": "start-server-and-test preview http://localhost:4173 'cypress run --e2e'",
+ "test:e2e:dev": "start-server-and-test 'vite dev --port 4173' http://localhost:4173 'cypress open --e2e'",
+ "build-only": "vite build",
+ "type-check": "vue-tsc --build --force",
+ "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore",
+ "format": "prettier --write src/"
+ },
+ "dependencies": {
+ "pinia": "^2.1.7",
+ "vue": "^3.4.29",
+ "vue-router": "^4.3.3"
+ },
+ "devDependencies": {
+ "@rushstack/eslint-patch": "^1.8.0",
+ "@tsconfig/node20": "^20.1.4",
+ "@types/jsdom": "^21.1.7",
+ "@types/node": "^20.14.5",
+ "@vitejs/plugin-vue": "^5.0.5",
+ "@vitejs/plugin-vue-jsx": "^4.0.0",
+ "@vue/eslint-config-prettier": "^9.0.0",
+ "@vue/eslint-config-typescript": "^13.0.0",
+ "@vue/test-utils": "^2.4.6",
+ "@vue/tsconfig": "^0.5.1",
+ "cypress": "^13.12.0",
+ "eslint": "^8.57.0",
+ "eslint-plugin-cypress": "^3.3.0",
+ "eslint-plugin-vue": "^9.23.0",
+ "jsdom": "^24.1.0",
+ "npm-run-all2": "^6.2.0",
+ "prettier": "^3.2.5",
+ "start-server-and-test": "^2.0.4",
+ "typescript": "~5.4.0",
+ "vite": "^5.3.1",
+ "vite-plugin-vue-devtools": "^7.3.1",
+ "vitest": "^1.6.0",
+ "vue-tsc": "^2.0.21"
+ }
+}
diff --git a/src/webPage/phraseFloatingPanel/public/favicon.ico b/src/webPage/phraseFloatingPanel/public/favicon.ico
new file mode 100644
index 0000000..df36fcf
Binary files /dev/null and b/src/webPage/phraseFloatingPanel/public/favicon.ico differ
diff --git a/src/webPage/phraseFloatingPanel/src/App.vue b/src/webPage/phraseFloatingPanel/src/App.vue
new file mode 100644
index 0000000..e1b58e6
--- /dev/null
+++ b/src/webPage/phraseFloatingPanel/src/App.vue
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/webPage/phraseFloatingPanel/src/assets/base.css b/src/webPage/phraseFloatingPanel/src/assets/base.css
new file mode 100644
index 0000000..ad3cc04
--- /dev/null
+++ b/src/webPage/phraseFloatingPanel/src/assets/base.css
@@ -0,0 +1,2 @@
+/* color palette from */
+
diff --git a/src/webPage/phraseFloatingPanel/src/assets/main.css b/src/webPage/phraseFloatingPanel/src/assets/main.css
new file mode 100644
index 0000000..02b21d1
--- /dev/null
+++ b/src/webPage/phraseFloatingPanel/src/assets/main.css
@@ -0,0 +1,3 @@
+@import './base.css';
+
+
diff --git a/src/webPage/phraseFloatingPanel/src/main.ts b/src/webPage/phraseFloatingPanel/src/main.ts
new file mode 100644
index 0000000..5dcad83
--- /dev/null
+++ b/src/webPage/phraseFloatingPanel/src/main.ts
@@ -0,0 +1,14 @@
+import './assets/main.css'
+
+import { createApp } from 'vue'
+import { createPinia } from 'pinia'
+
+import App from './App.vue'
+import router from './router'
+
+const app = createApp(App)
+
+app.use(createPinia())
+app.use(router)
+
+app.mount('#app')
diff --git a/src/webPage/phraseFloatingPanel/src/router/index.ts b/src/webPage/phraseFloatingPanel/src/router/index.ts
new file mode 100644
index 0000000..9b98158
--- /dev/null
+++ b/src/webPage/phraseFloatingPanel/src/router/index.ts
@@ -0,0 +1,14 @@
+import { createRouter, createWebHistory } from 'vue-router'
+
+const router = createRouter({
+ history: createWebHistory(import.meta.env.BASE_URL),
+ routes: [
+ {
+ path: '/',
+ name: 'home',
+ component: () => import('../views/HomeView.vue')
+ }
+ ]
+})
+
+export default router
diff --git a/src/webPage/phraseFloatingPanel/src/stores/counter.ts b/src/webPage/phraseFloatingPanel/src/stores/counter.ts
new file mode 100644
index 0000000..b6757ba
--- /dev/null
+++ b/src/webPage/phraseFloatingPanel/src/stores/counter.ts
@@ -0,0 +1,12 @@
+import { ref, computed } from 'vue'
+import { defineStore } from 'pinia'
+
+export const useCounterStore = defineStore('counter', () => {
+ const count = ref(0)
+ const doubleCount = computed(() => count.value * 2)
+ function increment() {
+ count.value++
+ }
+
+ return { count, doubleCount, increment }
+})
diff --git a/src/webPage/phraseFloatingPanel/src/views/HomeView.vue b/src/webPage/phraseFloatingPanel/src/views/HomeView.vue
new file mode 100644
index 0000000..0acad45
--- /dev/null
+++ b/src/webPage/phraseFloatingPanel/src/views/HomeView.vue
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/src/webPage/phraseFloatingPanel/tsconfig.app.json b/src/webPage/phraseFloatingPanel/tsconfig.app.json
new file mode 100644
index 0000000..e14c754
--- /dev/null
+++ b/src/webPage/phraseFloatingPanel/tsconfig.app.json
@@ -0,0 +1,14 @@
+{
+ "extends": "@vue/tsconfig/tsconfig.dom.json",
+ "include": ["env.d.ts", "src/**/*", "src/**/*.vue"],
+ "exclude": ["src/**/__tests__/*"],
+ "compilerOptions": {
+ "composite": true,
+ "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
+
+ "baseUrl": ".",
+ "paths": {
+ "@/*": ["./src/*"]
+ }
+ }
+}
diff --git a/src/webPage/phraseFloatingPanel/tsconfig.json b/src/webPage/phraseFloatingPanel/tsconfig.json
new file mode 100644
index 0000000..5304731
--- /dev/null
+++ b/src/webPage/phraseFloatingPanel/tsconfig.json
@@ -0,0 +1,17 @@
+{
+ "files": [],
+ "references": [
+ {
+ "path": "./tsconfig.node.json"
+ },
+ {
+ "path": "./tsconfig.app.json"
+ },
+ {
+ "path": "./tsconfig.vitest.json"
+ }
+ ],
+ "compilerOptions": {
+ "module": "NodeNext"
+ }
+}
diff --git a/src/webPage/phraseFloatingPanel/tsconfig.node.json b/src/webPage/phraseFloatingPanel/tsconfig.node.json
new file mode 100644
index 0000000..f094063
--- /dev/null
+++ b/src/webPage/phraseFloatingPanel/tsconfig.node.json
@@ -0,0 +1,19 @@
+{
+ "extends": "@tsconfig/node20/tsconfig.json",
+ "include": [
+ "vite.config.*",
+ "vitest.config.*",
+ "cypress.config.*",
+ "nightwatch.conf.*",
+ "playwright.config.*"
+ ],
+ "compilerOptions": {
+ "composite": true,
+ "noEmit": true,
+ "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
+
+ "module": "ESNext",
+ "moduleResolution": "Bundler",
+ "types": ["node"]
+ }
+}
diff --git a/src/webPage/phraseFloatingPanel/tsconfig.vitest.json b/src/webPage/phraseFloatingPanel/tsconfig.vitest.json
new file mode 100644
index 0000000..571995d
--- /dev/null
+++ b/src/webPage/phraseFloatingPanel/tsconfig.vitest.json
@@ -0,0 +1,11 @@
+{
+ "extends": "./tsconfig.app.json",
+ "exclude": [],
+ "compilerOptions": {
+ "composite": true,
+ "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.vitest.tsbuildinfo",
+
+ "lib": [],
+ "types": ["node", "jsdom"]
+ }
+}
diff --git a/src/webPage/phraseFloatingPanel/vite.config.ts b/src/webPage/phraseFloatingPanel/vite.config.ts
new file mode 100644
index 0000000..ef08de3
--- /dev/null
+++ b/src/webPage/phraseFloatingPanel/vite.config.ts
@@ -0,0 +1,27 @@
+import { fileURLToPath, URL } from 'node:url'
+
+import { defineConfig } from 'vite'
+import vue from '@vitejs/plugin-vue'
+import vueJsx from '@vitejs/plugin-vue-jsx'
+import vueDevTools from 'vite-plugin-vue-devtools'
+
+// https://vitejs.dev/config/
+export default defineConfig({
+ build: {
+ rollupOptions: {
+ output: {
+ // entryFileNames: 'stylish-reader-phrase-floating-panel.js',
+ // chunkFileNames: 'stylish-reader-phrase-floating-panel-[name].js',
+ assetFileNames: 'stylish-reader-phrase-floating-panel-[name].[ext]',
+ inlineDynamicImports: true,
+ entryFileNames: 'stylish-reader-phrase-floating-panel.js'
+ }
+ }
+ },
+ plugins: [vue(), vueJsx(), vueDevTools()],
+ resolve: {
+ alias: {
+ '@': fileURLToPath(new URL('./src', import.meta.url))
+ }
+ }
+})
diff --git a/src/webPage/phraseFloatingPanel/vitest.config.ts b/src/webPage/phraseFloatingPanel/vitest.config.ts
new file mode 100644
index 0000000..4b1c897
--- /dev/null
+++ b/src/webPage/phraseFloatingPanel/vitest.config.ts
@@ -0,0 +1,14 @@
+import { fileURLToPath } from 'node:url'
+import { mergeConfig, defineConfig, configDefaults } from 'vitest/config'
+import viteConfig from './vite.config'
+
+export default mergeConfig(
+ viteConfig,
+ defineConfig({
+ test: {
+ environment: 'jsdom',
+ exclude: [...configDefaults.exclude, 'e2e/**'],
+ root: fileURLToPath(new URL('./', import.meta.url))
+ }
+ })
+)