From c34287e2aef98dce3834df2cebe1090e96112bc1 Mon Sep 17 00:00:00 2001 From: Dan Essig Date: Sun, 7 Jun 2026 15:37:52 -0700 Subject: [PATCH 1/8] build(deps): bump tmp, ip-address, @xmldom/xmldom (transitive) - tmp 0.2.5 -> 0.2.7 (#116) - ip-address 10.1.0 -> 10.2.0 (#112) - @xmldom/xmldom 0.8.12 -> 0.8.13 (#111) All transitive deps under electron-builder packaging tooling. package.json unchanged; lock-only update. --- package-lock.json | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/package-lock.json b/package-lock.json index 41fde49..55f178d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,7 @@ "name": "clipless", "version": "1.8.5", "hasInstallScript": true, + "license": "MIT", "dependencies": { "@electron-toolkit/preload": "^3.0.1", "@electron-toolkit/utils": "^4.0.0", @@ -3545,9 +3546,9 @@ } }, "node_modules/@xmldom/xmldom": { - "version": "0.8.12", - "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.12.tgz", - "integrity": "sha512-9k/gHF6n/pAi/9tqr3m3aqkuiNosYTurLLUtc7xQ9sxB/wm7WPygCv8GYa6mS0fLJEHhqMC1ATYhz++U/lRHqg==", + "version": "0.8.13", + "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.13.tgz", + "integrity": "sha512-KRYzxepc14G/CEpEGc3Yn+JKaAeT63smlDr+vjB8jRfgTBBI9wRj/nkQEO+ucV8p8I9bfKLWp37uHgFrbntPvw==", "dev": true, "license": "MIT", "engines": { @@ -7284,9 +7285,9 @@ } }, "node_modules/ip-address": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.1.0.tgz", - "integrity": "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==", + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.2.0.tgz", + "integrity": "sha512-/+S6j4E9AHvW9SWMSEY9Xfy66O5PWvVEJ08O0y5JGyEKQpojb0K0GKpz/v5HJ/G0vi3D2sjGK78119oXZeE0qA==", "dev": true, "license": "MIT", "engines": { @@ -11164,9 +11165,9 @@ "license": "MIT" }, "node_modules/tmp": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.5.tgz", - "integrity": "sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==", + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.7.tgz", + "integrity": "sha512-e0votIpp4Uo2AJYSzVHV6xCcawuiez3DzqDAbrTc3YxBkplN6e+dM13ZeIcZnDg/QpSuU2zfZ3rzwY8ukEnaXw==", "dev": true, "license": "MIT", "engines": { From e7d25c0bed2be889cdd289f435c0ac89bb7b0bcc Mon Sep 17 00:00:00 2001 From: Dan Essig Date: Sun, 7 Jun 2026 15:39:05 -0700 Subject: [PATCH 2/8] build(deps): bump vite 6.4.1 -> 6.4.3 and postcss to 8.5.15 - vite ^6.4.2 (#109), resolves to 6.4.3 (path-traversal + server.fs fixes) - postcss 8.5.6 -> 8.5.15 (#114), transitive under vite (XSS/path fixes) Build (main/preload/renderer), typecheck, unit tests + 100% coverage all pass. --- package-lock.json | 22 +++++++++++----------- package.json | 2 +- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/package-lock.json b/package-lock.json index 55f178d..8a6ecf4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -50,7 +50,7 @@ "react": "^19.1.0", "react-dom": "^19.1.0", "typescript": "^5.8.3", - "vite": "^6.4.1", + "vite": "^6.4.2", "vitest": "^4.0.18" } }, @@ -8805,9 +8805,9 @@ "license": "MIT" }, "node_modules/nanoid": { - "version": "3.3.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "version": "3.3.12", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", + "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", "funding": [ { "type": "github", @@ -9500,9 +9500,9 @@ } }, "node_modules/postcss": { - "version": "8.5.6", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", - "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "version": "8.5.15", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz", + "integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==", "funding": [ { "type": "opencollective", @@ -9519,7 +9519,7 @@ ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.11", + "nanoid": "^3.3.12", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, @@ -11524,9 +11524,9 @@ } }, "node_modules/vite": { - "version": "6.4.1", - "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.1.tgz", - "integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==", + "version": "6.4.3", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.3.tgz", + "integrity": "sha512-NTKlcQjlAK7MlQoyb6LgaqHc8sso/pVyUJYWMws3jg21uTJw/LddqIFPcPqP6PzpgbIcZyKI85sFE4HBrQDA8A==", "license": "MIT", "dependencies": { "esbuild": "^0.25.0", diff --git a/package.json b/package.json index 4019d08..d0b1993 100644 --- a/package.json +++ b/package.json @@ -74,7 +74,7 @@ "react": "^19.1.0", "react-dom": "^19.1.0", "typescript": "^5.8.3", - "vite": "^6.4.1", + "vite": "^6.4.2", "vitest": "^4.0.18" } } From 70c0d7f44cf0455f61c450f94665b7e525f030fb Mon Sep 17 00:00:00 2001 From: Dan Essig Date: Sun, 7 Jun 2026 15:39:54 -0700 Subject: [PATCH 3/8] build(deps): bump vitest, @vitest/coverage-v8, @vitest/ui 4.0.18 -> 4.1.8 (#122) Test tooling minor bump. Lint, typecheck, and unit tests pass with 100% coverage maintained (statements/branches/functions/lines). --- package-lock.json | 259 ++++++++++++++++++++++++---------------------- package.json | 6 +- 2 files changed, 141 insertions(+), 124 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8a6ecf4..87da5bb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -36,8 +36,8 @@ "@types/react-dom": "^19.1.2", "@types/react-syntax-highlighter": "^15.5.13", "@vitejs/plugin-react": "^4.3.4", - "@vitest/coverage-v8": "^4.0.18", - "@vitest/ui": "^4.0.18", + "@vitest/coverage-v8": "^4.1.8", + "@vitest/ui": "^4.1.8", "electron": "^35.7.5", "electron-builder": "^26.8.1", "electron-vite": "^3.1.0", @@ -51,7 +51,7 @@ "react-dom": "^19.1.0", "typescript": "^5.8.3", "vite": "^6.4.2", - "vitest": "^4.0.18" + "vitest": "^4.1.8" } }, "node_modules/@acemir/cssom": { @@ -279,9 +279,9 @@ } }, "node_modules/@babel/helper-string-parser": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", - "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", + "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", "dev": true, "license": "MIT", "engines": { @@ -289,9 +289,9 @@ } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", - "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", "dev": true, "license": "MIT", "engines": { @@ -323,13 +323,13 @@ } }, "node_modules/@babel/parser": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.0.tgz", - "integrity": "sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.7.tgz", + "integrity": "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.29.0" + "@babel/types": "^7.29.7" }, "bin": { "parser": "bin/babel-parser.js" @@ -430,14 +430,14 @@ } }, "node_modules/@babel/types": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", - "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.7.tgz", + "integrity": "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.28.5" + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -3382,29 +3382,29 @@ } }, "node_modules/@vitest/coverage-v8": { - "version": "4.0.18", - "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-4.0.18.tgz", - "integrity": "sha512-7i+N2i0+ME+2JFZhfuz7Tg/FqKtilHjGyGvoHYQ6iLV0zahbsJ9sljC9OcFcPDbhYKCet+sG8SsVqlyGvPflZg==", + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-4.1.8.tgz", + "integrity": "sha512-lt3kovsyHwYe00wq4D1ti0Z974fWj4NLp6siqiyEufUpyFwK9Yhi7rBhac9JL5aA0zoMrJqc4vYPZRUnI7l7nw==", "dev": true, "license": "MIT", "dependencies": { "@bcoe/v8-coverage": "^1.0.2", - "@vitest/utils": "4.0.18", - "ast-v8-to-istanbul": "^0.3.10", + "@vitest/utils": "4.1.8", + "ast-v8-to-istanbul": "^1.0.0", "istanbul-lib-coverage": "^3.2.2", "istanbul-lib-report": "^3.0.1", "istanbul-reports": "^3.2.0", - "magicast": "^0.5.1", + "magicast": "^0.5.2", "obug": "^2.1.1", - "std-env": "^3.10.0", - "tinyrainbow": "^3.0.3" + "std-env": "^4.0.0-rc.1", + "tinyrainbow": "^3.1.0" }, "funding": { "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "@vitest/browser": "4.0.18", - "vitest": "4.0.18" + "@vitest/browser": "4.1.8", + "vitest": "4.1.8" }, "peerDependenciesMeta": { "@vitest/browser": { @@ -3413,31 +3413,31 @@ } }, "node_modules/@vitest/expect": { - "version": "4.0.18", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.0.18.tgz", - "integrity": "sha512-8sCWUyckXXYvx4opfzVY03EOiYVxyNrHS5QxX3DAIi5dpJAAkyJezHCP77VMX4HKA2LDT/Jpfo8i2r5BE3GnQQ==", + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.8.tgz", + "integrity": "sha512-h3nDO677RDLEGlBxyQ5CW8RlMThSKSRLUePLOx09gNIWRL40edgA1GCZSZgf1W55MFAG6/Sw14KeaAnqv0NKdQ==", "dev": true, "license": "MIT", "dependencies": { - "@standard-schema/spec": "^1.0.0", + "@standard-schema/spec": "^1.1.0", "@types/chai": "^5.2.2", - "@vitest/spy": "4.0.18", - "@vitest/utils": "4.0.18", - "chai": "^6.2.1", - "tinyrainbow": "^3.0.3" + "@vitest/spy": "4.1.8", + "@vitest/utils": "4.1.8", + "chai": "^6.2.2", + "tinyrainbow": "^3.1.0" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/mocker": { - "version": "4.0.18", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.0.18.tgz", - "integrity": "sha512-HhVd0MDnzzsgevnOWCBj5Otnzobjy5wLBe4EdeeFGv8luMsGcYqDuFRMcttKWZA5vVO8RFjexVovXvAM4JoJDQ==", + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.8.tgz", + "integrity": "sha512-LEiN/xe4OSIbKe9HQIp5OC24agGD9J5CnmMgsLohVVoOPWL9a2sBoR6VBx43jQZb7Kr1l4RCuyCJzcAa0+dojw==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "4.0.18", + "@vitest/spy": "4.1.8", "estree-walker": "^3.0.3", "magic-string": "^0.30.21" }, @@ -3446,7 +3446,7 @@ }, "peerDependencies": { "msw": "^2.4.9", - "vite": "^6.0.0 || ^7.0.0-0" + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { "msw": { @@ -3458,26 +3458,26 @@ } }, "node_modules/@vitest/pretty-format": { - "version": "4.0.18", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.0.18.tgz", - "integrity": "sha512-P24GK3GulZWC5tz87ux0m8OADrQIUVDPIjjj65vBXYG17ZeU3qD7r+MNZ1RNv4l8CGU2vtTRqixrOi9fYk/yKw==", + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.8.tgz", + "integrity": "sha512-9GasEBxpZ1VYIpqHf/0+YGg121uSNwCKOJqIrTwWP/TB7DmFCiaBpNl3aPZzoLWfWkuqhbH8vJIVobZkvdo2cA==", "dev": true, "license": "MIT", "dependencies": { - "tinyrainbow": "^3.0.3" + "tinyrainbow": "^3.1.0" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/runner": { - "version": "4.0.18", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.0.18.tgz", - "integrity": "sha512-rpk9y12PGa22Jg6g5M3UVVnTS7+zycIGk9ZNGN+m6tZHKQb7jrP7/77WfZy13Y/EUDd52NDsLRQhYKtv7XfPQw==", + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.8.tgz", + "integrity": "sha512-EmVxeBAfMJvycdjd6Hm+RbFBbA9fKvo0Kx37hNpBYoYeavH3RNsBXWDooR1mgD52dCrxIIuP7UotpfiwOikvcg==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/utils": "4.0.18", + "@vitest/utils": "4.1.8", "pathe": "^2.0.3" }, "funding": { @@ -3485,13 +3485,14 @@ } }, "node_modules/@vitest/snapshot": { - "version": "4.0.18", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.0.18.tgz", - "integrity": "sha512-PCiV0rcl7jKQjbgYqjtakly6T1uwv/5BQ9SwBLekVg/EaYeQFPiXcgrC2Y7vDMA8dM1SUEAEV82kgSQIlXNMvA==", + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.8.tgz", + "integrity": "sha512-acfZboRmAIf05DEKcBQy33VXojFJjtUdLyo7oOmV9kebb2xdU01UknNiPuPZoJZQyO7DF0gZdTGTpeAzET9QPQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "4.0.18", + "@vitest/pretty-format": "4.1.8", + "@vitest/utils": "4.1.8", "magic-string": "^0.30.21", "pathe": "^2.0.3" }, @@ -3500,9 +3501,9 @@ } }, "node_modules/@vitest/spy": { - "version": "4.0.18", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.0.18.tgz", - "integrity": "sha512-cbQt3PTSD7P2OARdVW3qWER5EGq7PHlvE+QfzSC0lbwO+xnt7+XH06ZzFjFRgzUX//JmpxrCu92VdwvEPlWSNw==", + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.8.tgz", + "integrity": "sha512-6EevtBp6OZOPF7bmz36HrGMeP3txgVSrgebWxHOafDXGkhIzfXK14f8KF6MuFfgXXUeHxmpD3BQxkV00/3s5mA==", "dev": true, "license": "MIT", "funding": { @@ -3510,36 +3511,37 @@ } }, "node_modules/@vitest/ui": { - "version": "4.0.18", - "resolved": "https://registry.npmjs.org/@vitest/ui/-/ui-4.0.18.tgz", - "integrity": "sha512-CGJ25bc8fRi8Lod/3GHSvXRKi7nBo3kxh0ApW4yCjmrWmRmlT53B5E08XRSZRliygG0aVNxLrBEqPYdz/KcCtQ==", + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@vitest/ui/-/ui-4.1.8.tgz", + "integrity": "sha512-RUS2ZU2TsduVrI+9c12uTNaKrNUTsm6yFt3fueEUB9iKvyC2UP83F+sqIz00HQIah4UOL1TMoDAki8K0NjGvsA==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/utils": "4.0.18", + "@vitest/utils": "4.1.8", "fflate": "^0.8.2", - "flatted": "^3.3.3", + "flatted": "^3.4.2", "pathe": "^2.0.3", "sirv": "^3.0.2", "tinyglobby": "^0.2.15", - "tinyrainbow": "^3.0.3" + "tinyrainbow": "^3.1.0" }, "funding": { "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "vitest": "4.0.18" + "vitest": "4.1.8" } }, "node_modules/@vitest/utils": { - "version": "4.0.18", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.0.18.tgz", - "integrity": "sha512-msMRKLMVLWygpK3u2Hybgi4MNjcYJvwTb0Ru09+fOyCXIgT5raYP041DRRdiJiI3k/2U6SEbAETB3YtBrUkCFA==", + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.8.tgz", + "integrity": "sha512-uOJamYALNhfJ6iolExyQM40yIQwDqYnkKtQ5VCiSe17E33H0aQ/u+1GlRuz4LZBk6Mm3sg90G9hEbmEt37C1Zg==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "4.0.18", - "tinyrainbow": "^3.0.3" + "@vitest/pretty-format": "4.1.8", + "convert-source-map": "^2.0.0", + "tinyrainbow": "^3.1.0" }, "funding": { "url": "https://opencollective.com/vitest" @@ -4050,9 +4052,9 @@ } }, "node_modules/ast-v8-to-istanbul": { - "version": "0.3.11", - "resolved": "https://registry.npmjs.org/ast-v8-to-istanbul/-/ast-v8-to-istanbul-0.3.11.tgz", - "integrity": "sha512-Qya9fkoofMjCBNVdWINMjB5KZvkYfaO9/anwkWnjxibpWUxo5iHl2sOdP7/uAqaRuUYuoo8rDwnbaaKVFxoUvw==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/ast-v8-to-istanbul/-/ast-v8-to-istanbul-1.0.3.tgz", + "integrity": "sha512-jCMQ6ZylLPudp0CDfBmQBZUsrh1/8psbmu9ibeVWKuHWD0YrH9YABwlKu5kVEFoT0GCQQW9Z/SxfuEbbkGQCRg==", "dev": true, "license": "MIT", "dependencies": { @@ -5848,9 +5850,9 @@ } }, "node_modules/es-module-lexer": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", - "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.1.0.tgz", + "integrity": "sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==", "dev": true, "license": "MIT" }, @@ -6413,9 +6415,9 @@ } }, "node_modules/fflate": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", - "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==", + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.3.tgz", + "integrity": "sha512-tbZNuJrLwGUp3zshBtdy4W+ORxZuIh8a5ilyIEQDC5rY1f3U20JMry0Ll3WBzU58EZKsEuJFXhb5gwv8CsPvgA==", "dev": true, "license": "MIT" }, @@ -6510,9 +6512,9 @@ } }, "node_modules/flatted": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", - "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", + "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", "dev": true, "license": "ISC" }, @@ -8393,14 +8395,14 @@ } }, "node_modules/magicast": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.5.1.tgz", - "integrity": "sha512-xrHS24IxaLrvuo613F719wvOIv9xPHFWQHuvGUBmPnCA/3MQxKI3b+r7n1jAoDHmsbC5bRhTZYR77invLAxVnw==", + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.5.3.tgz", + "integrity": "sha512-pVKE4UdSQ7DvHzivsCIFx2BJn1mHG6KsyrFcaxFx6tONdneEuThrDx0Cj3AMg58KyN4pzYT+LHOotxDQDjNvkw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/parser": "^7.28.5", - "@babel/types": "^7.28.5", + "@babel/parser": "^7.29.3", + "@babel/types": "^7.29.0", "source-map-js": "^1.2.1" } }, @@ -9114,15 +9116,18 @@ } }, "node_modules/obug": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.1.tgz", - "integrity": "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.2.tgz", + "integrity": "sha512-AWGB9WFcRXOQs48Z/udjI5ZcZMHXwX8XPByNpOydgcGsDLIzjGizhoMWJyKAWze7AVW/2W1i+/gPX4YtKe5cyg==", "dev": true, "funding": [ "https://github.com/sponsors/sxzz", "https://opencollective.com/debug" ], - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">=12.20.0" + } }, "node_modules/once": { "version": "1.4.0", @@ -10673,9 +10678,9 @@ } }, "node_modules/std-env": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz", - "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.1.0.tgz", + "integrity": "sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ==", "dev": true, "license": "MIT" }, @@ -11080,9 +11085,9 @@ "license": "MIT" }, "node_modules/tinyexec": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.2.tgz", - "integrity": "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.2.4.tgz", + "integrity": "sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==", "dev": true, "license": "MIT", "engines": { @@ -11135,9 +11140,9 @@ } }, "node_modules/tinyrainbow": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.0.3.tgz", - "integrity": "sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.1.0.tgz", + "integrity": "sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==", "dev": true, "license": "MIT", "engines": { @@ -11624,31 +11629,31 @@ } }, "node_modules/vitest": { - "version": "4.0.18", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.0.18.tgz", - "integrity": "sha512-hOQuK7h0FGKgBAas7v0mSAsnvrIgAvWmRFjmzpJ7SwFHH3g1k2u37JtYwOwmEKhK6ZO3v9ggDBBm0La1LCK4uQ==", + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.8.tgz", + "integrity": "sha512-flY6ScbCIt9HThs+C5HS7jvGOB560DJtk/Z15IQROTA6zEy49Nh8T/dofWTQL+n3vswqn87sbJNiuqw1SDp5Ig==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/expect": "4.0.18", - "@vitest/mocker": "4.0.18", - "@vitest/pretty-format": "4.0.18", - "@vitest/runner": "4.0.18", - "@vitest/snapshot": "4.0.18", - "@vitest/spy": "4.0.18", - "@vitest/utils": "4.0.18", - "es-module-lexer": "^1.7.0", - "expect-type": "^1.2.2", + "@vitest/expect": "4.1.8", + "@vitest/mocker": "4.1.8", + "@vitest/pretty-format": "4.1.8", + "@vitest/runner": "4.1.8", + "@vitest/snapshot": "4.1.8", + "@vitest/spy": "4.1.8", + "@vitest/utils": "4.1.8", + "es-module-lexer": "^2.0.0", + "expect-type": "^1.3.0", "magic-string": "^0.30.21", "obug": "^2.1.1", "pathe": "^2.0.3", "picomatch": "^4.0.3", - "std-env": "^3.10.0", + "std-env": "^4.0.0-rc.1", "tinybench": "^2.9.0", "tinyexec": "^1.0.2", "tinyglobby": "^0.2.15", - "tinyrainbow": "^3.0.3", - "vite": "^6.0.0 || ^7.0.0", + "tinyrainbow": "^3.1.0", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0", "why-is-node-running": "^2.3.0" }, "bin": { @@ -11664,12 +11669,15 @@ "@edge-runtime/vm": "*", "@opentelemetry/api": "^1.9.0", "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", - "@vitest/browser-playwright": "4.0.18", - "@vitest/browser-preview": "4.0.18", - "@vitest/browser-webdriverio": "4.0.18", - "@vitest/ui": "4.0.18", + "@vitest/browser-playwright": "4.1.8", + "@vitest/browser-preview": "4.1.8", + "@vitest/browser-webdriverio": "4.1.8", + "@vitest/coverage-istanbul": "4.1.8", + "@vitest/coverage-v8": "4.1.8", + "@vitest/ui": "4.1.8", "happy-dom": "*", - "jsdom": "*" + "jsdom": "*", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { "@edge-runtime/vm": { @@ -11690,6 +11698,12 @@ "@vitest/browser-webdriverio": { "optional": true }, + "@vitest/coverage-istanbul": { + "optional": true + }, + "@vitest/coverage-v8": { + "optional": true + }, "@vitest/ui": { "optional": true }, @@ -11698,13 +11712,16 @@ }, "jsdom": { "optional": true + }, + "vite": { + "optional": false } } }, "node_modules/vitest/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true, "license": "MIT", "engines": { diff --git a/package.json b/package.json index d0b1993..2542926 100644 --- a/package.json +++ b/package.json @@ -60,8 +60,8 @@ "@types/react-dom": "^19.1.2", "@types/react-syntax-highlighter": "^15.5.13", "@vitejs/plugin-react": "^4.3.4", - "@vitest/coverage-v8": "^4.0.18", - "@vitest/ui": "^4.0.18", + "@vitest/coverage-v8": "^4.1.8", + "@vitest/ui": "^4.1.8", "electron": "^35.7.5", "electron-builder": "^26.8.1", "electron-vite": "^3.1.0", @@ -75,6 +75,6 @@ "react-dom": "^19.1.0", "typescript": "^5.8.3", "vite": "^6.4.2", - "vitest": "^4.0.18" + "vitest": "^4.1.8" } } From c6c3e353cb4161b81111e2b8f5e9b79179429ca5 Mon Sep 17 00:00:00 2001 From: Dan Essig Date: Sun, 7 Jun 2026 15:46:05 -0700 Subject: [PATCH 4/8] build(deps): bump electron 35.7.5 -> 42.3.3 (#115) Major bump (7 majors). Validated empirically: - No API breaking changes 36-42 affect this app (clipboard handled in main via IPC, not renderer; modern setWindowOpenHandler already used; no printer/notification/utility-process/OSR usage). - Toolchain electron-version-agnostic (electron-vite, electron-builder, electron-updater); no native runtime modules. - lint, typecheck, build all green. - Full Playwright e2e suite: 18/18 pass (clipboard text+image, context menu, settings, theme, tools launcher, templates). Note: E42 no longer downloads its binary in postinstall; it fetches on first run. Vulnerability count dropped 14 -> 8. --- package-lock.json | 142 +++++++++++++++++++++++++++++++++++----------- package.json | 2 +- 2 files changed, 110 insertions(+), 34 deletions(-) diff --git a/package-lock.json b/package-lock.json index 87da5bb..1551116 100644 --- a/package-lock.json +++ b/package-lock.json @@ -38,7 +38,7 @@ "@vitejs/plugin-react": "^4.3.4", "@vitest/coverage-v8": "^4.1.8", "@vitest/ui": "^4.1.8", - "electron": "^35.7.5", + "electron": "^42.3.3", "electron-builder": "^26.8.1", "electron-vite": "^3.1.0", "eslint": "^9.24.0", @@ -753,24 +753,47 @@ } }, "node_modules/@electron/get": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@electron/get/-/get-2.0.3.tgz", - "integrity": "sha512-Qkzpg2s9GnVV2I2BjRksUi43U5e6+zaQMcjoJy0C+C5oxaKl+fmckGDQFtRpZpZV0NQekuZZ+tGz7EA9TVnQtQ==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@electron/get/-/get-5.0.0.tgz", + "integrity": "sha512-pjoBpru1KdEtcExBnuHAP1cAc/5faoedw0hzJkL3o4/IJp7HNF1+fbrdxT3gMYRX2oJfvnA/WXeCTVQpYYxyJA==", "license": "MIT", "dependencies": { "debug": "^4.1.1", - "env-paths": "^2.2.0", - "fs-extra": "^8.1.0", - "got": "^11.8.5", + "env-paths": "^3.0.0", + "graceful-fs": "^4.2.11", "progress": "^2.0.3", - "semver": "^6.2.0", + "semver": "^7.6.3", "sumchecker": "^3.0.1" }, "engines": { - "node": ">=12" + "node": ">=22.12.0" }, "optionalDependencies": { - "global-agent": "^3.0.0" + "undici": "^7.24.4" + } + }, + "node_modules/@electron/get/node_modules/env-paths": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-3.0.0.tgz", + "integrity": "sha512-dtJUTepzMW3Lm/NPxRf3wP4642UWhjL2sQxc+ym2YMj1m/H2zDNQOlezafzkHwn6sMstjHTwG6iQQsctDW/b1A==", + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@electron/get/node_modules/semver": { + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.2.tgz", + "integrity": "sha512-c8jsqUZm3omBOI66G90z1Dyw5z622G8oLG+omfsHBJf3CWQTlOcwOjvOG6wtiNfW6anKm/eA39LMwMtMez2TiQ==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" } }, "node_modules/@electron/notarize": { @@ -2447,6 +2470,7 @@ "version": "4.6.0", "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", + "dev": true, "license": "MIT", "engines": { "node": ">=10" @@ -2466,6 +2490,7 @@ "version": "4.0.6", "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==", + "dev": true, "license": "MIT", "dependencies": { "defer-to-connect": "^2.0.0" @@ -2910,6 +2935,7 @@ "version": "6.0.3", "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz", "integrity": "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==", + "dev": true, "license": "MIT", "dependencies": { "@types/http-cache-semantics": "*", @@ -2975,6 +3001,7 @@ "version": "4.0.4", "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz", "integrity": "sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==", + "dev": true, "license": "MIT" }, "node_modules/@types/json-schema": { @@ -2988,6 +3015,7 @@ "version": "3.1.4", "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz", "integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==", + "dev": true, "license": "MIT", "dependencies": { "@types/node": "*" @@ -3004,6 +3032,7 @@ "version": "22.16.0", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.16.0.tgz", "integrity": "sha512-B2egV9wALML1JCpv3VQoQ+yesQKAmNMBIAY7OteVrikcOcAkWm+dGL6qpeCktPjAv6N1JLnhbNiqS35UpFyBsQ==", + "devOptional": true, "license": "MIT", "dependencies": { "undici-types": "~6.21.0" @@ -3061,6 +3090,7 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.3.tgz", "integrity": "sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==", + "dev": true, "license": "MIT", "dependencies": { "@types/node": "*" @@ -4194,6 +4224,7 @@ "resolved": "https://registry.npmjs.org/boolean/-/boolean-3.2.0.tgz", "integrity": "sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==", "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", + "dev": true, "license": "MIT", "optional": true }, @@ -4465,6 +4496,7 @@ "version": "5.0.4", "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==", + "dev": true, "license": "MIT", "engines": { "node": ">=10.6.0" @@ -4474,6 +4506,7 @@ "version": "7.0.4", "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.4.tgz", "integrity": "sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==", + "dev": true, "license": "MIT", "dependencies": { "clone-response": "^1.0.2", @@ -4734,6 +4767,7 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz", "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==", + "dev": true, "license": "MIT", "dependencies": { "mimic-response": "^1.0.0" @@ -5038,6 +5072,7 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "dev": true, "license": "MIT", "dependencies": { "mimic-response": "^3.1.0" @@ -5053,6 +5088,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "dev": true, "license": "MIT", "engines": { "node": ">=10" @@ -5085,6 +5121,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", + "dev": true, "license": "MIT", "engines": { "node": ">=10" @@ -5157,6 +5194,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", + "dev": true, "license": "MIT", "optional": true }, @@ -5366,21 +5404,21 @@ } }, "node_modules/electron": { - "version": "35.7.5", - "resolved": "https://registry.npmjs.org/electron/-/electron-35.7.5.tgz", - "integrity": "sha512-dnL+JvLraKZl7iusXTVTGYs10TKfzUi30uEDTqsmTm0guN9V2tbOjTzyIZbh9n3ygUjgEYyo+igAwMRXIi3IPw==", - "hasInstallScript": true, + "version": "42.3.3", + "resolved": "https://registry.npmjs.org/electron/-/electron-42.3.3.tgz", + "integrity": "sha512-0MwYp9wTb7TrtTalOYqeW+suqd9T/Znstr/nDLKqFGIjHdBZX339guo3mQqTPURRZ/UQmYM4uMpzKpI5wLptfQ==", "license": "MIT", "dependencies": { - "@electron/get": "^2.0.0", - "@types/node": "^22.7.7", + "@electron/get": "^5.0.0", + "@types/node": "^24.9.0", "extract-zip": "^2.0.1" }, "bin": { - "electron": "cli.js" + "electron": "cli.js", + "install-electron": "install.js" }, "engines": { - "node": ">= 12.20.55" + "node": ">= 22.12.0" } }, "node_modules/electron-builder": { @@ -5666,6 +5704,21 @@ "node": ">=6 <7 || >=8" } }, + "node_modules/electron/node_modules/@types/node": { + "version": "24.13.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.13.1.tgz", + "integrity": "sha512-RSpUJGmvsJ1ZeBehQZFhIdpsz+bIpES0nIQXko4Ybq+N+kX6XvOq3Jo+iJ82FWLdblFq85AsMikd3m35jgezYg==", + "license": "MIT", + "dependencies": { + "undici-types": "~7.18.0" + } + }, + "node_modules/electron/node_modules/undici-types": { + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz", + "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==", + "license": "MIT" + }, "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -5723,6 +5776,7 @@ "version": "2.2.1", "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -5916,6 +5970,7 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", + "dev": true, "license": "MIT", "optional": true }, @@ -5973,7 +6028,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">=10" @@ -6588,20 +6643,6 @@ "node": ">=0.4.x" } }, - "node_modules/fs-extra": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", - "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - }, - "engines": { - "node": ">=6 <7 || >=8" - } - }, "node_modules/fs-minipass": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-3.0.3.tgz", @@ -6815,6 +6856,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/global-agent/-/global-agent-3.0.0.tgz", "integrity": "sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==", + "dev": true, "license": "BSD-3-Clause", "optional": true, "dependencies": { @@ -6833,6 +6875,7 @@ "version": "7.7.2", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true, "license": "ISC", "optional": true, "bin": { @@ -6887,6 +6930,7 @@ "version": "11.8.6", "resolved": "https://registry.npmjs.org/got/-/got-11.8.6.tgz", "integrity": "sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==", + "dev": true, "license": "MIT", "dependencies": { "@sindresorhus/is": "^4.0.0", @@ -7111,6 +7155,7 @@ "version": "4.2.0", "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==", + "dev": true, "license": "BSD-2-Clause" }, "node_modules/http-proxy-agent": { @@ -7131,6 +7176,7 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz", "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==", + "dev": true, "license": "MIT", "dependencies": { "quick-lru": "^5.1.1", @@ -7951,6 +7997,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, "license": "MIT" }, "node_modules/json-schema-traverse": { @@ -7971,6 +8018,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", + "dev": true, "license": "ISC", "optional": true }, @@ -7991,6 +8039,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "dev": true, "license": "MIT", "optionalDependencies": { "graceful-fs": "^4.1.6" @@ -8016,6 +8065,7 @@ "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, "license": "MIT", "dependencies": { "json-buffer": "3.0.1" @@ -8345,6 +8395,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -8462,6 +8513,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz", "integrity": "sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==", + "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -8561,6 +8613,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", + "dev": true, "license": "MIT", "engines": { "node": ">=4" @@ -8989,6 +9042,7 @@ "version": "6.1.0", "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", + "dev": true, "license": "MIT", "engines": { "node": ">=10" @@ -9217,6 +9271,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -9779,6 +9834,7 @@ "version": "5.1.1", "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", + "dev": true, "license": "MIT", "engines": { "node": ">=10" @@ -10045,6 +10101,7 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==", + "dev": true, "license": "MIT" }, "node_modules/resolve-from": { @@ -10061,6 +10118,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz", "integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==", + "dev": true, "license": "MIT", "dependencies": { "lowercase-keys": "^2.0.0" @@ -10123,6 +10181,7 @@ "version": "2.15.4", "resolved": "https://registry.npmjs.org/roarr/-/roarr-2.15.4.tgz", "integrity": "sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==", + "dev": true, "license": "BSD-3-Clause", "optional": true, "dependencies": { @@ -10319,6 +10378,7 @@ "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -10328,6 +10388,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", "integrity": "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==", + "dev": true, "license": "MIT", "optional": true }, @@ -10335,6 +10396,7 @@ "version": "7.0.1", "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-7.0.1.tgz", "integrity": "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==", + "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -10644,6 +10706,7 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", + "dev": true, "license": "BSD-3-Clause", "optional": true }, @@ -11278,6 +11341,7 @@ "version": "0.13.1", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz", "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==", + "dev": true, "license": "(MIT OR CC0-1.0)", "optional": true, "engines": { @@ -11416,10 +11480,21 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/undici": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.27.2.tgz", + "integrity": "sha512-uZsKNuzQxDMUY6M3pIMvy5tvlGmtq8XJ2oLAkfRKGNu+1VQAIvLy2xIVG5ATZl5wDXl/tddByAWCizRbOme+TA==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=20.18.1" + } + }, "node_modules/undici-types": { "version": "6.21.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "devOptional": true, "license": "MIT" }, "node_modules/unique-filename": { @@ -11452,6 +11527,7 @@ "version": "0.1.2", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true, "license": "MIT", "engines": { "node": ">= 4.0.0" diff --git a/package.json b/package.json index 2542926..b312cd8 100644 --- a/package.json +++ b/package.json @@ -62,7 +62,7 @@ "@vitejs/plugin-react": "^4.3.4", "@vitest/coverage-v8": "^4.1.8", "@vitest/ui": "^4.1.8", - "electron": "^35.7.5", + "electron": "^42.3.3", "electron-builder": "^26.8.1", "electron-vite": "^3.1.0", "eslint": "^9.24.0", From 81618c9ce1f668136899572ba73e94184bd81081 Mon Sep 17 00:00:00 2001 From: Dan Essig Date: Sun, 7 Jun 2026 21:17:32 -0700 Subject: [PATCH 5/8] 1.8.6 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1551116..1e001cd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "clipless", - "version": "1.8.5", + "version": "1.8.6", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "clipless", - "version": "1.8.5", + "version": "1.8.6", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index b312cd8..3d88138 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "clipless", - "version": "1.8.5", + "version": "1.8.6", "description": "An Electron application with React and TypeScript", "main": "./out/main/index.js", "author": "Daniel Essig", From 9a19aa4a5bfbd2aa54a66784a9c0931b9220a082 Mon Sep 17 00:00:00 2001 From: Dan Essig Date: Sun, 7 Jun 2026 21:27:00 -0700 Subject: [PATCH 6/8] lint format --- README.md | 46 +- e2e/context-menu.spec.ts | 9 +- screenshots/README.md | 16 +- screenshots/capture.spec.ts | 4 +- screenshots/fixtures/demo-data.ts | 21 +- screenshots/helpers.ts | 5 +- site/404.html | 128 ++-- site/app.js | 118 +-- site/docs/index.html | 1114 ++++++++++++++++----------- site/download/index.html | 544 +++++++++----- site/index.html | 1164 +++++++++++++++++++++-------- site/styles.css | 789 ++++++++++++++----- 12 files changed, 2702 insertions(+), 1256 deletions(-) diff --git a/README.md b/README.md index fc59d99..354cd7b 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ tickets and URLs in what you copy and turning them into one-click actions.

-**✓ Windows, macOS & Linux · ✓ Encrypted local storage · ✓ No account needed** +**✓ Windows, macOS & Linux · ✓ Encrypted local storage · ✓ No account needed**
@@ -98,13 +98,13 @@ patterns, pick your tools, and launch them all at once — no retyping, no tab j Real-time clipboard monitoring with 250ms polling, intelligent format prioritization, and duplicate prevention — all running quietly in the background so it never interrupts your flow. -| Format | What you get | -| --- | --- | -| **Text** | Plain text with automatic programming-language detection | -| **HTML** | Rich HTML content with visual indicators | -| **RTF** | Full Rich Text Format support | -| **Images** | Image clipboard data with preview and generated thumbnails | -| **Bookmarks** | URLs with titles (macOS / Windows compatible) | +| Format | What you get | +| ------------- | ---------------------------------------------------------- | +| **Text** | Plain text with automatic programming-language detection | +| **HTML** | Rich HTML content with visual indicators | +| **RTF** | Full Rich Text Format support | +| **Images** | Image clipboard data with preview and generated thumbnails | +| **Bookmarks** | URLs with titles (macOS / Windows compatible) | - **Lock clips** to keep important items from rotating out of history - **Clip Quick Search** to filter your entire history instantly @@ -164,8 +164,8 @@ Clipless shines anywhere people copy the same kinds of data all day — but noth is locked to one job. Define your own patterns and tools, and it bends to whatever workflow you throw at it. -| ☎️ Call center & support | 🗂️ Data entry & admin | 🛡️ Security & research | -| --- | --- | --- | +| ☎️ Call center & support | 🗂️ Data entry & admin | 🛡️ Security & research | +| ----------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- | | Copy a customer email or account number and open it across your CRM, billing and knowledge-base tools in a single move. | Bridge legacy and modern systems — extract reference numbers, populate templates and process records in batches without retyping. | Pull IPs, domains and hashes from any text and fan them out to VirusTotal, AbuseIPDB and the rest of your toolkit instantly. | > **…and whatever you do next.** Custom regex patterns, custom tools, custom templates — @@ -262,10 +262,10 @@ or the **[GitHub releases](https://github.com/dantheuber/clipless/releases)**. macOS builds aren't code-signed yet. This causes `.dmg` files downloaded from releases to not immediately work. The sticking point is **Gatekeeper**: anything downloaded through a browser gets a `com.apple.quarantine` flag, and macOS refuses to open a quarantined, unsigned app — usually -with *"Clipless is damaged and can't be opened."* The DMG isn't actually damaged; clearing that +with _"Clipless is damaged and can't be opened."_ The DMG isn't actually damaged; clearing that flag (or building locally, where it's never applied) fixes it. Two ways to install: -**Option 1 — Download a `.dmg` and clear the quarantine flag** *(recommended)* +**Option 1 — Download a `.dmg` and clear the quarantine flag** _(recommended)_ 1. Get the Clipless `.dmg` for your Mac from a [release](https://github.com/dantheuber/clipless/releases) — `arm64` for Apple Silicon, `x64` for Intel (or a build artifact) 2. Open it and drag **Clipless** into your **Applications** folder @@ -275,7 +275,7 @@ flag (or building locally, where it's never applied) fixes it. Two ways to insta ``` 4. Launch Clipless normally — Gatekeeper will let it through -**Option 2 — Build it yourself** *(no Terminal step on a downloaded file)* +**Option 2 — Build it yourself** _(no Terminal step on a downloaded file)_ A DMG you compile locally never receives the quarantine flag, so it installs straight away. You'll need the **Xcode Command Line Tools** (`xcode-select --install`) and **Node.js 20+**. @@ -347,16 +347,16 @@ npm run dev # start with hot reload ### Scripts -| Command | What it does | -| --- | --- | -| `npm run dev` | Start development with hot reload | -| `npm run build` | Type-check and build all processes | -| `npm run lint` | ESLint with caching | -| `npm run format` | Prettier formatting | -| `npm run typecheck` | Type-check all TypeScript | -| `npm test` / `npx vitest` | Unit tests (Vitest) | -| `npx playwright test` | E2E tests (Playwright + Electron) | -| `npm run build:win` · `build:mac` · `build:linux` | Platform-specific packaging | +| Command | What it does | +| ------------------------------------------------- | ---------------------------------- | +| `npm run dev` | Start development with hot reload | +| `npm run build` | Type-check and build all processes | +| `npm run lint` | ESLint with caching | +| `npm run format` | Prettier formatting | +| `npm run typecheck` | Type-check all TypeScript | +| `npm test` / `npx vitest` | Unit tests (Vitest) | +| `npx playwright test` | E2E tests (Playwright + Electron) | +| `npm run build:win` · `build:mac` · `build:linux` | Platform-specific packaging | > **Heads up:** E2E tests interact with your **system clipboard** — they read from and write > to it. Avoid copying sensitive data before running them, and expect your clipboard contents diff --git a/e2e/context-menu.spec.ts b/e2e/context-menu.spec.ts index 4b795fa..f589271 100644 --- a/e2e/context-menu.spec.ts +++ b/e2e/context-menu.spec.ts @@ -60,12 +60,9 @@ test.describe('Context Menu', () => { test('right-click on a clip further down the list shows context menu fully visible', async () => { // Add several more clips to push items down the list for (let i = 0; i < 8; i++) { - await app.evaluate( - async ({ clipboard }, t) => { - clipboard.writeText(t); - }, - `ctx-filler-${i}-${UNIQUE}` - ); + await app.evaluate(async ({ clipboard }, t) => { + clipboard.writeText(t); + }, `ctx-filler-${i}-${UNIQUE}`); await window.waitForTimeout(500); } await window.waitForTimeout(1000); diff --git a/screenshots/README.md b/screenshots/README.md index f403b15..9b6a4d3 100644 --- a/screenshots/README.md +++ b/screenshots/README.md @@ -22,14 +22,14 @@ npm run screenshots:only # skips the build (reuse an existing out/ build) PNGs are written to `screenshots/output/` (gitignored on the code branch). Each screen is captured in both light and dark themes at 2x device scale: -| File | Screen | -| ----------------------------- | ------------------------------------------- | -| `main-{light,dark}.png` | Main window with curated clips (hero) | -| `search-{light,dark}.png` | Clip search bar filtering the list | -| `settings-general-{…}.png` | Settings → General | -| `settings-tools-{…}.png` | Settings → Tools (Quick Clips / Templates) | -| `settings-hotkeys-{…}.png` | Settings → Hotkeys | -| `patterns-{light,dark}.png` | Tools Launcher: live Quick Clips matching | +| File | Screen | +| --------------------------- | ------------------------------------------ | +| `main-{light,dark}.png` | Main window with curated clips (hero) | +| `search-{light,dark}.png` | Clip search bar filtering the list | +| `settings-general-{…}.png` | Settings → General | +| `settings-tools-{…}.png` | Settings → Tools (Quick Clips / Templates) | +| `settings-hotkeys-{…}.png` | Settings → Hotkeys | +| `patterns-{light,dark}.png` | Tools Launcher: live Quick Clips matching | ## Publishing to the site diff --git a/screenshots/capture.spec.ts b/screenshots/capture.spec.ts index 247e52d..b058f8b 100644 --- a/screenshots/capture.spec.ts +++ b/screenshots/capture.spec.ts @@ -50,7 +50,9 @@ for (const theme of THEMES) { await settings.waitForTimeout(300); await shoot(settings, `settings-hotkeys-${theme}.png`); await page.evaluate(() => - (window as unknown as { api: { closeSettings: () => Promise } }).api.closeSettings() + ( + window as unknown as { api: { closeSettings: () => Promise } } + ).api.closeSettings() ); // 4. Quick Clips pattern matching in the Tools Launcher. diff --git a/screenshots/fixtures/demo-data.ts b/screenshots/fixtures/demo-data.ts index 4466193..457ae9d 100644 --- a/screenshots/fixtures/demo-data.ts +++ b/screenshots/fixtures/demo-data.ts @@ -86,10 +86,22 @@ export const SEARCH_TERMS: { name: string; pattern: string }[] = [ /** Quick Tools — URL templates keyed off the capture groups above. */ export const QUICK_TOOLS: { name: string; url: string; captureGroups: string[] }[] = [ - { name: 'VirusTotal — IP', url: 'https://www.virustotal.com/gui/ip-address/{ip}', captureGroups: ['ip'] }, + { + name: 'VirusTotal — IP', + url: 'https://www.virustotal.com/gui/ip-address/{ip}', + captureGroups: ['ip'], + }, { name: 'AbuseIPDB', url: 'https://www.abuseipdb.com/check/{ip}', captureGroups: ['ip'] }, - { name: 'Have I Been Pwned', url: 'https://haveibeenpwned.com/account/{email}', captureGroups: ['email'] }, - { name: 'Incident Dashboard', url: 'https://dashboard.example.com/incidents/{ticket}', captureGroups: ['ticket'] }, + { + name: 'Have I Been Pwned', + url: 'https://haveibeenpwned.com/account/{email}', + captureGroups: ['email'], + }, + { + name: 'Incident Dashboard', + url: 'https://dashboard.example.com/incidents/{ticket}', + captureGroups: ['ticket'], + }, ]; /** Templates — mix of named-token (matched) and positional (clip) templates. */ @@ -100,7 +112,8 @@ export const TEMPLATES: { name: string; content: string }[] = [ }, { name: 'Email Reply', - content: "Hi — regarding {email}, we've reviewed the recent activity from {ip} and will follow up shortly.", + content: + "Hi — regarding {email}, we've reviewed the recent activity from {ip} and will follow up shortly.", }, { name: 'Clip Digest', diff --git a/screenshots/helpers.ts b/screenshots/helpers.ts index 3cc3e1f..cba8c2c 100644 --- a/screenshots/helpers.ts +++ b/screenshots/helpers.ts @@ -83,7 +83,10 @@ export async function seed(app: ElectronApplication, page: Page, theme: Theme): // Pre-set the OS clipboard to the most-recent clip so the clipboard monitor // dedupes against it instead of injecting whatever is currently copied. - await app.evaluate(({ clipboard }, text) => clipboard.writeText(text), DEMO_CLIPS[0].clip.content); + await app.evaluate( + ({ clipboard }, text) => clipboard.writeText(text), + DEMO_CLIPS[0].clip.content + ); // Persist the curated clip list. The main `storage-save-clips` handler takes a // ClipItem[] plus a map of locked indices (it derives StoredClip metadata diff --git a/site/404.html b/site/404.html index 099af73..5bb5358 100644 --- a/site/404.html +++ b/site/404.html @@ -1,61 +1,75 @@ - + - - - - Page not found — Clipless - - - - - - - + - - -
-
-
-
404
-

This clip got cleared.

-

The page you're looking for isn't here. It may have moved, or never existed.

-
- Back to home - Read the docs + .nf .code { + font-family: var(--font-mono); + font-size: clamp(64px, 16vw, 140px); + font-weight: 600; + line-height: 1; + background: var(--accent-grad); + -webkit-background-clip: text; + background-clip: text; + color: transparent; + } + .nf h1 { + font-size: clamp(24px, 4vw, 36px); + margin-top: 18px; + } + .nf p { + color: var(--text-dim); + margin-top: 14px; + max-width: 420px; + } + .nf .hero-cta { + justify-content: center; + margin-top: 32px; + } + + + +
+
+
+
404
+

This clip got cleared.

+

The page you're looking for isn't here. It may have moved, or never existed.

+
-
-
- + + diff --git a/site/app.js b/site/app.js index b2b616d..81dc701 100644 --- a/site/app.js +++ b/site/app.js @@ -4,20 +4,24 @@ file wires up the toggle, nav, scroll reveal and docs TOC. ============================================================ */ (function () { - "use strict"; + 'use strict'; var root = document.documentElement; - var STORAGE_KEY = "clipless-theme"; + var STORAGE_KEY = 'clipless-theme'; /* ---- Theme: toggle, persist, follow system until a manual choice ---- */ function hasManualChoice() { - try { return !!localStorage.getItem(STORAGE_KEY); } catch (e) { return false; } + try { + return !!localStorage.getItem(STORAGE_KEY); + } catch (e) { + return false; + } } function applyThemeLabels(theme) { // Update any "Try light/dark mode" labels to name the *other* theme. - var other = theme === "dark" ? "light" : "dark"; - document.querySelectorAll("[data-theme-label]").forEach(function (el) { + var other = theme === 'dark' ? 'light' : 'dark'; + document.querySelectorAll('[data-theme-label]').forEach(function (el) { el.textContent = other; }); } @@ -25,81 +29,90 @@ function setTheme(theme, manual) { root.dataset.theme = theme; if (manual) { - try { localStorage.setItem(STORAGE_KEY, theme); } catch (e) {} + try { + localStorage.setItem(STORAGE_KEY, theme); + } catch (e) {} } applyThemeLabels(theme); } function toggleTheme() { - var next = root.dataset.theme === "dark" ? "light" : "dark"; + var next = root.dataset.theme === 'dark' ? 'light' : 'dark'; setTheme(next, true); } // Init labels for whatever theme the inline head script picked. - applyThemeLabels(root.dataset.theme || "dark"); + applyThemeLabels(root.dataset.theme || 'dark'); - document.querySelectorAll("[data-theme-toggle]").forEach(function (btn) { - btn.addEventListener("click", toggleTheme); + document.querySelectorAll('[data-theme-toggle]').forEach(function (btn) { + btn.addEventListener('click', toggleTheme); }); // Follow the OS theme until the user makes a manual choice this session. - var mq = window.matchMedia("(prefers-color-scheme: light)"); + var mq = window.matchMedia('(prefers-color-scheme: light)'); var onScheme = function (e) { - if (!hasManualChoice()) setTheme(e.matches ? "light" : "dark", false); + if (!hasManualChoice()) setTheme(e.matches ? 'light' : 'dark', false); }; - if (mq.addEventListener) mq.addEventListener("change", onScheme); + if (mq.addEventListener) mq.addEventListener('change', onScheme); else if (mq.addListener) mq.addListener(onScheme); /* ---- Nav: shadow on scroll ---- */ - var nav = document.querySelector(".nav"); + var nav = document.querySelector('.nav'); if (nav) { var onScroll = function () { - nav.classList.toggle("scrolled", window.scrollY > 8); + nav.classList.toggle('scrolled', window.scrollY > 8); }; onScroll(); - window.addEventListener("scroll", onScroll, { passive: true }); + window.addEventListener('scroll', onScroll, { passive: true }); } /* ---- Mobile nav drawer ---- */ - var navToggle = document.querySelector("[data-nav-toggle]"); - var navMobile = document.querySelector(".nav-mobile"); + var navToggle = document.querySelector('[data-nav-toggle]'); + var navMobile = document.querySelector('.nav-mobile'); if (navToggle && navMobile) { - navToggle.addEventListener("click", function () { - var open = navMobile.classList.toggle("open"); - navToggle.setAttribute("aria-expanded", open ? "true" : "false"); + navToggle.addEventListener('click', function () { + var open = navMobile.classList.toggle('open'); + navToggle.setAttribute('aria-expanded', open ? 'true' : 'false'); }); - navMobile.querySelectorAll("a").forEach(function (a) { - a.addEventListener("click", function () { - navMobile.classList.remove("open"); - navToggle.setAttribute("aria-expanded", "false"); + navMobile.querySelectorAll('a').forEach(function (a) { + a.addEventListener('click', function () { + navMobile.classList.remove('open'); + navToggle.setAttribute('aria-expanded', 'false'); }); }); } /* ---- Scroll reveal ---- */ - var reveal = document.querySelectorAll(".reveal:not(.in)"); - if (!("IntersectionObserver" in window)) { - reveal.forEach(function (el) { el.classList.add("in"); }); + var reveal = document.querySelectorAll('.reveal:not(.in)'); + if (!('IntersectionObserver' in window)) { + reveal.forEach(function (el) { + el.classList.add('in'); + }); } else { - var io = new IntersectionObserver(function (entries) { - entries.forEach(function (en) { - if (en.isIntersecting) { - en.target.classList.add("in"); - io.unobserve(en.target); - } - }); - }, { threshold: 0.12, rootMargin: "0px 0px -8% 0px" }); - reveal.forEach(function (el) { io.observe(el); }); + var io = new IntersectionObserver( + function (entries) { + entries.forEach(function (en) { + if (en.isIntersecting) { + en.target.classList.add('in'); + io.unobserve(en.target); + } + }); + }, + { threshold: 0.12, rootMargin: '0px 0px -8% 0px' } + ); + reveal.forEach(function (el) { + io.observe(el); + }); } /* ---- Docs: scroll-spy highlighting of the TOC ---- */ - var docsNav = document.querySelector(".docs-nav"); + var docsNav = document.querySelector('.docs-nav'); if (docsNav) { var links = Array.prototype.slice.call(docsNav.querySelectorAll("a[href^='#']")); var byId = {}; var sections = links .map(function (a) { - var id = a.getAttribute("href").slice(1); + var id = a.getAttribute('href').slice(1); var sec = document.getElementById(id); if (sec) byId[id] = a; return sec; @@ -111,18 +124,27 @@ if (id === current) return; current = id; links.forEach(function (a) { - a.classList.toggle("active", a.getAttribute("href") === "#" + id); + a.classList.toggle('active', a.getAttribute('href') === '#' + id); }); }; - var spy = new IntersectionObserver(function (entries) { - // Pick the heading nearest the top that is currently on screen. - var visible = entries - .filter(function (e) { return e.isIntersecting; }) - .sort(function (a, b) { return a.boundingClientRect.top - b.boundingClientRect.top; }); - if (visible.length) setActive(visible[0].target.id); - }, { rootMargin: "-80px 0px -70% 0px", threshold: 0 }); + var spy = new IntersectionObserver( + function (entries) { + // Pick the heading nearest the top that is currently on screen. + var visible = entries + .filter(function (e) { + return e.isIntersecting; + }) + .sort(function (a, b) { + return a.boundingClientRect.top - b.boundingClientRect.top; + }); + if (visible.length) setActive(visible[0].target.id); + }, + { rootMargin: '-80px 0px -70% 0px', threshold: 0 } + ); - sections.forEach(function (sec) { spy.observe(sec); }); + sections.forEach(function (sec) { + spy.observe(sec); + }); } })(); diff --git a/site/docs/index.html b/site/docs/index.html index e01acd0..6e8e855 100644 --- a/site/docs/index.html +++ b/site/docs/index.html @@ -1,448 +1,700 @@ - + - - - - Documentation — Clipless - - - - - - - - - - - - - - - - - - - -
- - - + +
+
+ + + + + Documentation +

Everything Clipless can do.

+

+ A complete reference for clipboard history, Quick Clips pattern extraction, the Tools + Launcher, templates, hotkeys and every setting. +

- - - -
-
- Documentation -

Everything Clipless can do.

-

- A complete reference for clipboard history, Quick Clips pattern extraction, - the Tools Launcher, templates, hotkeys and every setting. -

-
- -
- - - - -
-
-

Getting Started

-
    -
  1. Download and install Clipless for your platform
  2. -
  3. Launch the application — it starts monitoring your clipboard immediately
  4. -
  5. Look for the Clipless icon in your system tray
  6. -
  7. Copy content as you normally would; Clipless captures it automatically
  8. -
-
- -
-

Clipboard History

-

- Clipless polls the system clipboard every 250ms and stores new content - automatically. Supported formats: -

-
    -
  • Plain text with optional code language detection
  • -
  • HTML with rendered preview
  • -
  • RTF (rich text)
  • -
  • Images (screenshots, copied graphics)
  • -
  • Bookmarks (URLs with titles)
  • -
-

- Duplicate content is detected and ignored. Click a clip's row number (or - use a Quick Clip hotkey) to copy it back to the clipboard. -

- -

Editing Clips

-

- Click on any clip to expand it and edit its contents inline. When editing a - text clip, Clipless applies best-guess syntax highlighting based on the text - content, making it easier to work with code snippets and structured data. -

- -

Clip Actions

-

Each clip has a cog icon menu and a right-click context menu with actions including:

-
    -
  • Lock / Unlock — prevent a clip from being removed when the history limit is reached
  • -
  • Delete — remove a clip from history
  • -
  • Scan for Search Terms — scan a previous clip's content against your Quick Clips patterns and open the Tools Launcher with the results. This lets you use Quick Tools on any clip in your history, not just the most recent one.
  • -
-

Storage

-

Clipboard history is stored locally in an encrypted file (data.enc) using OS-native encryption:

+
+ +
- -
-

Quick Clips

-

- Quick Clips are user-defined regex patterns that extract data from clipboard - content. When copied text matches a pattern, the extracted values can be used - with Quick Tools and Templates. -

- -

How Patterns Work

-

- Each pattern is a regular expression with named capture groups. The capture - group names become tokens you can reference in Quick Tools URLs and Templates. -

-

Example pattern for extracting a customer number:

-
Customer #\s*(?<customerNumber>\d+)
-

When you copy text containing "Customer # 123456", Quick Clips extracts customerNumber = 123456.

- -

Pattern Templates

-

Clipless includes pattern templates you can add with one click in the Tools Manager:

-
- - - - - - - - - - - -
PatternCapture Group
Email Addressemail
URLurl
Domain NamedomainName
Phone NumberphoneNumber
IP Address (v4)ipAddress
MAC AddressmacAddress
IPv6 Addressipv6Address
-
-

These are suggestions, not active by default. Add the ones relevant to your work.

- -

Managing Patterns

-
    -
  1. Right-click the system tray icon and open Settings
  2. -
  3. Go to the Tools tab
  4. -
  5. Add patterns from templates or create custom ones with your own regex
  6. -
-
- -
-

Quick Tools

-

Quick Tools are URL templates that open web pages with data extracted by Quick Clips.

- -

Setup

-
    -
  1. Open Settings > Tools tab
  2. -
  3. Add a new Quick Tool with a name and URL template
  4. -
  5. Use capture group names as tokens in the URL
  6. -
- -

URL Templates

-

Tokens are capture group names wrapped in curly braces:

-
https://example.com/lookup?email={email}
+        
+
+        
+        
+
+

Getting Started

+
    +
  1. Download and install Clipless for your platform
  2. +
  3. Launch the application — it starts monitoring your clipboard immediately
  4. +
  5. Look for the Clipless icon in your system tray
  6. +
  7. Copy content as you normally would; Clipless captures it automatically
  8. +
+
+ +
+

Clipboard History

+

+ Clipless polls the system clipboard every 250ms and stores new content automatically. + Supported formats: +

+
    +
  • Plain text with optional code language detection
  • +
  • HTML with rendered preview
  • +
  • RTF (rich text)
  • +
  • Images (screenshots, copied graphics)
  • +
  • Bookmarks (URLs with titles)
  • +
+

+ Duplicate content is detected and ignored. Click a clip's row number (or use a Quick + Clip hotkey) to copy it back to the clipboard. +

+ +

Editing Clips

+

+ Click on any clip to expand it and edit its contents inline. When editing a text clip, + Clipless applies best-guess syntax highlighting based on the text content, making it + easier to work with code snippets and structured data. +

+ +

Clip Actions

+

+ Each clip has a cog icon menu and a right-click context menu with actions including: +

+
    +
  • + Lock / Unlock — prevent a clip from being removed when the history + limit is reached +
  • +
  • Delete — remove a clip from history
  • +
  • + Scan for Search Terms — scan a previous clip's content against your + Quick Clips patterns and open the Tools Launcher with the results. This lets you use + Quick Tools on any clip in your history, not just the most recent one. +
  • +
+ +

Storage

+

+ Clipboard history is stored locally in an encrypted file (data.enc) using + OS-native encryption: +

+
    +
  • Windows: DPAPI (Data Protection API)
  • +
  • macOS: Keychain Services
  • +
  • Linux: Secret Service API (GNOME Keyring / KDE Wallet)
  • +
+

No data is sent to any server.

+
+ +
+

Quick Clips

+

+ Quick Clips are user-defined regex patterns that extract data from clipboard content. + When copied text matches a pattern, the extracted values can be used with Quick Tools + and Templates. +

+ +

How Patterns Work

+

+ Each pattern is a regular expression with named capture groups. The capture group + names become tokens you can reference in Quick Tools URLs and Templates. +

+

Example pattern for extracting a customer number:

+
Customer #\s*(?<customerNumber>\d+)
+

+ When you copy text containing "Customer # 123456", Quick Clips extracts + customerNumber = 123456. +

+ +

Pattern Templates

+

+ Clipless includes pattern templates you can add with one click in the Tools Manager: +

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PatternCapture Group
Email Addressemail
URLurl
Domain NamedomainName
Phone NumberphoneNumber
IP Address (v4)ipAddress
MAC AddressmacAddress
IPv6 Addressipv6Address
+
+

These are suggestions, not active by default. Add the ones relevant to your work.

+ +

Managing Patterns

+
    +
  1. Right-click the system tray icon and open Settings
  2. +
  3. Go to the Tools tab
  4. +
  5. Add patterns from templates or create custom ones with your own regex
  6. +
+
+ +
+

Quick Tools

+

+ Quick Tools are URL templates that open web pages with data extracted by Quick Clips. +

+ +

Setup

+
    +
  1. Open Settings > Tools tab
  2. +
  3. Add a new Quick Tool with a name and URL template
  4. +
  5. Use capture group names as tokens in the URL
  6. +
+ +

URL Templates

+

Tokens are capture group names wrapped in curly braces:

+
https://example.com/lookup?email={email}
 https://tool.com/check?email={email}&domain={domainName}
-

A tool is available when its required tokens are present in the current Quick Clips matches.

- -

Usage

-

When Quick Clips detects patterns in a clip, you can:

-
    -
  • Click the scanner icon on the clip to see matches and open tools
  • -
  • Use the Tools Launcher window (Ctrl+Shift+T) for quick access
  • -
  • Use the cog icon or right-click menu on any clip and select "Scan for Search Terms" to scan older clips and open the Tools Launcher with their extracted data
  • -
- -

Example: Call Center Workflow

-

Define patterns for email and customer number, then create tools:

-
CRM Lookup:    https://crm.company.com/search?email={email}
+            

+ A tool is available when its required tokens are present in the current Quick Clips + matches. +

+ +

Usage

+

When Quick Clips detects patterns in a clip, you can:

+
    +
  • Click the scanner icon on the clip to see matches and open tools
  • +
  • Use the Tools Launcher window (Ctrl+Shift+T) for quick access
  • +
  • + Use the cog icon or right-click menu on any clip and select "Scan for Search Terms" + to scan older clips and open the Tools Launcher with their extracted data +
  • +
+ +

Example: Call Center Workflow

+

Define patterns for email and customer number, then create tools:

+
CRM Lookup:    https://crm.company.com/search?email={email}
 Account Info:  https://billing.company.com/customer/{customerNumber}
 Ticket Search: https://helpdesk.company.com/history/{email}
-

Copy a customer email, and all three tools become available in one click.

- -

Example: Data Entry

-

Define a pattern to extract an order number from pasted text, then create a tool:

-
Order Lookup: https://orders.company.com/view/{orderNumber}
-
- -
-

Templates Added in v1.6

-

Templates generate formatted text by substituting tokens with clipboard data.

- -

Token Types

-

Positional tokens reference clips by recency:

-
    -
  • {c1} — most recent clip
  • -
  • {c2} — second most recent
  • -
  • {c3} — third most recent, and so on
  • -
-

Named tokens reference Quick Clips capture group values:

-
    -
  • {email}, {domainName}, {phoneNumber}, etc.
  • -
- -

Example

-

Template:

-
Hello {c1},
+            

Copy a customer email, and all three tools become available in one click.

+ +

Example: Data Entry

+

Define a pattern to extract an order number from pasted text, then create a tool:

+
Order Lookup: https://orders.company.com/view/{orderNumber}
+
+ +
+

Templates Added in v1.6

+

Templates generate formatted text by substituting tokens with clipboard data.

+ +

Token Types

+

Positional tokens reference clips by recency:

+
    +
  • {c1} — most recent clip
  • +
  • {c2} — second most recent
  • +
  • {c3} — third most recent, and so on
  • +
+

Named tokens reference Quick Clips capture group values:

+
    +
  • + {email}, {domainName}, {phoneNumber}, etc. +
  • +
+ +

Example

+

Template:

+
Hello {c1},
 
 Your account ({email}) has been updated. Reference number: {customerNumber}.
 
 Regards
-

- This pulls the most recent clip for the greeting name, and uses Quick Clips - matches for the email and customer number. -

- -

Managing Templates

-

Templates are managed in Settings > Tools tab, alongside Quick Clips patterns and Quick Tools.

-
- - - -
-

Tools Launcher Window

-

The Tools Launcher is a dedicated window for quickly acting on clipboard content.

-
    -
  • Open with Ctrl+Shift+T (default hotkey)
  • -
  • Automatically scans the most recent clip against your Quick Clips patterns
  • -
  • Shows matched values and available Quick Tools
  • -
  • If already open, sending a new clip refreshes the content
  • -
-
- -
-

Global Hotkeys

-

All hotkeys are configurable in Settings > Hotkeys. Any hotkey can be rebound or disabled.

-
- - - - - - - - - - - - -
Default ShortcutAction
Ctrl+Shift+VToggle main window
Ctrl+Shift+1Copy most recent clip to clipboard
Ctrl+Shift+2Copy 2nd most recent clip
Ctrl+Shift+3Copy 3rd most recent clip
Ctrl+Shift+4Copy 4th most recent clip
Ctrl+Shift+5Copy 5th most recent clip
Ctrl+Shift+FToggle clip search bar
Ctrl+Shift+TOpen Tools Launcher
-
-

On macOS, Ctrl is replaced with Cmd.

-
- -
-

Settings Reference

- -

Application

-
- - - - - - - - - -
SettingDescriptionDefault
Maximum ClipsNumber of clips to retain (15–100)
Start MinimizedStart in system tray
Auto StartLaunch with OS
ThemeLight, Dark, or SystemSystem
Code DetectionDetect and highlight code languagesOn
-
- -

Window

-
- - - - - - - - - -
SettingDescriptionDefault
Window TransparencyEnable transparent windowOff
Transparency LevelOpacity percentage (0–100%)
Opaque When FocusedFull opacity when window is focusedOn
Always On TopKeep window above othersOff
Remember PositionRestore window position on launchOn
-
- -

Hotkeys

-

Configure key combinations for all 8 global hotkeys. Hotkeys can be individually enabled or disabled.

- -

Tools

-

Manage Quick Clips patterns, Quick Tools, and Templates.

-
- -
-

Code Detection

-

- When enabled (Settings > Application > Code Detection), Clipless detects - programming languages in text clips and applies syntax highlighting in the UI. -

-
- -
-

Security and Privacy

-
    -
  • All clipboard data is encrypted at rest using OS-native encryption
  • -
  • Data is stored locally only — no cloud sync, no remote servers
  • -
  • No telemetry, analytics, or tracking of any kind
  • -
  • The application is open source: github.com/dantheuber/clipless
  • -
-
- -
-

Workflow Examples

- -

Call Center Agent

-
    -
  1. Add Quick Clips patterns for email, phone number, and customer ID
  2. -
  3. Create Quick Tools pointing to your CRM, billing system, and helpdesk
  4. -
  5. When a customer provides their info, copy it once
  6. -
  7. Open the Tools Launcher (Ctrl+Shift+T) to see extracted data and launch lookups
  8. -
- -

Data Entry

-
    -
  1. Create Templates with positional tokens for standardized form text
  2. -
  3. Use Quick Clips patterns to extract structured data from source documents
  4. -
  5. Use the clipboard history (Ctrl+Shift+1 through 5) to quickly re-paste recent items
  6. -
-
- -
-

Troubleshooting

- -
- -

Still stuck? Check GitHub Issues for known problems, or create an issue with your platform and steps to reproduce.

-
- -

Clipboard not detecting content

-
    -
  • Verify Clipless is running (check system tray)
  • -
  • On macOS/Linux, check accessibility/permissions
  • -
  • Restart the application
  • -
- -

Patterns not matching

-
    -
  • Verify regex syntax in your custom patterns
  • -
  • Test the regex against your expected input
  • -
  • Ensure capture groups are named: (?<name>...)
  • -
+

+ This pulls the most recent clip for the greeting name, and uses Quick Clips matches + for the email and customer number. +

+ +

Managing Templates

+

+ Templates are managed in Settings > Tools tab, alongside Quick Clips patterns and + Quick Tools. +

+
+ + + +
+

Tools Launcher Window

+

The Tools Launcher is a dedicated window for quickly acting on clipboard content.

+
    +
  • Open with Ctrl+Shift+T (default hotkey)
  • +
  • Automatically scans the most recent clip against your Quick Clips patterns
  • +
  • Shows matched values and available Quick Tools
  • +
  • If already open, sending a new clip refreshes the content
  • +
+
+ +
+

Global Hotkeys

+

+ All hotkeys are configurable in Settings > Hotkeys. Any hotkey can be rebound or + disabled. +

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Default ShortcutAction
Ctrl+Shift+VToggle main window
Ctrl+Shift+1Copy most recent clip to clipboard
Ctrl+Shift+2Copy 2nd most recent clip
Ctrl+Shift+3Copy 3rd most recent clip
Ctrl+Shift+4Copy 4th most recent clip
Ctrl+Shift+5Copy 5th most recent clip
Ctrl+Shift+FToggle clip search bar
Ctrl+Shift+TOpen Tools Launcher
+
+

On macOS, Ctrl is replaced with Cmd.

+
+ +
+

Settings Reference

+ +

Application

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
SettingDescriptionDefault
Maximum ClipsNumber of clips to retain (15–100)
Start MinimizedStart in system tray
Auto StartLaunch with OS
ThemeLight, Dark, or SystemSystem
Code DetectionDetect and highlight code languagesOn
+
+ +

Window

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
SettingDescriptionDefault
Window TransparencyEnable transparent windowOff
Transparency LevelOpacity percentage (0–100%)
Opaque When FocusedFull opacity when window is focusedOn
Always On TopKeep window above othersOff
Remember PositionRestore window position on launchOn
+
+ +

Hotkeys

+

+ Configure key combinations for all 8 global hotkeys. Hotkeys can be individually + enabled or disabled. +

+ +

Tools

+

Manage Quick Clips patterns, Quick Tools, and Templates.

+
+ +
+

Code Detection

+

+ When enabled (Settings > Application > Code Detection), Clipless detects + programming languages in text clips and applies syntax highlighting in the UI. +

+
+ +
+

Security and Privacy

+
    +
  • All clipboard data is encrypted at rest using OS-native encryption
  • +
  • Data is stored locally only — no cloud sync, no remote servers
  • +
  • No telemetry, analytics, or tracking of any kind
  • +
  • + The application is open source: + github.com/dantheuber/clipless +
  • +
+
+ +
+

Workflow Examples

+ +

Call Center Agent

+
    +
  1. Add Quick Clips patterns for email, phone number, and customer ID
  2. +
  3. Create Quick Tools pointing to your CRM, billing system, and helpdesk
  4. +
  5. When a customer provides their info, copy it once
  6. +
  7. + Open the Tools Launcher (Ctrl+Shift+T) to see extracted data and launch lookups +
  8. +
+ +

Data Entry

+
    +
  1. Create Templates with positional tokens for standardized form text
  2. +
  3. Use Quick Clips patterns to extract structured data from source documents
  4. +
  5. + Use the clipboard history (Ctrl+Shift+1 through 5) to quickly re-paste recent items +
  6. +
+
+ +
+

Troubleshooting

+ +
+ + + +

+ Still stuck? Check + GitHub Issues + for known problems, or + create an issue + with your platform and steps to reproduce. +

+
+ +

Clipboard not detecting content

+
    +
  • Verify Clipless is running (check system tray)
  • +
  • On macOS/Linux, check accessibility/permissions
  • +
  • Restart the application
  • +
+ +

Patterns not matching

+
    +
  • Verify regex syntax in your custom patterns
  • +
  • Test the regex against your expected input
  • +
  • Ensure capture groups are named: (?<name>...)
  • +
+ +

Quick Tools not opening

+
    +
  • Check URL template syntax (tokens use {name} format)
  • +
  • Verify the web tool URL is accessible
  • +
  • Ensure the required capture groups are present in matched data
  • +
+
+
+
+
-

Quick Tools not opening

-
    -
  • Check URL template syntax (tokens use {name} format)
  • -
  • Verify the web tool URL is accessible
  • -
  • Ensure the required capture groups are present in matched data
  • -
- - - -
- - - - - - + + + + diff --git a/site/download/index.html b/site/download/index.html index b207c4c..e95af86 100644 --- a/site/download/index.html +++ b/site/download/index.html @@ -1,219 +1,375 @@ - + - - - - Download — Clipless - - - + + + + Download — Clipless + + + - - - - - - + + + + + + - - - - + + + + - - - -
+ + + +
- - + -
-
- Download -

Get Clipless.

-

- Free, open source, and ready for Windows, macOS and Linux. Grab the latest - signed-by-the-source build straight from GitHub Releases. -

-
- - - Download from GitHub Releases - +
+
+ + + + Download +

Get Clipless.

+

+ Free, open source, and ready for Windows, macOS and Linux. Grab the latest + signed-by-the-source build straight from GitHub Releases. +

+ +

+ + Latest release version + +

-

- - Latest release version - -

-
-
-
-

Platform support

-
-
- -

Windows 10 / 11

-
-
Format
.exe installer
-
Arch
x64 (64-bit)
-
-
-
- -

macOS

-
-
Format
.dmg disk image
-
Requires
macOS 10.15+ (Catalina)
-
Arch
Apple Silicon (arm64) & Intel (x64) — separate downloads
-
+
+
+

Platform support

+
+
+ +

Windows 10 / 11

+
+
Format
+
.exe installer
+
Arch
+
x64 (64-bit)
+
+
+
+ +

macOS

+
+
Format
+
.dmg disk image
+
Requires
+
macOS 10.15+ (Catalina)
+
Arch
+
Apple Silicon (arm64) & Intel (x64) — separate downloads
+
+
+
+ +

Linux

+
+
Format
+
.AppImage
+
Arch
+
x64 (64-bit)
+
+
-
- -

Linux

-
-
Format
.AppImage
-
Arch
x64 (64-bit)
-
-
-
-
+ -
-

Installation

+
+

Installation

-

Windows

-
    -
  1. Download the .exe file from GitHub Releases
  2. -
  3. Run the installer
  4. -
  5. If SmartScreen warns you, click "More info" then "Run anyway"
  6. -
  7. Find Clipless in your Start Menu or system tray
  8. -
+

Windows

+
    +
  1. Download the .exe file from GitHub Releases
  2. +
  3. Run the installer
  4. +
  5. If SmartScreen warns you, click "More info" then "Run anyway"
  6. +
  7. Find Clipless in your Start Menu or system tray
  8. +
-

macOS

-
    -
  1. Download the .dmg for your Mac from GitHub Releases — arm64 for Apple Silicon, x64 for Intel
  2. -
  3. Open the disk image and drag Clipless to Applications
  4. -
  5. If Gatekeeper blocks the app, go to System Settings > Privacy & Security and click "Open Anyway" (or right-click the app > Open)
  6. -
-

If you see "damaged and can't be opened", clear the quarantine attribute:

-
xattr -cr /Applications/Clipless.app
+

macOS

+
    +
  1. + Download the .dmg for your Mac from GitHub Releases — + arm64 for Apple Silicon, x64 for Intel +
  2. +
  3. Open the disk image and drag Clipless to Applications
  4. +
  5. + If Gatekeeper blocks the app, go to System Settings > Privacy & Security and + click "Open Anyway" (or right-click the app > Open) +
  6. +
+

If you see "damaged and can't be opened", clear the quarantine attribute:

+
xattr -cr /Applications/Clipless.app
-

Linux

-
    -
  1. Download the .AppImage file from GitHub Releases
  2. -
  3. Make it executable and run it
  4. -
-
chmod +x Clipless-*.AppImage
+          

Linux

+
    +
  1. Download the .AppImage file from GitHub Releases
  2. +
  3. Make it executable and run it
  4. +
+
chmod +x Clipless-*.AppImage
 ./Clipless-*.AppImage
-
+
-
-

Security warnings

-
- +
+

Security warnings

+
+ + + +

+ Clipless isn't signed with a commercial code-signing certificate, so your OS may show + a warning during installation. This is expected for open-source projects without paid + certificates — the app is built from + public source code. +

+
+
+ +
+

Updates

- Clipless isn't signed with a commercial code-signing certificate, so your OS - may show a warning during installation. This is expected for open-source - projects without paid certificates — the app is built from - public source code. + Clipless checks for updates automatically and notifies you when a new version is + available. To update manually, download the latest release and install over your + existing installation — settings and clipboard history are preserved.

-
-
+ -
-

Updates

-

- Clipless checks for updates automatically and notifies you when a new version - is available. To update manually, download the latest release and install over - your existing installation — settings and clipboard history are preserved. -

-
- -
-

Troubleshooting

-
    -
  • Windows: "App can't run on this PC" — ensure you're running 64-bit Windows.
  • -
  • macOS: "Damaged and can't be opened" — run xattr -cr /Applications/Clipless.app in Terminal.
  • -
  • Linux: "Permission denied" — run chmod +x Clipless-*.AppImage.
  • -
  • Antivirus blocking installation — add Clipless to your allowlist. Alerts are caused by the lack of code signing, not actual threats.
  • -
-

For other issues, check GitHub Issues.

-
-
-
+
+

Troubleshooting

+
    +
  • + Windows: "App can't run on this PC" — ensure you're running 64-bit + Windows. +
  • +
  • + macOS: "Damaged and can't be opened" — run + xattr -cr /Applications/Clipless.app in Terminal. +
  • +
  • + Linux: "Permission denied" — run + chmod +x Clipless-*.AppImage. +
  • +
  • + Antivirus blocking installation — add Clipless to your allowlist. + Alerts are caused by the lack of code signing, not actual threats. +
  • +
+

+ For other issues, check + GitHub Issues. +

+
+ + - - + - - + + diff --git a/site/index.html b/site/index.html index 59b9e77..8757af9 100644 --- a/site/index.html +++ b/site/index.html @@ -1,351 +1,903 @@ - + - - - - Clipless — the clipboard manager that reads - - - + + + + Clipless — the clipboard manager that reads + + + - - - - - - - + + + + + + + - - - - + + + + - - - - -
+ + + + +
- - + -
- -
-
-
- - Free & open source clipboard manager - -

The clipboard that reads between the lines.

-

- Clipless quietly remembers everything you copy, then reads it — spotting - emails, IPs, tickets and URLs and turning them into one-click actions. -

- -
- Windows, macOS & Linux - Encrypted local storage - No account needed +
+ +
+
+
+ + Free & open source clipboard manager + +

+ The clipboard that reads between the lines. +

+

+ Clipless quietly remembers everything you copy, then reads it — spotting emails, IPs, + tickets and URLs and turning them into one-click actions. +

+ +
+ + + + Windows, macOS & Linux + + + + Encrypted local storage + + + + No account needed +
-
-
-
-
Clipless — clipboard history
-
- Clipless main window listing copied clips, each flagged with a scanner icon - Clipless main window listing copied clips, each flagged with a scanner icon +
+
+
+
+
Clipless — clipboard history
+
+
+ Clipless main window listing copied clips, each flagged with a scanner icon + Clipless main window listing copied clips, each flagged with a scanner icon +
-
-
+
- -
-
-
- Quick Clips -

It reads what you copy.

-

- The moment something useful lands in your clipboard, Clipless flags it. - A scanner icon appears on the clip — open it to see every pattern it - pulled out, ready to act on. -

-
    -
  • Automatic detection — emails, IPs, URLs, phone numbers, ticket IDs and your own custom regex
  • -
  • Pick exactly what you need — each extracted value is individually selectable
  • -
  • Named capture groups — build patterns once and reuse them across every clip
  • -
-
- -
-
-
- Tools Launcher -

One copy. Every tool, open.

-

- Send the data Clipless found straight into the web tools you already use. - Select a few patterns, pick your tools, and launch them all at once — - no retyping, no tab juggling. -

-
    -
  • Multi-token URLs — drop values into any link, e.g. tool.com/{ip}/{email}
  • -
  • Open in bulk — fire off several lookups simultaneously with one click
  • -
  • Templates & sharing — match templates automatically and export configs for your team
  • -
-
-
-
-
Clipless — Tools Launcher
-
- Clipless Tools Launcher with found patterns on the left and matching web tools on the right - Clipless Tools Launcher with found patterns on the left and matching web tools on the right + +
+
+
+ + + + Tools Launcher +

One copy. Every tool, open.

+

+ Send the data Clipless found straight into the web tools you already use. Select a few + patterns, pick your tools, and launch them all at once — no retyping, no tab juggling. +

+
    +
  • + + Multi-token URLs + — drop values into any link, e.g. tool.com/{ip}/{email} +
  • +
  • + + + Open in bulk + — fire off several lookups simultaneously with one click +
  • +
  • + + + + + Templates & sharing + — match templates automatically and export configs for your team +
  • +
+
+
+
+
+
+
Clipless — Tools Launcher
+
+
+ Clipless Tools Launcher with found patterns on the left and matching web tools on the right + Clipless Tools Launcher with found patterns on the left and matching web tools on the right +
-
-
+ - -
-
-
- Light & Dark -

Looks right, day or night.

-

- Clipless follows your system theme out of the box and flips instantly when - you switch — with smooth, considered transitions instead of a jarring flash. - Prefer to set it yourself? One dropdown, done. -

-
    -
  • Matches your system — respects your OS light / dark preference automatically
  • -
  • Carefully tuned palettes — readable contrast and soft surfaces in both modes
  • -
-
- This whole site does it too — - + +
+
+
+ + + + Light & Dark +

Looks right, day or night.

+

+ Clipless follows your system theme out of the box and flips instantly when you switch + — with smooth, considered transitions instead of a jarring flash. Prefer to set it + yourself? One dropdown, done. +

+
    +
  • + + Matches your system + — respects your OS light / dark preference automatically +
  • +
  • + + + Carefully tuned palettes + — readable contrast and soft surfaces in both modes +
  • +
+
+ This whole site does it too — + +
-
-
-
-
Clipless — Settings
-
- Clipless settings window showing theme and general options in light mode - Clipless settings window showing theme and general options in dark mode +
+
+
+
+
Clipless — Settings
+
+
+ Clipless settings window showing theme and general options in light mode + Clipless settings window showing theme and general options in dark mode +
-
-
+
- -
-
-
- Who it's for -

Built for repetitive work — flexible for anything.

-

- Clipless shines anywhere people copy the same kinds of data all day. But - nothing about it is locked to one job — define your own patterns and tools, - and it bends to whatever workflow you throw at it. -

-
-
-
- -

Call centre & support

-

Copy a customer email or account number and open it across your CRM, billing and knowledge-base tools in a single move.

+ +
+
+
+ + + + + + Who it's for +

Built for repetitive work — flexible for anything.

+

+ Clipless shines anywhere people copy the same kinds of data all day. But nothing about + it is locked to one job — define your own patterns and tools, and it bends to whatever + workflow you throw at it. +

-
- -

Data entry & admin

-

Bridge legacy and modern systems — extract reference numbers, populate templates and process records in batches without retyping.

-
-
- -

Security & research

-

Pull IPs, domains and hashes from any text and fan them out to VirusTotal, AbuseIPDB and the rest of your toolkit instantly.

-
-
- -
-

…and whatever you do next.

+
+
+ + + + +

Call centre & support

+

+ Copy a customer email or account number and open it across your CRM, billing and + knowledge-base tools in a single move. +

+
+
+ + + + + +

Data entry & admin

- Custom regex patterns, custom tools, custom templates — Clipless is a - blank canvas with smart defaults. If you can describe the data and where - it should go, you can wire it up. No use case is too niche. + Bridge legacy and modern systems — extract reference numbers, populate templates and + process records in batches without retyping.

+
+ + + +

Security & research

+

+ Pull IPs, domains and hashes from any text and fan them out to VirusTotal, AbuseIPDB + and the rest of your toolkit instantly. +

+
+
+ + +
+

…and whatever you do next.

+

+ Custom regex patterns, custom tools, custom templates — Clipless is a blank canvas + with smart defaults. If you can describe the data and where it should go, you can + wire it up. No use case is too niche. +

+
+
-
-
+
- -
-
-
- More to love -

The quiet details

-
-
-
- -

Global hotkeys

-

Reach recent clips and the launcher from anywhere — even when Clipless is minimised.

-
-
- -

Encrypted storage

-

History is encrypted with DPAPI, Keychain or Secret Service and stays on your device.

+ +
+
+
+ + + + + More to love +

The quiet details

-
- -

Starts with you

-

Auto-launch on boot, start minimised to the tray, and update quietly in the background.

+
+
+ + + +

Global hotkeys

+

+ Reach recent clips and the launcher from anywhere — even when Clipless is minimised. +

+
+
+ + + +

Encrypted storage

+

+ History is encrypted with DPAPI, Keychain or Secret Service and stays on your + device. +

+
+
+ + + +

Starts with you

+

+ Auto-launch on boot, start minimised to the tray, and update quietly in the + background. +

+
-
-
+
- -
-
-
- Get Clipless -

Start copying smarter.

-

- Free, open source, and ready for Windows, macOS and Linux. Grab the latest - build or browse the source — it's all on GitHub. -

- -
- Windows - macOS - Linux + +
+
+
+ + + + Get Clipless +

Start copying smarter.

+

+ Free, open source, and ready for Windows, macOS and Linux. Grab the latest build or + browse the source — it's all on GitHub. +

+ +
+ + Windows + + macOS + + Linux +
+

+ Clipless isn't code-signed yet, so your OS may warn about an "unidentified developer." + It's safe — the build comes straight from the open-source repo. The + download page + explains how to install past the warning. +

-

- Clipless isn't code-signed yet, so your OS may warn about an "unidentified - developer." It's safe — the build comes straight from the open-source repo. - The download page explains how to install past the warning. -

-
-
-
+ + - - + - - + + diff --git a/site/styles.css b/site/styles.css index a9eb63d..bc7c98a 100644 --- a/site/styles.css +++ b/site/styles.css @@ -11,9 +11,9 @@ --accent-soft: rgba(59, 130, 246, 0.14); --accent-grad: linear-gradient(135deg, var(--accent), var(--accent-2)); - --font-display: "Space Grotesk", "Inter", system-ui, sans-serif; - --font-body: "Inter", system-ui, -apple-system, sans-serif; - --font-mono: "JetBrains Mono", "Menlo", ui-monospace, monospace; + --font-display: 'Space Grotesk', 'Inter', system-ui, sans-serif; + --font-body: 'Inter', system-ui, -apple-system, sans-serif; + --font-mono: 'JetBrains Mono', 'Menlo', ui-monospace, monospace; --maxw: 1180px; --radius: 16px; @@ -24,7 +24,7 @@ /* ---------- Dark (default) ---------- */ :root, -[data-theme="dark"] { +[data-theme='dark'] { --bg: #0c0c0e; --bg-grad-1: #121217; --surface: #161619; @@ -42,7 +42,7 @@ } /* ---------- Light ---------- */ -[data-theme="light"] { +[data-theme='light'] { --bg: #fbfbfd; --bg-grad-1: #f3f4f8; --surface: #ffffff; @@ -59,9 +59,13 @@ color-scheme: light; } -* { box-sizing: border-box; } +* { + box-sizing: border-box; +} -html { scroll-behavior: smooth; } +html { + scroll-behavior: smooth; +} body { margin: 0; @@ -71,15 +75,25 @@ body { line-height: 1.6; -webkit-font-smoothing: antialiased; text-rendering: optimizeLegibility; - transition: background-color 0.5s ease, color 0.5s ease; + transition: + background-color 0.5s ease, + color 0.5s ease; overflow-x: hidden; } -::selection { background: var(--accent); color: #fff; } +::selection { + background: var(--accent); + color: #fff; +} -a { color: inherit; text-decoration: none; } +a { + color: inherit; + text-decoration: none; +} -h1, h2, h3 { +h1, +h2, +h3 { font-family: var(--font-display); font-weight: 600; letter-spacing: -0.02em; @@ -88,7 +102,10 @@ h1, h2, h3 { text-wrap: balance; } -p { margin: 0; text-wrap: pretty; } +p { + margin: 0; + text-wrap: pretty; +} .wrap { width: 100%; @@ -106,7 +123,11 @@ p { margin: 0; text-wrap: pretty; } z-index: -1; background: radial-gradient(1100px 700px at 80% -8%, var(--accent-soft), transparent 60%), - radial-gradient(900px 600px at 0% 0%, color-mix(in oklab, var(--accent) 8%, transparent), transparent 55%), + radial-gradient( + 900px 600px at 0% 0%, + color-mix(in oklab, var(--accent) 8%, transparent), + transparent 55% + ), linear-gradient(180deg, var(--bg-grad-1), var(--bg) 38%); opacity: 1; transition: background 0.5s ease; @@ -123,9 +144,13 @@ p { margin: 0; text-wrap: pretty; } -webkit-backdrop-filter: blur(16px) saturate(160%); background: color-mix(in oklab, var(--bg) 72%, transparent); border-bottom: 1px solid transparent; - transition: border-color 0.3s ease, background 0.5s ease; + transition: + border-color 0.3s ease, + background 0.5s ease; +} +.nav.scrolled { + border-bottom-color: var(--border); } -.nav.scrolled { border-bottom-color: var(--border); } .nav-inner { height: 66px; display: flex; @@ -133,17 +158,23 @@ p { margin: 0; text-wrap: pretty; } justify-content: space-between; gap: 20px; } -.brand { display: flex; align-items: center; gap: 11px; } +.brand { + display: flex; + align-items: center; + gap: 11px; +} .brand .mark { - width: 30px; height: 30px; + width: 30px; + height: 30px; color: var(--text); - display: grid; place-items: center; + display: grid; + place-items: center; } .mark-img { display: block; background-color: currentColor; - -webkit-mask: url("assets/clipless-mark.png") center / contain no-repeat; - mask: url("assets/clipless-mark.png") center / contain no-repeat; + -webkit-mask: url('assets/clipless-mark.png') center / contain no-repeat; + mask: url('assets/clipless-mark.png') center / contain no-repeat; transition: background-color 0.5s ease; } .brand .name { @@ -163,8 +194,14 @@ p { margin: 0; text-wrap: pretty; } font-weight: 500; transition: color 0.18s ease; } -.nav-links a:hover { color: var(--text); } -.nav-right { display: flex; align-items: center; gap: 14px; } +.nav-links a:hover { + color: var(--text); +} +.nav-right { + display: flex; + align-items: center; + gap: 14px; +} /* Theme toggle */ .theme-toggle { @@ -172,19 +209,38 @@ p { margin: 0; text-wrap: pretty; } border: 1px solid var(--border); background: var(--surface); color: var(--text-dim); - width: 38px; height: 38px; + width: 38px; + height: 38px; border-radius: 11px; - display: grid; place-items: center; + display: grid; + place-items: center; cursor: pointer; - transition: color 0.18s, border-color 0.18s, background 0.18s, transform 0.4s cubic-bezier(.3,.8,.4,1); + transition: + color 0.18s, + border-color 0.18s, + background 0.18s, + transform 0.4s cubic-bezier(0.3, 0.8, 0.4, 1); +} +.theme-toggle:hover { + color: var(--text); + border-color: var(--border-strong); +} +.theme-toggle svg { + width: 18px; + height: 18px; } -.theme-toggle:hover { color: var(--text); border-color: var(--border-strong); } -.theme-toggle svg { width: 18px; height: 18px; } /* Icon crossfade by theme: show sun in dark (→ go light), moon in light */ -.icon-sun, .icon-moon { display: none; } -[data-theme="dark"] .icon-sun { display: block; } -[data-theme="light"] .icon-moon { display: block; } +.icon-sun, +.icon-moon { + display: none; +} +[data-theme='dark'] .icon-sun { + display: block; +} +[data-theme='light'] .icon-moon { + display: block; +} /* Buttons */ .btn { @@ -199,30 +255,56 @@ p { margin: 0; text-wrap: pretty; } align-items: center; gap: 9px; border: 1px solid transparent; - transition: transform 0.16s ease, box-shadow 0.2s ease, background 0.2s, border-color 0.2s, color 0.2s; + transition: + transform 0.16s ease, + box-shadow 0.2s ease, + background 0.2s, + border-color 0.2s, + color 0.2s; white-space: nowrap; } -.btn svg { width: 17px; height: 17px; } +.btn svg { + width: 17px; + height: 17px; +} .btn-primary { background: var(--accent-grad); color: #fff; box-shadow: 0 8px 24px -8px color-mix(in oklab, var(--accent) 70%, transparent); } -.btn-primary:hover { transform: translateY(-2px); box-shadow: 0 14px 30px -10px color-mix(in oklab, var(--accent) 75%, transparent); } +.btn-primary:hover { + transform: translateY(-2px); + box-shadow: 0 14px 30px -10px color-mix(in oklab, var(--accent) 75%, transparent); +} .btn-ghost { background: var(--surface); color: var(--text); border-color: var(--border); } -.btn-ghost:hover { border-color: var(--border-strong); transform: translateY(-2px); } -.btn-sm { padding: 9px 16px; font-size: 13.5px; border-radius: 10px; } -.btn-lg { padding: 14px 26px; font-size: 16px; border-radius: 13px; } +.btn-ghost:hover { + border-color: var(--border-strong); + transform: translateY(-2px); +} +.btn-sm { + padding: 9px 16px; + font-size: 13.5px; + border-radius: 10px; +} +.btn-lg { + padding: 14px 26px; + font-size: 16px; + border-radius: 13px; +} /* ============================================================ Section scaffolding ============================================================ */ -section { position: relative; } -.section-pad { padding: 96px 0; } +section { + position: relative; +} +.section-pad { + padding: 96px 0; +} .eyebrow { font-family: var(--font-mono); font-size: 12px; @@ -235,19 +317,32 @@ section { position: relative; } gap: 9px; } .eyebrow::before { - content: ""; - width: 22px; height: 1.5px; + content: ''; + width: 22px; + height: 1.5px; background: var(--accent); display: inline-block; } -.eyebrow svg { width: 15px; height: 15px; flex: none; } +.eyebrow svg { + width: 15px; + height: 15px; + flex: none; +} /* ============================================================ Hero ============================================================ */ -.hero { padding-top: 78px; padding-bottom: 40px; } -.hero-centered { text-align: center; } -.hero-centered .hero-copy { max-width: 760px; margin: 0 auto; } +.hero { + padding-top: 78px; + padding-bottom: 40px; +} +.hero-centered { + text-align: center; +} +.hero-centered .hero-copy { + max-width: 760px; + margin: 0 auto; +} .hero-pill { display: inline-flex; align-items: center; @@ -262,11 +357,16 @@ section { position: relative; } margin-bottom: 26px; } .hero-pill .dot { - width: 7px; height: 7px; border-radius: 50%; + width: 7px; + height: 7px; + border-radius: 50%; background: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); } -.hero-pill b { color: var(--text); font-weight: 600; } +.hero-pill b { + color: var(--text); + font-weight: 600; +} h1.hero-title { font-size: clamp(40px, 6.4vw, 76px); @@ -284,14 +384,19 @@ h1.hero-title { margin-top: 24px; max-width: 560px; } -.hero-centered .hero-sub { margin-left: auto; margin-right: auto; } +.hero-centered .hero-sub { + margin-left: auto; + margin-right: auto; +} .hero-cta { display: flex; gap: 14px; margin-top: 36px; flex-wrap: wrap; } -.hero-centered .hero-cta { justify-content: center; } +.hero-centered .hero-cta { + justify-content: center; +} .hero-trust { margin-top: 22px; font-size: 13px; @@ -300,9 +405,19 @@ h1.hero-title { gap: 16px; flex-wrap: wrap; } -.hero-centered .hero-trust { justify-content: center; } -.hero-trust span { display: inline-flex; align-items: center; gap: 7px; } -.hero-trust svg { width: 14px; height: 14px; color: var(--accent); } +.hero-centered .hero-trust { + justify-content: center; +} +.hero-trust span { + display: inline-flex; + align-items: center; + gap: 7px; +} +.hero-trust svg { + width: 14px; + height: 14px; + color: var(--accent); +} /* Hero layouts */ .hero-split .hero-grid { @@ -311,7 +426,9 @@ h1.hero-title { gap: 56px; align-items: center; } -.hero-centered .hero-shot-wrap { margin-top: 64px; } +.hero-centered .hero-shot-wrap { + margin-top: 64px; +} /* ============================================================ App window mockup @@ -323,7 +440,9 @@ h1.hero-title { border: 1px solid var(--border-strong); box-shadow: var(--shot-shadow); overflow: hidden; - transition: background 0.5s ease, border-color 0.5s ease; + transition: + background 0.5s ease, + border-color 0.5s ease; } .appwin-bar { display: flex; @@ -333,9 +452,14 @@ h1.hero-title { background: color-mix(in oklab, var(--surface) 70%, var(--surface-2)); border-bottom: 1px solid var(--border); } -.appwin-bar .dots { display: flex; gap: 7px; } +.appwin-bar .dots { + display: flex; + gap: 7px; +} .appwin-bar .dots i { - width: 11px; height: 11px; border-radius: 50%; + width: 11px; + height: 11px; + border-radius: 50%; background: var(--surface-3); border: 1px solid var(--border); } @@ -364,14 +488,23 @@ h1.hero-title { opacity: 0; transition: opacity 0.55s ease; } -[data-theme="dark"] .shot .shot-dark { opacity: 1; } -[data-theme="dark"] .shot .shot-light { opacity: 0; transition: opacity 0.55s ease; } -.shot .shot-light { transition: opacity 0.55s ease; } +[data-theme='dark'] .shot .shot-dark { + opacity: 1; +} +[data-theme='dark'] .shot .shot-light { + opacity: 0; + transition: opacity 0.55s ease; +} +.shot .shot-light { + transition: opacity 0.55s ease; +} /* floating glow behind hero shot */ -.hero-shot-wrap { position: relative; } +.hero-shot-wrap { + position: relative; +} .hero-shot-wrap::before { - content: ""; + content: ''; position: absolute; inset: -8% -4% 8%; background: var(--accent-grad); @@ -391,9 +524,15 @@ h1.hero-title { gap: 64px; align-items: center; } -.feature.reverse { grid-template-columns: 1.12fr 1fr; } -.feature.reverse .feature-copy { order: 2; } -.feature.reverse .feature-media { order: 1; } +.feature.reverse { + grid-template-columns: 1.12fr 1fr; +} +.feature.reverse .feature-copy { + order: 2; +} +.feature.reverse .feature-media { + order: 1; +} .feature-copy h2 { font-size: clamp(28px, 3.4vw, 42px); margin-top: 18px; @@ -419,19 +558,34 @@ h1.hero-title { } .feature-list .ic { flex: none; - width: 24px; height: 24px; + width: 24px; + height: 24px; border-radius: 8px; background: var(--accent-soft); color: var(--accent); - display: grid; place-items: center; + display: grid; + place-items: center; margin-top: 1px; } -.feature-list .ic svg { width: 14px; height: 14px; } -.feature-list b { font-weight: 600; } -.feature-list span.dim { color: var(--text-dim); font-weight: 400; } +.feature-list .ic svg { + width: 14px; + height: 14px; +} +.feature-list b { + font-weight: 600; +} +.feature-list span.dim { + color: var(--text-dim); + font-weight: 400; +} /* token chips for quick clips */ -.token-row { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 26px; } +.token-row { + display: flex; + gap: 8px; + flex-wrap: wrap; + margin-top: 26px; +} .token { font-family: var(--font-mono); font-size: 12.5px; @@ -444,7 +598,10 @@ h1.hero-title { gap: 7px; align-items: center; } -.token b { color: var(--accent); font-weight: 600; } +.token b { + color: var(--accent); + font-weight: 600; +} /* ============================================================ Theming demo callout @@ -475,17 +632,37 @@ h1.hero-title { display: inline-flex; gap: 8px; align-items: center; - transition: border-color 0.18s, transform 0.16s; + transition: + border-color 0.18s, + transform 0.16s; +} +.theme-demo button:hover { + border-color: var(--accent); + transform: translateY(-1px); +} +.theme-demo button svg { + width: 15px; + height: 15px; + color: var(--accent); } -.theme-demo button:hover { border-color: var(--accent); transform: translateY(-1px); } -.theme-demo button svg { width: 15px; height: 15px; color: var(--accent); } /* ============================================================ Who it's for ============================================================ */ -.usecase-head { max-width: 680px; margin: 0 auto; text-align: center; } -.usecase-head h2 { font-size: clamp(28px, 3.6vw, 44px); margin-top: 16px; } -.usecase-head p { font-size: 17.5px; color: var(--text-dim); margin-top: 18px; } +.usecase-head { + max-width: 680px; + margin: 0 auto; + text-align: center; +} +.usecase-head h2 { + font-size: clamp(28px, 3.6vw, 44px); + margin-top: 16px; +} +.usecase-head p { + font-size: 17.5px; + color: var(--text-dim); + margin-top: 18px; +} .usecase-grid { display: grid; grid-template-columns: repeat(3, 1fr); @@ -497,26 +674,47 @@ h1.hero-title { border: 1px solid var(--border); border-radius: var(--radius); padding: 28px 26px; - transition: border-color 0.25s ease, transform 0.25s ease, background 0.5s ease; + transition: + border-color 0.25s ease, + transform 0.25s ease, + background 0.5s ease; +} +.usecase:hover { + border-color: var(--border-strong); + transform: translateY(-4px); } -.usecase:hover { border-color: var(--border-strong); transform: translateY(-4px); } .usecase .uc-ic { - width: 46px; height: 46px; + width: 46px; + height: 46px; border-radius: 13px; background: var(--accent-soft); color: var(--accent); - display: grid; place-items: center; + display: grid; + place-items: center; margin-bottom: 20px; } -.usecase .uc-ic svg { width: 23px; height: 23px; } -.usecase h3 { font-size: 19px; } -.usecase p { font-size: 14.5px; color: var(--text-dim); margin-top: 11px; } +.usecase .uc-ic svg { + width: 23px; + height: 23px; +} +.usecase h3 { + font-size: 19px; +} +.usecase p { + font-size: 14.5px; + color: var(--text-dim); + margin-top: 11px; +} /* flexible / any-use-case banner */ .flexible { margin-top: 22px; grid-column: 1 / -1; - background: linear-gradient(120deg, color-mix(in oklab, var(--accent) 10%, var(--surface)), var(--surface)); + background: linear-gradient( + 120deg, + color-mix(in oklab, var(--accent) 10%, var(--surface)), + var(--surface) + ); border: 1px solid var(--border); border-radius: var(--radius); padding: 34px 38px; @@ -527,13 +725,28 @@ h1.hero-title { flex-wrap: wrap; } .flexible .fx-ic { - width: 52px; height: 52px; border-radius: 14px; flex: none; - background: var(--accent-grad); color: #fff; - display: grid; place-items: center; + width: 52px; + height: 52px; + border-radius: 14px; + flex: none; + background: var(--accent-grad); + color: #fff; + display: grid; + place-items: center; +} +.flexible .fx-ic svg { + width: 26px; + height: 26px; +} +.flexible h3 { + font-size: 22px; +} +.flexible p { + color: var(--text-dim); + font-size: 15px; + margin-top: 8px; + max-width: 640px; } -.flexible .fx-ic svg { width: 26px; height: 26px; } -.flexible h3 { font-size: 22px; } -.flexible p { color: var(--text-dim); font-size: 15px; margin-top: 8px; max-width: 640px; } /* ============================================================ More features strip @@ -549,18 +762,36 @@ h1.hero-title { border-radius: var(--radius); padding: 24px; background: var(--surface); - transition: border-color 0.25s, background 0.5s ease; + transition: + border-color 0.25s, + background 0.5s ease; +} +.more-card:hover { + border-color: var(--border-strong); } -.more-card:hover { border-color: var(--border-strong); } .more-card .m-ic { - width: 40px; height: 40px; border-radius: 11px; - background: var(--surface-2); border: 1px solid var(--border); + width: 40px; + height: 40px; + border-radius: 11px; + background: var(--surface-2); + border: 1px solid var(--border); color: var(--accent); - display: grid; place-items: center; margin-bottom: 16px; + display: grid; + place-items: center; + margin-bottom: 16px; +} +.more-card .m-ic svg { + width: 20px; + height: 20px; +} +.more-card h3 { + font-size: 16.5px; +} +.more-card p { + font-size: 13.5px; + color: var(--text-dim); + margin-top: 8px; } -.more-card .m-ic svg { width: 20px; height: 20px; } -.more-card h3 { font-size: 16.5px; } -.more-card p { font-size: 13.5px; color: var(--text-dim); margin-top: 8px; } /* ============================================================ Download @@ -570,27 +801,48 @@ h1.hero-title { border-radius: var(--radius-lg); border: 1px solid var(--border-strong); background: - radial-gradient(700px 360px at 50% -20%, var(--accent-soft), transparent 70%), - var(--surface); + radial-gradient(700px 360px at 50% -20%, var(--accent-soft), transparent 70%), var(--surface); padding: 72px 40px; text-align: center; overflow: hidden; } -.download-card h2 { font-size: clamp(30px, 4vw, 50px); } -.download-card p { font-size: 18px; color: var(--text-dim); margin: 18px auto 0; max-width: 540px; } -.download-card .hero-cta { justify-content: center; } +.download-card h2 { + font-size: clamp(30px, 4vw, 50px); +} +.download-card p { + font-size: 18px; + color: var(--text-dim); + margin: 18px auto 0; + max-width: 540px; +} +.download-card .hero-cta { + justify-content: center; +} .dl-platforms { - display: flex; gap: 22px; justify-content: center; flex-wrap: wrap; - margin-top: 30px; font-size: 13px; color: var(--text-faint); + display: flex; + gap: 22px; + justify-content: center; + flex-wrap: wrap; + margin-top: 30px; + font-size: 13px; + color: var(--text-faint); +} +.dl-platforms span { + display: inline-flex; + align-items: center; + gap: 7px; +} +.dl-platforms svg { + width: 15px; + height: 15px; } -.dl-platforms span { display: inline-flex; align-items: center; gap: 7px; } -.dl-platforms svg { width: 15px; height: 15px; } .dl-note { margin-top: 30px; font-size: 12.5px; color: var(--text-faint); max-width: 520px; - margin-left: auto; margin-right: auto; + margin-left: auto; + margin-right: auto; line-height: 1.55; } @@ -609,11 +861,26 @@ h1.hero-title { gap: 24px; flex-wrap: wrap; } -.footer .brand .name { font-size: 17px; } -.footer-links { display: flex; gap: 26px; } -.footer-links a { font-size: 14px; color: var(--text-dim); transition: color 0.18s; } -.footer-links a:hover { color: var(--text); } -.footer-fine { font-size: 12.5px; color: var(--text-faint); margin-top: 22px; } +.footer .brand .name { + font-size: 17px; +} +.footer-links { + display: flex; + gap: 26px; +} +.footer-links a { + font-size: 14px; + color: var(--text-dim); + transition: color 0.18s; +} +.footer-links a:hover { + color: var(--text); +} +.footer-fine { + font-size: 12.5px; + color: var(--text-faint); + margin-top: 22px; +} /* ============================================================ Scroll reveal @@ -621,44 +888,88 @@ h1.hero-title { .reveal { opacity: 0; transform: translateY(26px); - transition: opacity 0.7s cubic-bezier(.22,.61,.36,1), transform 0.7s cubic-bezier(.22,.61,.36,1); + transition: + opacity 0.7s cubic-bezier(0.22, 0.61, 0.36, 1), + transform 0.7s cubic-bezier(0.22, 0.61, 0.36, 1); will-change: opacity, transform; } -.reveal.in { opacity: 1; transform: none; } -.reveal.d1 { transition-delay: 0.08s; } -.reveal.d2 { transition-delay: 0.16s; } -.reveal.d3 { transition-delay: 0.24s; } +.reveal.in { + opacity: 1; + transform: none; +} +.reveal.d1 { + transition-delay: 0.08s; +} +.reveal.d2 { + transition-delay: 0.16s; +} +.reveal.d3 { + transition-delay: 0.24s; +} @media (prefers-reduced-motion: reduce) { - .reveal { opacity: 1; transform: none; transition: none; } - html { scroll-behavior: auto; } + .reveal { + opacity: 1; + transform: none; + transition: none; + } + html { + scroll-behavior: auto; + } } /* ============================================================ Responsive ============================================================ */ @media (max-width: 900px) { - .section-pad { padding: 70px 0; } - .hero-split .hero-grid { grid-template-columns: 1fr; gap: 44px; } - .feature, .feature.reverse { grid-template-columns: 1fr; gap: 36px; } - .feature.reverse .feature-copy { order: 1; } - .feature.reverse .feature-media { order: 2; } - .usecase-grid { grid-template-columns: 1fr; } - .more-grid { grid-template-columns: 1fr; } - .nav-links { display: none; } + .section-pad { + padding: 70px 0; + } + .hero-split .hero-grid { + grid-template-columns: 1fr; + gap: 44px; + } + .feature, + .feature.reverse { + grid-template-columns: 1fr; + gap: 36px; + } + .feature.reverse .feature-copy { + order: 1; + } + .feature.reverse .feature-media { + order: 2; + } + .usecase-grid { + grid-template-columns: 1fr; + } + .more-grid { + grid-template-columns: 1fr; + } + .nav-links { + display: none; + } } @media (max-width: 560px) { - .wrap { padding: 0 18px; } - .download-card { padding: 48px 22px; } - .flexible { padding: 26px; } + .wrap { + padding: 0 18px; + } + .download-card { + padding: 48px 22px; + } + .flexible { + padding: 26px; + } } /* ============================================================ Active nav link + mobile menu ============================================================ */ -.nav-links a.active { color: var(--text); } +.nav-links a.active { + color: var(--text); +} .nav-links a.active::after { - content: ""; + content: ''; display: block; height: 2px; margin-top: 4px; @@ -672,15 +983,23 @@ h1.hero-title { border: 1px solid var(--border); background: var(--surface); color: var(--text); - width: 38px; height: 38px; + width: 38px; + height: 38px; border-radius: 11px; cursor: pointer; align-items: center; justify-content: center; - transition: border-color 0.18s, background 0.18s; + transition: + border-color 0.18s, + background 0.18s; +} +.nav-toggle:hover { + border-color: var(--border-strong); +} +.nav-toggle svg { + width: 20px; + height: 20px; } -.nav-toggle:hover { border-color: var(--border-strong); } -.nav-toggle svg { width: 20px; height: 20px; } .nav-mobile { display: none; @@ -689,7 +1008,9 @@ h1.hero-title { backdrop-filter: blur(16px) saturate(160%); -webkit-backdrop-filter: blur(16px) saturate(160%); } -.nav-mobile.open { display: block; } +.nav-mobile.open { + display: block; +} .nav-mobile .wrap { display: flex; flex-direction: column; @@ -703,18 +1024,28 @@ h1.hero-title { color: var(--text-dim); padding: 11px 6px; border-radius: 9px; - transition: color 0.16s, background 0.16s; + transition: + color 0.16s, + background 0.16s; +} +.nav-mobile a:hover { + color: var(--text); + background: var(--surface-2); } -.nav-mobile a:hover { color: var(--text); background: var(--surface-2); } @media (max-width: 900px) { - .nav-toggle { display: inline-flex; } + .nav-toggle { + display: inline-flex; + } } /* ============================================================ Inner page header (docs / download) ============================================================ */ -.page-head { padding-top: 64px; padding-bottom: 40px; } +.page-head { + padding-top: 64px; + padding-bottom: 40px; +} .page-head h1 { font-size: clamp(34px, 5vw, 56px); margin-top: 18px; @@ -760,7 +1091,14 @@ h1.hero-title { color: var(--text-faint); padding: 0 12px 10px; } -.docs-nav ul { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 1px; } +.docs-nav ul { + list-style: none; + margin: 0; + padding: 0; + display: flex; + flex-direction: column; + gap: 1px; +} .docs-nav a { display: block; font-size: 14px; @@ -768,9 +1106,15 @@ h1.hero-title { padding: 7px 12px; border-radius: 8px; border-left: 2px solid transparent; - transition: color 0.16s, background 0.16s, border-color 0.16s; + transition: + color 0.16s, + background 0.16s, + border-color 0.16s; +} +.docs-nav a:hover { + color: var(--text); + background: var(--surface-2); } -.docs-nav a:hover { color: var(--text); background: var(--surface-2); } .docs-nav a.active { color: var(--accent); background: var(--accent-soft); @@ -779,25 +1123,49 @@ h1.hero-title { } /* ---- Prose ---- */ -.prose { max-width: 760px; } -.prose > section { scroll-margin-top: 88px; } +.prose { + max-width: 760px; +} +.prose > section { + scroll-margin-top: 88px; +} .prose h2 { font-size: clamp(24px, 3vw, 32px); margin-top: 8px; padding-top: 40px; scroll-margin-top: 88px; } -.prose section:first-child h2 { padding-top: 0; } +.prose section:first-child h2 { + padding-top: 0; +} .prose h3 { font-size: 19px; margin-top: 36px; scroll-margin-top: 88px; } -.prose p { color: var(--text-dim); margin-top: 16px; font-size: 15.5px; } -.prose ul, .prose ol { color: var(--text-dim); margin: 16px 0 0; padding-left: 22px; font-size: 15.5px; } -.prose li { margin-top: 8px; } -.prose li::marker { color: var(--text-faint); } -.prose strong, .prose b { color: var(--text); font-weight: 600; } +.prose p { + color: var(--text-dim); + margin-top: 16px; + font-size: 15.5px; +} +.prose ul, +.prose ol { + color: var(--text-dim); + margin: 16px 0 0; + padding-left: 22px; + font-size: 15.5px; +} +.prose li { + margin-top: 8px; +} +.prose li::marker { + color: var(--text-faint); +} +.prose strong, +.prose b { + color: var(--text); + font-weight: 600; +} .prose a { color: var(--accent); font-weight: 500; @@ -806,7 +1174,9 @@ h1.hero-title { text-underline-offset: 2px; transition: text-decoration-color 0.16s; } -.prose a:hover { text-decoration-color: var(--accent); } +.prose a:hover { + text-decoration-color: var(--accent); +} .prose hr { border: none; border-top: 1px solid var(--border); @@ -841,8 +1211,17 @@ h1.hero-title { } /* tables */ -.prose .table-wrap { margin-top: 20px; overflow-x: auto; border: 1px solid var(--border); border-radius: 12px; } -.prose table { width: 100%; border-collapse: collapse; font-size: 14px; } +.prose .table-wrap { + margin-top: 20px; + overflow-x: auto; + border: 1px solid var(--border); + border-radius: 12px; +} +.prose table { + width: 100%; + border-collapse: collapse; + font-size: 14px; +} .prose thead th { text-align: left; font-family: var(--font-body); @@ -858,8 +1237,12 @@ h1.hero-title { border-bottom: 1px solid var(--border); color: var(--text-dim); } -.prose tbody tr:last-child td { border-bottom: none; } -.prose tbody td code { font-size: 12.5px; } +.prose tbody tr:last-child td { + border-bottom: none; +} +.prose tbody td code { + font-size: 12.5px; +} /* "Added in vX" pill + callouts */ .added-in { @@ -888,13 +1271,28 @@ h1.hero-title { border-radius: 12px; padding: 16px 18px; } -.callout .ic { flex: none; color: var(--accent); margin-top: 1px; } -.callout .ic svg { width: 18px; height: 18px; } -.callout p { margin: 0; font-size: 14.5px; } +.callout .ic { + flex: none; + color: var(--accent); + margin-top: 1px; +} +.callout .ic svg { + width: 18px; + height: 18px; +} +.callout p { + margin: 0; + font-size: 14.5px; +} @media (max-width: 900px) { - .docs-layout { grid-template-columns: 1fr; gap: 0; } - .docs-nav { display: none; } + .docs-layout { + grid-template-columns: 1fr; + gap: 0; + } + .docs-nav { + display: none; + } } /* ============================================================ @@ -911,24 +1309,56 @@ h1.hero-title { border: 1px solid var(--border); border-radius: var(--radius); padding: 28px 26px; - transition: border-color 0.25s ease, transform 0.25s ease, background 0.5s ease; + transition: + border-color 0.25s ease, + transform 0.25s ease, + background 0.5s ease; +} +.platform-card:hover { + border-color: var(--border-strong); + transform: translateY(-4px); } -.platform-card:hover { border-color: var(--border-strong); transform: translateY(-4px); } .platform-card .pf-ic { - width: 46px; height: 46px; + width: 46px; + height: 46px; border-radius: 13px; background: var(--accent-soft); color: var(--accent); - display: grid; place-items: center; + display: grid; + place-items: center; margin-bottom: 18px; } -.platform-card .pf-ic svg { width: 24px; height: 24px; } -.platform-card h3 { font-size: 18px; } -.platform-card dl { margin: 14px 0 0; display: grid; grid-template-columns: auto 1fr; gap: 6px 12px; font-size: 13.5px; } -.platform-card dt { color: var(--text-faint); } -.platform-card dd { margin: 0; color: var(--text-dim); } +.platform-card .pf-ic svg { + width: 24px; + height: 24px; +} +.platform-card h3 { + font-size: 18px; +} +.platform-card dl { + margin: 14px 0 0; + display: grid; + grid-template-columns: auto 1fr; + gap: 6px 12px; + font-size: 13.5px; +} +.platform-card dt { + color: var(--text-faint); +} +.platform-card dd { + margin: 0; + color: var(--text-dim); +} -.install-steps { counter-reset: step; list-style: none; padding: 0; margin: 24px 0 0; display: flex; flex-direction: column; gap: 14px; } +.install-steps { + counter-reset: step; + list-style: none; + padding: 0; + margin: 24px 0 0; + display: flex; + flex-direction: column; + gap: 14px; +} .install-steps > li { position: relative; padding-left: 44px; @@ -942,17 +1372,22 @@ h1.hero-title { counter-increment: step; content: counter(step); position: absolute; - left: 0; top: 0; - width: 30px; height: 30px; + left: 0; + top: 0; + width: 30px; + height: 30px; border-radius: 9px; background: var(--accent-soft); color: var(--accent); font-family: var(--font-mono); font-weight: 600; font-size: 14px; - display: grid; place-items: center; + display: grid; + place-items: center; } @media (max-width: 900px) { - .platform-grid { grid-template-columns: 1fr; } + .platform-grid { + grid-template-columns: 1fr; + } } From 1ce78d6e9e46217401a5606c1a2df0561e7283fb Mon Sep 17 00:00:00 2001 From: Dan Essig Date: Sun, 7 Jun 2026 21:27:03 -0700 Subject: [PATCH 7/8] 1.8.7 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1e001cd..e2d5053 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "clipless", - "version": "1.8.6", + "version": "1.8.7", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "clipless", - "version": "1.8.6", + "version": "1.8.7", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 3d88138..b564005 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "clipless", - "version": "1.8.6", + "version": "1.8.7", "description": "An Electron application with React and TypeScript", "main": "./out/main/index.js", "author": "Daniel Essig", From cb8b2b6f8e08754fc2f35692e77e6e66fd29450a Mon Sep 17 00:00:00 2001 From: Dan Essig Date: Sun, 7 Jun 2026 21:27:23 -0700 Subject: [PATCH 8/8] Revert "1.8.7" This reverts commit 1ce78d6e9e46217401a5606c1a2df0561e7283fb. --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index e2d5053..1e001cd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "clipless", - "version": "1.8.7", + "version": "1.8.6", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "clipless", - "version": "1.8.7", + "version": "1.8.6", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index b564005..3d88138 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "clipless", - "version": "1.8.7", + "version": "1.8.6", "description": "An Electron application with React and TypeScript", "main": "./out/main/index.js", "author": "Daniel Essig",