7 Commits

Author SHA1 Message Date
efe23a99ac 0.3.5
All checks were successful
Deploy to Production / deploy (push) Successful in 13s
2026-02-27 04:50:57 +00:00
bebb63c1de feat: improve PWA update mechanism with visibility check 2026-02-27 04:50:39 +00:00
98d76e3a35 0.3.4
All checks were successful
Deploy to Production / deploy (push) Successful in 13s
2026-02-27 04:44:34 +00:00
cc7e80a807 feat: improve mobile layout for Password Generator button 2026-02-27 04:43:58 +00:00
1f5500f7d7 0.3.3
All checks were successful
Deploy to Production / deploy (push) Successful in 13s
2026-02-27 04:37:29 +00:00
d404370027 fix: adjust layout for Password Generator and Clipboard Sniffer on smaller screens 2026-02-27 04:37:21 +00:00
8fb3ee1069 refactor: align privacy policy layout with app design
All checks were successful
Deploy to Production / deploy (push) Successful in 7s
2026-02-27 04:20:29 +00:00
6 changed files with 207 additions and 81 deletions

4
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{ {
"name": "tools-app", "name": "tools-app",
"version": "0.3.2", "version": "0.3.5",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "tools-app", "name": "tools-app",
"version": "0.3.2", "version": "0.3.5",
"dependencies": { "dependencies": {
"lucide-vue-next": "^0.575.0", "lucide-vue-next": "^0.575.0",
"vue": "^3.5.25", "vue": "^3.5.25",

View File

@@ -1,7 +1,7 @@
{ {
"name": "tools-app", "name": "tools-app",
"private": true, "private": true,
"version": "0.3.2", "version": "0.3.5",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",

View File

@@ -5,6 +5,7 @@ import Header from './components/Header.vue'
import Footer from './components/Footer.vue' import Footer from './components/Footer.vue'
import Sidebar from './components/Sidebar.vue' import Sidebar from './components/Sidebar.vue'
import InstallPrompt from './components/InstallPrompt.vue' import InstallPrompt from './components/InstallPrompt.vue'
import ReloadPrompt from './components/ReloadPrompt.vue'
const isSidebarOpen = ref(window.innerWidth >= 768) const isSidebarOpen = ref(window.innerWidth >= 768)
const router = useRouter() const router = useRouter()
@@ -53,6 +54,7 @@ onUnmounted(() => {
</div> </div>
<Footer /> <Footer />
<InstallPrompt /> <InstallPrompt />
<ReloadPrompt />
</template> </template>
<style scoped> <style scoped>

View File

@@ -0,0 +1,94 @@
<script setup>
import { useRegisterSW } from 'virtual:pwa-register/vue'
import { watch, onMounted } from 'vue'
// Zmieniamy na autoUpdate w configu, więc tutaj tylko nasłuchujemy i ewentualnie wymuszamy
const {
needRefresh,
updateServiceWorker,
} = useRegisterSW({
immediate: true,
onRegistered(r) {
// Sprawdzaj aktualizacje co godzinę
r && setInterval(() => {
r.update()
}, 60 * 60 * 1000)
}
})
const updateSW = async () => {
if ('serviceWorker' in navigator) {
try {
const registration = await navigator.serviceWorker.ready
console.log('Checking for SW update...')
await registration.update()
} catch (e) {
console.error('Failed to update SW:', e)
}
}
}
onMounted(() => {
// Check on load
updateSW()
// Check when app becomes visible again (e.g. switching tabs/apps)
document.addEventListener('visibilitychange', () => {
if (document.visibilityState === 'visible') {
updateSW()
}
})
})
</script>
<template>
<div
v-if="needRefresh"
class="pwa-toast"
role="alert"
>
<div class="message">
New content available, click on reload button to update.
</div>
<button @click="updateServiceWorker()">
Reload
</button>
</div>
</template>
<style scoped>
.pwa-toast {
position: fixed;
right: 0;
bottom: 0;
margin: 16px;
padding: 12px;
border: 1px solid var(--glass-border);
border-radius: 4px;
z-index: 10000;
text-align: left;
box-shadow: var(--glass-shadow);
background-color: var(--glass-bg);
backdrop-filter: blur(10px);
color: var(--text-color);
display: flex;
flex-direction: column;
gap: 8px;
}
.pwa-toast button {
border: 1px solid var(--glass-border);
outline: none;
margin-right: 5px;
border-radius: 2px;
padding: 3px 10px;
cursor: pointer;
background: var(--button-bg);
color: var(--text-color);
transition: all 0.2s;
}
.pwa-toast button:hover {
background: var(--button-hover-bg);
}
</style>

View File

@@ -75,7 +75,7 @@ const generatePasswords = () => {
<div class="tool-panel"> <div class="tool-panel">
<div class="panel-header"> <div class="panel-header">
<h2 class="tool-title">Bulk Passwords Generator</h2> <h2 class="tool-title">Bulk Passwords Generator</h2>
<div class="action-area"> <div class="action-area desktop-only">
<button class="btn-neon generate-btn" @click="generatePasswords" v-ripple> <button class="btn-neon generate-btn" @click="generatePasswords" v-ripple>
Generate Generate
</button> </button>
@@ -131,6 +131,12 @@ const generatePasswords = () => {
</div> </div>
</div> </div>
<div class="mobile-only" style="margin-top: 1rem; width: 100%;">
<button class="btn-neon generate-btn" @click="generatePasswords" v-ripple style="width: 100%;">
Generate
</button>
</div>
<div class="result-area" :style="{ height: textareaHeight }"> <div class="result-area" :style="{ height: textareaHeight }">
<textarea <textarea
class="tool-textarea" class="tool-textarea"
@@ -189,9 +195,10 @@ const generatePasswords = () => {
.inputs-group { .inputs-group {
display: flex; display: flex;
gap: 2rem; gap: 1rem;
flex: 1; flex: 1;
min-width: 300px; min-width: 200px;
flex-wrap: wrap;
} }
.input-wrapper { .input-wrapper {
@@ -199,6 +206,7 @@ const generatePasswords = () => {
flex-direction: column; flex-direction: column;
gap: 0.5rem; gap: 0.5rem;
flex: 1; flex: 1;
min-width: 140px;
} }
.checkbox-label { .checkbox-label {
@@ -351,6 +359,14 @@ const generatePasswords = () => {
letter-spacing: 1px; letter-spacing: 1px;
} }
.desktop-only {
display: block;
}
.mobile-only {
display: none;
}
@media (max-width: 768px) { @media (max-width: 768px) {
.options-grid { .options-grid {
flex-direction: column; flex-direction: column;
@@ -370,5 +386,13 @@ const generatePasswords = () => {
.generate-btn { .generate-btn {
width: 100%; width: 100%;
} }
.desktop-only {
display: none;
}
.mobile-only {
display: block !important;
}
} }
</style> </style>

View File

@@ -3,8 +3,8 @@ import { ArrowLeft } from 'lucide-vue-next'
</script> </script>
<template> <template>
<div class="privacy-container"> <div class="tool-container">
<div class="privacy-content"> <div class="tool-panel privacy-panel">
<header class="privacy-header"> <header class="privacy-header">
<router-link to="/" class="back-link"> <router-link to="/" class="back-link">
<ArrowLeft size="20" /> <ArrowLeft size="20" />
@@ -14,96 +14,102 @@ import { ArrowLeft } from 'lucide-vue-next'
<p class="last-updated">Last Updated: February 27, 2026</p> <p class="last-updated">Last Updated: February 27, 2026</p>
</header> </header>
<section> <div class="privacy-body">
<h2>1. Introduction</h2> <section>
<p> <h2>1. Introduction</h2>
Welcome to Tools App ("we," "our," or "us"). We are committed to protecting your privacy. <p>
This Privacy Policy explains how our Chrome Extension ("Tools App Extension") handles your data. Welcome to Tools App ("we," "our," or "us"). We are committed to protecting your privacy.
</p> This Privacy Policy explains how our Chrome Extension ("Tools App Extension") handles your data.
</section> </p>
</section>
<section> <section>
<h2>2. Data Collection and Usage</h2> <h2>2. Data Collection and Usage</h2>
<p> <p>
The Tools App Extension is designed with privacy as a priority. The Tools App Extension is designed with privacy as a priority.
<strong>We do not collect, store, or transmit any of your personal data to external servers.</strong> <strong>We do not collect, store, or transmit any of your personal data to external servers.</strong>
</p> </p>
<h3>Clipboard Data</h3> <h3>Clipboard Data</h3>
<p> <p>
The extension requires the <code>clipboardRead</code> permission to function. The extension requires the <code>clipboardRead</code> permission to function.
It reads text from your clipboard <strong>only</strong> when you explicitly enable the "Clipboard Sniffer" tool in the Tools App web interface. It reads text from your clipboard <strong>only</strong> when you explicitly enable the "Clipboard Sniffer" tool in the Tools App web interface.
</p> </p>
<ul> <ul>
<li>Clipboard data is processed locally within your browser.</li> <li>Clipboard data is processed locally within your browser.</li>
<li>Data is sent directly from the extension to the open Tools App tab via a secure local communication channel.</li> <li>Data is sent directly from the extension to the open Tools App tab via a secure local communication channel.</li>
<li>Once you close the tab or stop the tool, the extension stops monitoring the clipboard immediately.</li> <li>Once you close the tab or stop the tool, the extension stops monitoring the clipboard immediately.</li>
<li>We do not have access to your clipboard history, and it is never uploaded to any cloud storage or third-party service.</li> <li>We do not have access to your clipboard history, and it is never uploaded to any cloud storage or third-party service.</li>
</ul> </ul>
</section> </section>
<section> <section>
<h2>3. Permissions</h2> <h2>3. Permissions</h2>
<p>The extension requests the following permissions for specific functional purposes:</p> <p>The extension requests the following permissions for specific functional purposes:</p>
<ul> <ul>
<li><strong>clipboardRead:</strong> To detect copied text when the Sniffer tool is active.</li> <li><strong>clipboardRead:</strong> To detect copied text when the Sniffer tool is active.</li>
<li><strong>scripting:</strong> To communicate with the Tools App web page.</li> <li><strong>scripting:</strong> To communicate with the Tools App web page.</li>
<li><strong>storage:</strong> To save local user preferences (e.g., sound settings).</li> <li><strong>storage:</strong> To save local user preferences (e.g., sound settings).</li>
<li><strong>alarms:</strong> To maintain the background process active during monitoring sessions.</li> <li><strong>alarms:</strong> To maintain the background process active during monitoring sessions.</li>
</ul> </ul>
</section> </section>
<section> <section>
<h2>4. Third-Party Services</h2> <h2>4. Third-Party Services</h2>
<p> <p>
Our extension operates independently and does not use any third-party analytics, tracking scripts, or advertising networks. Our extension operates independently and does not use any third-party analytics, tracking scripts, or advertising networks.
</p> </p>
</section> </section>
<section> <section>
<h2>5. Changes to This Policy</h2> <h2>5. Changes to This Policy</h2>
<p> <p>
We may update our Privacy Policy from time to time. We will notify you of any changes by posting the new Privacy Policy on this page. We may update our Privacy Policy from time to time. We will notify you of any changes by posting the new Privacy Policy on this page.
</p> </p>
</section> </section>
<section> <section>
<h2>6. Contact Us</h2> <h2>6. Contact Us</h2>
<p> <p>
If you have any questions about this Privacy Policy, please contact us via the repository or support channels provided in the Chrome Web Store listing. If you have any questions about this Privacy Policy, please contact us via the repository or support channels provided in the Chrome Web Store listing.
</p> </p>
</section> </section>
</div>
</div> </div>
</div> </div>
</template> </template>
<style scoped> <style scoped>
.privacy-container { .privacy-panel {
min-height: 100vh; max-width: 900px; /* Slightly wider for reading */
width: 100%; margin: 0 auto;
display: flex;
justify-content: center;
padding: 2rem;
background: var(--bg-gradient);
color: var(--text-color);
} }
.privacy-content { .privacy-body {
width: 100%; overflow-y: auto;
max-width: 800px; padding-right: 0.5rem;
background: var(--glass-bg); }
backdrop-filter: blur(12px);
-webkit-backdrop-filter: blur(12px); /* Custom scrollbar for privacy body */
border: 1px solid var(--glass-border); .privacy-body::-webkit-scrollbar {
border-radius: 16px; width: 8px;
padding: 3rem; }
box-shadow: var(--glass-shadow);
.privacy-body::-webkit-scrollbar-track {
background: rgba(0, 0, 0, 0.1);
border-radius: 4px;
}
.privacy-body::-webkit-scrollbar-thumb {
background: rgba(255, 255, 255, 0.2);
border-radius: 4px;
} }
.privacy-header { .privacy-header {
margin-bottom: 3rem; margin-bottom: 2rem;
border-bottom: 1px solid var(--glass-border); border-bottom: 1px solid var(--glass-border);
padding-bottom: 2rem; padding-bottom: 1.5rem;
flex-shrink: 0;
} }
.back-link { .back-link {