diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index b8ae04a..3f3131a 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -31,4 +31,16 @@ jobs:
- name: π₯οΈ Build Electron app
# Run Electron build script from root directory
- run: npm run build
\ No newline at end of file
+
+ run: npm run build
+
+ - name: π¨ Send failure notification to Slack
+ if: failure()
+ uses: rtCamp/action-slack-notify@v2
+ env:
+ SLACK_CHANNEL: general
+ SLACK_TITLE: "π¨ Build Failed"
+ SLACK_MESSAGE: "π Build failed for `${{ github.repository }}` repo on main branch."
+ SLACK_COLOR: 'danger'
+ SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }}
+
diff --git a/README.md b/README.md
index 8397aec..e166914 100644
--- a/README.md
+++ b/README.md
@@ -117,12 +117,10 @@ We have a list of [help wanted](https://github.com/pickle-com/glass/issues?q=is%
| Status | Issue | Description |
|--------|--------------------------------|---------------------------------------------------|
-| π§ WIP | Code Refactoring | Refactoring the entire codebase for better maintainability. |
| π§ WIP | Windows Build | Make Glass buildable & runnable in Windows |
| π§ WIP | Local LLM Support | Supporting Local LLM to power AI answers |
| π§ WIP | AEC Improvement | Transcription is not working occasionally |
| π§ WIP | Firebase Data Storage Issue | Session & ask should be saved in firebase for signup users |
-| π§ WIP | Login Issue | Currently breaking when switching between local and sign-in mode |
| π§ WIP | Liquid Glass | Liquid Glass UI for MacOS 26 |
### Changelog
diff --git a/electron-builder.yml b/electron-builder.yml
index 35e6ed5..79b81fb 100644
--- a/electron-builder.yml
+++ b/electron-builder.yml
@@ -44,6 +44,8 @@ win:
- target: portable
arch: x64
requestedExecutionLevel: asInvoker
+ # Disable code signing to avoid symbolic link issues on Windows
+ signAndEditExecutable: false
# NSIS installer configuration for Windows
nsis:
diff --git a/package-lock.json b/package-lock.json
index edabde8..5ae800a 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -10,6 +10,9 @@
"hasInstallScript": true,
"license": "GPL-3.0",
"dependencies": {
+
+ "@anthropic-ai/sdk": "^0.56.0",
+
"@google/genai": "^1.8.0",
"@google/generative-ai": "^0.24.1",
"axios": "^1.10.0",
@@ -51,6 +54,17 @@
"electron-liquid-glass": "^1.0.1"
}
},
+
+ "node_modules/@anthropic-ai/sdk": {
+ "version": "0.56.0",
+ "resolved": "https://registry.npmjs.org/@anthropic-ai/sdk/-/sdk-0.56.0.tgz",
+ "integrity": "sha512-SLCB8M8+VMg1cpCucnA1XWHGWqVSZtIWzmOdDOEu3eTFZMB+A0sGZ1ESO5MHDnqrNTXz3safMrWx9x4rMZSOqA==",
+ "license": "MIT",
+ "bin": {
+ "anthropic-ai-sdk": "bin/cli"
+ }
+ },
+
"node_modules/@develar/schema-utils": {
"version": "2.6.5",
"resolved": "https://registry.npmjs.org/@develar/schema-utils/-/schema-utils-2.6.5.tgz",
@@ -875,9 +889,11 @@
}
},
"node_modules/@emnapi/runtime": {
- "version": "1.4.3",
- "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.3.tgz",
- "integrity": "sha512-pBPWdu6MLKROBX05wSNKcNb++m5Er+KQ9QkB+WVM+pW2Kx9hoSrVTnu3BdkI5eBLZoKu/J6mW/B6i6bJB2ytXQ==",
+
+ "version": "1.4.4",
+ "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.4.tgz",
+ "integrity": "sha512-hHyapA4A3gPaDCNfiqyZUStTMqIkKRshqPIuDOXv1hcBnD4U3l8cP0T1HMCfGRxQ6V64TGCcoswChANyOAwbQg==",
+
"license": "MIT",
"optional": true,
"dependencies": {
@@ -885,9 +901,11 @@
}
},
"node_modules/@esbuild/aix-ppc64": {
- "version": "0.25.5",
- "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.5.tgz",
- "integrity": "sha512-9o3TMmpmftaCMepOdA5k/yDw8SfInyzWWTjYTFCX3kPSDJMROQTb8jg+h9Cnwnmm1vOzvxN7gIfB5V2ewpjtGA==",
+
+ "version": "0.25.6",
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.6.tgz",
+ "integrity": "sha512-ShbM/3XxwuxjFiuVBHA+d3j5dyac0aEVVq1oluIDf71hUw0aRF59dV/efUsIwFnR6m8JNM2FjZOzmaZ8yG61kw==",
+
"cpu": [
"ppc64"
],
@@ -902,9 +920,11 @@
}
},
"node_modules/@esbuild/android-arm": {
- "version": "0.25.5",
- "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.5.tgz",
- "integrity": "sha512-AdJKSPeEHgi7/ZhuIPtcQKr5RQdo6OO2IL87JkianiMYMPbCtot9fxPbrMiBADOWWm3T2si9stAiVsGbTQFkbA==",
+
+ "version": "0.25.6",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.6.tgz",
+ "integrity": "sha512-S8ToEOVfg++AU/bHwdksHNnyLyVM+eMVAOf6yRKFitnwnbwwPNqKr3srzFRe7nzV69RQKb5DgchIX5pt3L53xg==",
+
"cpu": [
"arm"
],
@@ -919,9 +939,11 @@
}
},
"node_modules/@esbuild/android-arm64": {
- "version": "0.25.5",
- "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.5.tgz",
- "integrity": "sha512-VGzGhj4lJO+TVGV1v8ntCZWJktV7SGCs3Pn1GRWI1SBFtRALoomm8k5E9Pmwg3HOAal2VDc2F9+PM/rEY6oIDg==",
+
+ "version": "0.25.6",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.6.tgz",
+ "integrity": "sha512-hd5zdUarsK6strW+3Wxi5qWws+rJhCCbMiC9QZyzoxfk5uHRIE8T287giQxzVpEvCwuJ9Qjg6bEjcRJcgfLqoA==",
+
"cpu": [
"arm64"
],
@@ -936,9 +958,11 @@
}
},
"node_modules/@esbuild/android-x64": {
- "version": "0.25.5",
- "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.5.tgz",
- "integrity": "sha512-D2GyJT1kjvO//drbRT3Hib9XPwQeWd9vZoBJn+bu/lVsOZ13cqNdDeqIF/xQ5/VmWvMduP6AmXvylO/PIc2isw==",
+
+ "version": "0.25.6",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.6.tgz",
+ "integrity": "sha512-0Z7KpHSr3VBIO9A/1wcT3NTy7EB4oNC4upJ5ye3R7taCc2GUdeynSLArnon5G8scPwaU866d3H4BCrE5xLW25A==",
+
"cpu": [
"x64"
],
@@ -953,9 +977,11 @@
}
},
"node_modules/@esbuild/darwin-arm64": {
- "version": "0.25.5",
- "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.5.tgz",
- "integrity": "sha512-GtaBgammVvdF7aPIgH2jxMDdivezgFu6iKpmT+48+F8Hhg5J/sfnDieg0aeG/jfSvkYQU2/pceFPDKlqZzwnfQ==",
+
+ "version": "0.25.6",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.6.tgz",
+ "integrity": "sha512-FFCssz3XBavjxcFxKsGy2DYK5VSvJqa6y5HXljKzhRZ87LvEi13brPrf/wdyl/BbpbMKJNOr1Sd0jtW4Ge1pAA==",
+
"cpu": [
"arm64"
],
@@ -970,9 +996,11 @@
}
},
"node_modules/@esbuild/darwin-x64": {
- "version": "0.25.5",
- "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.5.tgz",
- "integrity": "sha512-1iT4FVL0dJ76/q1wd7XDsXrSW+oLoquptvh4CLR4kITDtqi2e/xwXwdCVH8hVHU43wgJdsq7Gxuzcs6Iq/7bxQ==",
+
+ "version": "0.25.6",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.6.tgz",
+ "integrity": "sha512-GfXs5kry/TkGM2vKqK2oyiLFygJRqKVhawu3+DOCk7OxLy/6jYkWXhlHwOoTb0WqGnWGAS7sooxbZowy+pK9Yg==",
+
"cpu": [
"x64"
],
@@ -987,9 +1015,11 @@
}
},
"node_modules/@esbuild/freebsd-arm64": {
- "version": "0.25.5",
- "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.5.tgz",
- "integrity": "sha512-nk4tGP3JThz4La38Uy/gzyXtpkPW8zSAmoUhK9xKKXdBCzKODMc2adkB2+8om9BDYugz+uGV7sLmpTYzvmz6Sw==",
+
+ "version": "0.25.6",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.6.tgz",
+ "integrity": "sha512-aoLF2c3OvDn2XDTRvn8hN6DRzVVpDlj2B/F66clWd/FHLiHaG3aVZjxQX2DYphA5y/evbdGvC6Us13tvyt4pWg==",
+
"cpu": [
"arm64"
],
@@ -1004,9 +1034,11 @@
}
},
"node_modules/@esbuild/freebsd-x64": {
- "version": "0.25.5",
- "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.5.tgz",
- "integrity": "sha512-PrikaNjiXdR2laW6OIjlbeuCPrPaAl0IwPIaRv+SMV8CiM8i2LqVUHFC1+8eORgWyY7yhQY+2U2fA55mBzReaw==",
+
+ "version": "0.25.6",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.6.tgz",
+ "integrity": "sha512-2SkqTjTSo2dYi/jzFbU9Plt1vk0+nNg8YC8rOXXea+iA3hfNJWebKYPs3xnOUf9+ZWhKAaxnQNUf2X9LOpeiMQ==",
+
"cpu": [
"x64"
],
@@ -1021,9 +1053,11 @@
}
},
"node_modules/@esbuild/linux-arm": {
- "version": "0.25.5",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.5.tgz",
- "integrity": "sha512-cPzojwW2okgh7ZlRpcBEtsX7WBuqbLrNXqLU89GxWbNt6uIg78ET82qifUy3W6OVww6ZWobWub5oqZOVtwolfw==",
+
+ "version": "0.25.6",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.6.tgz",
+ "integrity": "sha512-SZHQlzvqv4Du5PrKE2faN0qlbsaW/3QQfUUc6yO2EjFcA83xnwm91UbEEVx4ApZ9Z5oG8Bxz4qPE+HFwtVcfyw==",
+
"cpu": [
"arm"
],
@@ -1038,9 +1072,11 @@
}
},
"node_modules/@esbuild/linux-arm64": {
- "version": "0.25.5",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.5.tgz",
- "integrity": "sha512-Z9kfb1v6ZlGbWj8EJk9T6czVEjjq2ntSYLY2cw6pAZl4oKtfgQuS4HOq41M/BcoLPzrUbNd+R4BXFyH//nHxVg==",
+
+ "version": "0.25.6",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.6.tgz",
+ "integrity": "sha512-b967hU0gqKd9Drsh/UuAm21Khpoh6mPBSgz8mKRq4P5mVK8bpA+hQzmm/ZwGVULSNBzKdZPQBRT3+WuVavcWsQ==",
+
"cpu": [
"arm64"
],
@@ -1055,9 +1091,11 @@
}
},
"node_modules/@esbuild/linux-ia32": {
- "version": "0.25.5",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.5.tgz",
- "integrity": "sha512-sQ7l00M8bSv36GLV95BVAdhJ2QsIbCuCjh/uYrWiMQSUuV+LpXwIqhgJDcvMTj+VsQmqAHL2yYaasENvJ7CDKA==",
+
+ "version": "0.25.6",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.6.tgz",
+ "integrity": "sha512-aHWdQ2AAltRkLPOsKdi3xv0mZ8fUGPdlKEjIEhxCPm5yKEThcUjHpWB1idN74lfXGnZ5SULQSgtr5Qos5B0bPw==",
+
"cpu": [
"ia32"
],
@@ -1072,9 +1110,11 @@
}
},
"node_modules/@esbuild/linux-loong64": {
- "version": "0.25.5",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.5.tgz",
- "integrity": "sha512-0ur7ae16hDUC4OL5iEnDb0tZHDxYmuQyhKhsPBV8f99f6Z9KQM02g33f93rNH5A30agMS46u2HP6qTdEt6Q1kg==",
+
+ "version": "0.25.6",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.6.tgz",
+ "integrity": "sha512-VgKCsHdXRSQ7E1+QXGdRPlQ/e08bN6WMQb27/TMfV+vPjjTImuT9PmLXupRlC90S1JeNNW5lzkAEO/McKeJ2yg==",
+
"cpu": [
"loong64"
],
@@ -1089,9 +1129,11 @@
}
},
"node_modules/@esbuild/linux-mips64el": {
- "version": "0.25.5",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.5.tgz",
- "integrity": "sha512-kB/66P1OsHO5zLz0i6X0RxlQ+3cu0mkxS3TKFvkb5lin6uwZ/ttOkP3Z8lfR9mJOBk14ZwZ9182SIIWFGNmqmg==",
+
+ "version": "0.25.6",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.6.tgz",
+ "integrity": "sha512-WViNlpivRKT9/py3kCmkHnn44GkGXVdXfdc4drNmRl15zVQ2+D2uFwdlGh6IuK5AAnGTo2qPB1Djppj+t78rzw==",
+
"cpu": [
"mips64el"
],
@@ -1106,9 +1148,11 @@
}
},
"node_modules/@esbuild/linux-ppc64": {
- "version": "0.25.5",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.5.tgz",
- "integrity": "sha512-UZCmJ7r9X2fe2D6jBmkLBMQetXPXIsZjQJCjgwpVDz+YMcS6oFR27alkgGv3Oqkv07bxdvw7fyB71/olceJhkQ==",
+
+ "version": "0.25.6",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.6.tgz",
+ "integrity": "sha512-wyYKZ9NTdmAMb5730I38lBqVu6cKl4ZfYXIs31Baf8aoOtB4xSGi3THmDYt4BTFHk7/EcVixkOV2uZfwU3Q2Jw==",
+
"cpu": [
"ppc64"
],
@@ -1123,9 +1167,11 @@
}
},
"node_modules/@esbuild/linux-riscv64": {
- "version": "0.25.5",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.5.tgz",
- "integrity": "sha512-kTxwu4mLyeOlsVIFPfQo+fQJAV9mh24xL+y+Bm6ej067sYANjyEw1dNHmvoqxJUCMnkBdKpvOn0Ahql6+4VyeA==",
+
+ "version": "0.25.6",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.6.tgz",
+ "integrity": "sha512-KZh7bAGGcrinEj4qzilJ4hqTY3Dg2U82c8bv+e1xqNqZCrCyc+TL9AUEn5WGKDzm3CfC5RODE/qc96OcbIe33w==",
+
"cpu": [
"riscv64"
],
@@ -1140,9 +1186,11 @@
}
},
"node_modules/@esbuild/linux-s390x": {
- "version": "0.25.5",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.5.tgz",
- "integrity": "sha512-K2dSKTKfmdh78uJ3NcWFiqyRrimfdinS5ErLSn3vluHNeHVnBAFWC8a4X5N+7FgVE1EjXS1QDZbpqZBjfrqMTQ==",
+
+ "version": "0.25.6",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.6.tgz",
+ "integrity": "sha512-9N1LsTwAuE9oj6lHMyyAM+ucxGiVnEqUdp4v7IaMmrwb06ZTEVCIs3oPPplVsnjPfyjmxwHxHMF8b6vzUVAUGw==",
+
"cpu": [
"s390x"
],
@@ -1157,9 +1205,11 @@
}
},
"node_modules/@esbuild/linux-x64": {
- "version": "0.25.5",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.5.tgz",
- "integrity": "sha512-uhj8N2obKTE6pSZ+aMUbqq+1nXxNjZIIjCjGLfsWvVpy7gKCOL6rsY1MhRh9zLtUtAI7vpgLMK6DxjO8Qm9lJw==",
+
+ "version": "0.25.6",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.6.tgz",
+ "integrity": "sha512-A6bJB41b4lKFWRKNrWoP2LHsjVzNiaurf7wyj/XtFNTsnPuxwEBWHLty+ZE0dWBKuSK1fvKgrKaNjBS7qbFKig==",
+
"cpu": [
"x64"
],
@@ -1174,9 +1224,11 @@
}
},
"node_modules/@esbuild/netbsd-arm64": {
- "version": "0.25.5",
- "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.5.tgz",
- "integrity": "sha512-pwHtMP9viAy1oHPvgxtOv+OkduK5ugofNTVDilIzBLpoWAM16r7b/mxBvfpuQDpRQFMfuVr5aLcn4yveGvBZvw==",
+
+ "version": "0.25.6",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.6.tgz",
+ "integrity": "sha512-IjA+DcwoVpjEvyxZddDqBY+uJ2Snc6duLpjmkXm/v4xuS3H+3FkLZlDm9ZsAbF9rsfP3zeA0/ArNDORZgrxR/Q==",
+
"cpu": [
"arm64"
],
@@ -1191,9 +1243,11 @@
}
},
"node_modules/@esbuild/netbsd-x64": {
- "version": "0.25.5",
- "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.5.tgz",
- "integrity": "sha512-WOb5fKrvVTRMfWFNCroYWWklbnXH0Q5rZppjq0vQIdlsQKuw6mdSihwSo4RV/YdQ5UCKKvBy7/0ZZYLBZKIbwQ==",
+
+ "version": "0.25.6",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.6.tgz",
+ "integrity": "sha512-dUXuZr5WenIDlMHdMkvDc1FAu4xdWixTCRgP7RQLBOkkGgwuuzaGSYcOpW4jFxzpzL1ejb8yF620UxAqnBrR9g==",
+
"cpu": [
"x64"
],
@@ -1208,9 +1262,11 @@
}
},
"node_modules/@esbuild/openbsd-arm64": {
- "version": "0.25.5",
- "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.5.tgz",
- "integrity": "sha512-7A208+uQKgTxHd0G0uqZO8UjK2R0DDb4fDmERtARjSHWxqMTye4Erz4zZafx7Di9Cv+lNHYuncAkiGFySoD+Mw==",
+
+ "version": "0.25.6",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.6.tgz",
+ "integrity": "sha512-l8ZCvXP0tbTJ3iaqdNf3pjaOSd5ex/e6/omLIQCVBLmHTlfXW3zAxQ4fnDmPLOB1x9xrcSi/xtCWFwCZRIaEwg==",
+
"cpu": [
"arm64"
],
@@ -1225,9 +1281,11 @@
}
},
"node_modules/@esbuild/openbsd-x64": {
- "version": "0.25.5",
- "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.5.tgz",
- "integrity": "sha512-G4hE405ErTWraiZ8UiSoesH8DaCsMm0Cay4fsFWOOUcz8b8rC6uCvnagr+gnioEjWn0wC+o1/TAHt+It+MpIMg==",
+
+ "version": "0.25.6",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.6.tgz",
+ "integrity": "sha512-hKrmDa0aOFOr71KQ/19JC7az1P0GWtCN1t2ahYAf4O007DHZt/dW8ym5+CUdJhQ/qkZmI1HAF8KkJbEFtCL7gw==",
+
"cpu": [
"x64"
],
@@ -1241,10 +1299,29 @@
"node": ">=18"
}
},
+
+ "node_modules/@esbuild/openharmony-arm64": {
+ "version": "0.25.6",
+ "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.6.tgz",
+ "integrity": "sha512-+SqBcAWoB1fYKmpWoQP4pGtx+pUUC//RNYhFdbcSA16617cchuryuhOCRpPsjCblKukAckWsV+aQ3UKT/RMPcA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
"node_modules/@esbuild/sunos-x64": {
- "version": "0.25.5",
- "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.5.tgz",
- "integrity": "sha512-l+azKShMy7FxzY0Rj4RCt5VD/q8mG/e+mDivgspo+yL8zW7qEwctQ6YqKX34DTEleFAvCIUviCFX1SDZRSyMQA==",
+ "version": "0.25.6",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.6.tgz",
+ "integrity": "sha512-dyCGxv1/Br7MiSC42qinGL8KkG4kX0pEsdb0+TKhmJZgCUDBGmyo1/ArCjNGiOLiIAgdbWgmWgib4HoCi5t7kA==",
+
"cpu": [
"x64"
],
@@ -1259,9 +1336,11 @@
}
},
"node_modules/@esbuild/win32-arm64": {
- "version": "0.25.5",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.5.tgz",
- "integrity": "sha512-O2S7SNZzdcFG7eFKgvwUEZ2VG9D/sn/eIiz8XRZ1Q/DO5a3s76Xv0mdBzVM5j5R639lXQmPmSo0iRpHqUUrsxw==",
+
+ "version": "0.25.6",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.6.tgz",
+ "integrity": "sha512-42QOgcZeZOvXfsCBJF5Afw73t4veOId//XD3i+/9gSkhSV6Gk3VPlWncctI+JcOyERv85FUo7RxuxGy+z8A43Q==",
+
"cpu": [
"arm64"
],
@@ -1276,9 +1355,11 @@
}
},
"node_modules/@esbuild/win32-ia32": {
- "version": "0.25.5",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.5.tgz",
- "integrity": "sha512-onOJ02pqs9h1iMJ1PQphR+VZv8qBMQ77Klcsqv9CNW2w6yLqoURLcgERAIurY6QE63bbLuqgP9ATqajFLK5AMQ==",
+
+ "version": "0.25.6",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.6.tgz",
+ "integrity": "sha512-4AWhgXmDuYN7rJI6ORB+uU9DHLq/erBbuMoAuB4VWJTu5KtCgcKYPynF0YI1VkBNuEfjNlLrFr9KZPJzrtLkrQ==",
+
"cpu": [
"ia32"
],
@@ -1293,9 +1374,11 @@
}
},
"node_modules/@esbuild/win32-x64": {
- "version": "0.25.5",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.5.tgz",
- "integrity": "sha512-TXv6YnJ8ZMVdX+SXWVBo/0p8LTcrUYngpWjvm91TMjjBQii7Oz11Lw5lbDV5Y0TzuhSJHwiH4hEtC1I42mMS0g==",
+
+ "version": "0.25.6",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.6.tgz",
+ "integrity": "sha512-NgJPHHbEpLQgDH2MjQu90pzW/5vvXIZ7KOnPyNBm92A6WgZ/7b6fJyUBjoumLqeOQQGqY2QjQxRo97ah4Sj0cA==",
+
"cpu": [
"x64"
],
@@ -3172,9 +3255,11 @@
}
},
"node_modules/agent-base": {
- "version": "7.1.3",
- "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz",
- "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==",
+
+ "version": "7.1.4",
+ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz",
+ "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==",
+
"license": "MIT",
"engines": {
"node": ">= 14"
@@ -5970,9 +6055,11 @@
"optional": true
},
"node_modules/esbuild": {
- "version": "0.25.5",
- "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.5.tgz",
- "integrity": "sha512-P8OtKZRv/5J5hhz0cUAdu/cLuPIKXpQl1R9pZtvmHWQvrAUVd0UNIPT4IB4W3rNOqVO0rlqHmCIbSwxh/c9yUQ==",
+
+ "version": "0.25.6",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.6.tgz",
+ "integrity": "sha512-GVuzuUwtdsghE3ocJ9Bs8PNoF13HNQ5TXbEi2AhvVb8xU1Iwt9Fos9FEamfoee+u/TOsn7GUWc04lz46n2bbTg==",
+
"dev": true,
"hasInstallScript": true,
"license": "MIT",
@@ -5983,31 +6070,34 @@
"node": ">=18"
},
"optionalDependencies": {
- "@esbuild/aix-ppc64": "0.25.5",
- "@esbuild/android-arm": "0.25.5",
- "@esbuild/android-arm64": "0.25.5",
- "@esbuild/android-x64": "0.25.5",
- "@esbuild/darwin-arm64": "0.25.5",
- "@esbuild/darwin-x64": "0.25.5",
- "@esbuild/freebsd-arm64": "0.25.5",
- "@esbuild/freebsd-x64": "0.25.5",
- "@esbuild/linux-arm": "0.25.5",
- "@esbuild/linux-arm64": "0.25.5",
- "@esbuild/linux-ia32": "0.25.5",
- "@esbuild/linux-loong64": "0.25.5",
- "@esbuild/linux-mips64el": "0.25.5",
- "@esbuild/linux-ppc64": "0.25.5",
- "@esbuild/linux-riscv64": "0.25.5",
- "@esbuild/linux-s390x": "0.25.5",
- "@esbuild/linux-x64": "0.25.5",
- "@esbuild/netbsd-arm64": "0.25.5",
- "@esbuild/netbsd-x64": "0.25.5",
- "@esbuild/openbsd-arm64": "0.25.5",
- "@esbuild/openbsd-x64": "0.25.5",
- "@esbuild/sunos-x64": "0.25.5",
- "@esbuild/win32-arm64": "0.25.5",
- "@esbuild/win32-ia32": "0.25.5",
- "@esbuild/win32-x64": "0.25.5"
+
+ "@esbuild/aix-ppc64": "0.25.6",
+ "@esbuild/android-arm": "0.25.6",
+ "@esbuild/android-arm64": "0.25.6",
+ "@esbuild/android-x64": "0.25.6",
+ "@esbuild/darwin-arm64": "0.25.6",
+ "@esbuild/darwin-x64": "0.25.6",
+ "@esbuild/freebsd-arm64": "0.25.6",
+ "@esbuild/freebsd-x64": "0.25.6",
+ "@esbuild/linux-arm": "0.25.6",
+ "@esbuild/linux-arm64": "0.25.6",
+ "@esbuild/linux-ia32": "0.25.6",
+ "@esbuild/linux-loong64": "0.25.6",
+ "@esbuild/linux-mips64el": "0.25.6",
+ "@esbuild/linux-ppc64": "0.25.6",
+ "@esbuild/linux-riscv64": "0.25.6",
+ "@esbuild/linux-s390x": "0.25.6",
+ "@esbuild/linux-x64": "0.25.6",
+ "@esbuild/netbsd-arm64": "0.25.6",
+ "@esbuild/netbsd-x64": "0.25.6",
+ "@esbuild/openbsd-arm64": "0.25.6",
+ "@esbuild/openbsd-x64": "0.25.6",
+ "@esbuild/openharmony-arm64": "0.25.6",
+ "@esbuild/sunos-x64": "0.25.6",
+ "@esbuild/win32-arm64": "0.25.6",
+ "@esbuild/win32-ia32": "0.25.6",
+ "@esbuild/win32-x64": "0.25.6"
+
}
},
"node_modules/escalade": {
diff --git a/package.json b/package.json
index d1f185b..36c681d 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,9 @@
{
"name": "pickle-glass",
"productName": "Glass",
- "version": "0.2.1",
+
+ "version": "0.2.2",
+
"description": "Cl*ely for Free",
"main": "src/index.js",
"scripts": {
@@ -9,12 +11,14 @@
"start": "npm run build:renderer && electron-forge start",
"package": "npm run build:renderer && electron-forge package",
"make": "npm run build:renderer && electron-forge make",
- "build": "npm run build:renderer && electron-builder --config electron-builder.yml --publish never",
- "build:win": "npm run build:renderer && electron-builder --win --x64 --publish never",
- "publish": "npm run build:renderer && electron-builder --config electron-builder.yml --publish always",
+ "build": "npm run build:all && electron-builder --config electron-builder.yml --publish never",
+ "build:win": "npm run build:all && electron-builder --win --x64 --publish never",
+ "publish": "npm run build:all && electron-builder --config electron-builder.yml --publish always",
"lint": "eslint --ext .ts,.tsx,.js .",
"postinstall": "electron-builder install-app-deps",
"build:renderer": "node build.js",
+ "build:web": "cd pickleglass_web && npm run build && cd ..",
+ "build:all": "npm run build:renderer && npm run build:web",
"watch:renderer": "node build.js --watch"
},
"keywords": [
diff --git a/pickleglass_web/package-lock.json b/pickleglass_web/package-lock.json
index c7d23a8..a1726d6 100644
--- a/pickleglass_web/package-lock.json
+++ b/pickleglass_web/package-lock.json
@@ -42,21 +42,27 @@
}
},
"node_modules/@emnapi/core": {
- "version": "1.4.3",
- "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.4.3.tgz",
- "integrity": "sha512-4m62DuCE07lw01soJwPiBGC0nAww0Q+RY70VZ+n49yDIO13yyinhbWCeNnaob0lakDtWQzSdtNWzJeOJt2ma+g==",
+
+ "version": "1.4.4",
+ "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.4.4.tgz",
+ "integrity": "sha512-A9CnAbC6ARNMKcIcrQwq6HeHCjpcBZ5wSx4U01WXCqEKlrzB9F9315WDNHkrs2xbx7YjjSxbUYxuN6EQzpcY2g==",
+
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
- "@emnapi/wasi-threads": "1.0.2",
+
+ "@emnapi/wasi-threads": "1.0.3",
+
"tslib": "^2.4.0"
}
},
"node_modules/@emnapi/runtime": {
- "version": "1.4.3",
- "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.3.tgz",
- "integrity": "sha512-pBPWdu6MLKROBX05wSNKcNb++m5Er+KQ9QkB+WVM+pW2Kx9hoSrVTnu3BdkI5eBLZoKu/J6mW/B6i6bJB2ytXQ==",
+
+ "version": "1.4.4",
+ "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.4.tgz",
+ "integrity": "sha512-hHyapA4A3gPaDCNfiqyZUStTMqIkKRshqPIuDOXv1hcBnD4U3l8cP0T1HMCfGRxQ6V64TGCcoswChANyOAwbQg==",
+
"dev": true,
"license": "MIT",
"optional": true,
@@ -65,9 +71,11 @@
}
},
"node_modules/@emnapi/wasi-threads": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.0.2.tgz",
- "integrity": "sha512-5n3nTJblwRi8LlXkJ9eBzu+kZR8Yxcc7ubakyQTFzPMtIhFpUBRbsnc2Dv88IZDIbCDlBiWrknhB4Lsz7mg6BA==",
+
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.0.3.tgz",
+ "integrity": "sha512-8K5IFFsQqF9wQNJptGbS6FNKgUTsSRYnTqNCG1vPP8jFdjSv18n2mQfJpkt2Oibo9iBEzcDnDxNwKTzC7svlJw==",
+
"dev": true,
"license": "MIT",
"optional": true,
@@ -2667,9 +2675,11 @@
"license": "MIT"
},
"node_modules/electron-to-chromium": {
- "version": "1.5.179",
- "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.179.tgz",
- "integrity": "sha512-UWKi/EbBopgfFsc5k61wFpV7WrnnSlSzW/e2XcBmS6qKYTivZlLtoll5/rdqRTxGglGHkmkW0j0pFNJG10EUIQ==",
+
+ "version": "1.5.180",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.180.tgz",
+ "integrity": "sha512-ED+GEyEh3kYMwt2faNmgMB0b8O5qtATGgR4RmRsIp4T6p7B8vdMbIedYndnvZfsaXvSzegtpfqRMDNCjjiSduA==",
+
"license": "ISC"
},
"node_modules/emoji-regex": {
diff --git a/src/app/ApiKeyHeader.js b/src/app/ApiKeyHeader.js
index 25f1571..92962c8 100644
--- a/src/app/ApiKeyHeader.js
+++ b/src/app/ApiKeyHeader.js
@@ -1,12 +1,17 @@
import { html, css, LitElement } from "../assets/lit-core-2.7.4.min.js"
export class ApiKeyHeader extends LitElement {
+ //////// after_modelStateService ////////
static properties = {
- apiKey: { type: String },
+ llmApiKey: { type: String },
+ sttApiKey: { type: String },
+ llmProvider: { type: String },
+ sttProvider: { type: String },
isLoading: { type: Boolean },
errorMessage: { type: String },
- selectedProvider: { type: String },
+ providers: { type: Object, state: true },
}
+ //////// after_modelStateService ////////
static styles = css`
:host {
@@ -45,7 +50,7 @@ export class ApiKeyHeader extends LitElement {
}
.container {
- width: 285px;
+ width: 350px;
min-height: 260px;
padding: 18px 20px;
background: rgba(0, 0, 0, 0.3);
@@ -153,28 +158,22 @@ export class ApiKeyHeader extends LitElement {
outline: none;
}
- .provider-select {
+ .providers-container { display: flex; gap: 12px; width: 100%; }
+ .provider-column { flex: 1; display: flex; flex-direction: column; align-items: center; }
+ .provider-label { color: rgba(255, 255, 255, 0.7); font-size: 11px; font-weight: 500; margin-bottom: 6px; }
+ .api-input, .provider-select {
width: 100%;
height: 34px;
+ text-align: center;
background: rgba(255, 255, 255, 0.1);
border-radius: 10px;
border: 1px solid rgba(255, 255, 255, 0.2);
padding: 0 10px;
color: white;
font-size: 12px;
- font-weight: 400;
margin-bottom: 6px;
- text-align: center;
- cursor: pointer;
- -webkit-appearance: none;
- -moz-appearance: none;
- appearance: none;
- background-image: url('data:image/svg+xml;charset=US-ASCII,%3Csvg%20width%3D%2714%27%20height%3D%278%27%20viewBox%3D%270%200%2014%208%27%20xmlns%3D%27http%3A//www.w3.org/2000/svg%27%3E%3Cpath%20d%3D%27M1%201l6%206%206-6%27%20stroke%3D%27%23ffffff%27%20stroke-width%3D%271.5%27%20fill%3D%27none%27%20fill-rule%3D%27evenodd%27/%3E%3C/svg%3E');
- background-repeat: no-repeat;
- background-position: right 10px center;
- background-size: 12px;
- padding-right: 30px;
}
+ .provider-select option { background: #1a1a1a; color: white; }
.provider-select:hover {
background-color: rgba(255, 255, 255, 0.15);
@@ -187,11 +186,6 @@ export class ApiKeyHeader extends LitElement {
border-color: rgba(255, 255, 255, 0.4);
}
- .provider-select option {
- background: #1a1a1a;
- color: white;
- padding: 5px;
- }
.action-button {
width: 100%;
@@ -239,15 +233,7 @@ export class ApiKeyHeader extends LitElement {
font-weight: 500; /* Medium */
margin: 10px 0;
}
-
- .provider-label {
- color: rgba(255, 255, 255, 0.7);
- font-size: 11px;
- font-weight: 400;
- margin-bottom: 4px;
- width: 100%;
- text-align: left;
- }
+
/* ββββββββββββββββ[ GLASS BYPASS ]βββββββββββββββ */
:host-context(body.has-glass) .container,
@@ -278,11 +264,16 @@ export class ApiKeyHeader extends LitElement {
super()
this.dragState = null
this.wasJustDragged = false
- this.apiKey = ""
this.isLoading = false
this.errorMessage = ""
- this.validatedApiKey = null
- this.selectedProvider = "openai"
+ //////// after_modelStateService ////////
+ this.llmApiKey = "";
+ this.sttApiKey = "";
+ this.llmProvider = "openai";
+ this.sttProvider = "openai";
+ this.providers = { llm: [], stt: [] }; // μ΄κΈ°ν
+ this.loadProviderConfig();
+ //////// after_modelStateService ////////
this.handleMouseMove = this.handleMouseMove.bind(this)
this.handleMouseUp = this.handleMouseUp.bind(this)
@@ -303,6 +294,35 @@ export class ApiKeyHeader extends LitElement {
this.requestUpdate()
}
+ async loadProviderConfig() {
+ if (!window.require) return;
+ const { ipcRenderer } = window.require('electron');
+ const config = await ipcRenderer.invoke('model:get-provider-config');
+
+ const llmProviders = [];
+ const sttProviders = [];
+
+ for (const id in config) {
+ // 'openai-glass' κ°μ κ°μ Providerλ UIμ νμνμ§ μμ
+ if (id.includes('-glass')) continue;
+
+ if (config[id].llmModels.length > 0) {
+ llmProviders.push({ id, name: config[id].name });
+ }
+ if (config[id].sttModels.length > 0) {
+ sttProviders.push({ id, name: config[id].name });
+ }
+ }
+
+ this.providers = { llm: llmProviders, stt: sttProviders };
+
+ // κΈ°λ³Έ μ ν κ° μ€μ
+ if (llmProviders.length > 0) this.llmProvider = llmProviders[0].id;
+ if (sttProviders.length > 0) this.sttProvider = sttProviders[0].id;
+
+ this.requestUpdate();
+}
+
async handleMouseDown(e) {
if (e.target.tagName === "INPUT" || e.target.tagName === "BUTTON" || e.target.tagName === "SELECT") {
return
@@ -409,144 +429,45 @@ export class ApiKeyHeader extends LitElement {
}
}
+ //////// after_modelStateService ////////
async handleSubmit() {
- if (this.wasJustDragged || this.isLoading || !this.apiKey.trim()) {
- console.log("Submit blocked:", {
- wasJustDragged: this.wasJustDragged,
- isLoading: this.isLoading,
- hasApiKey: !!this.apiKey.trim(),
- })
- return
+ console.log('[ApiKeyHeader] handleSubmit: Submitting API keys...');
+ if (this.isLoading || !this.llmApiKey.trim() || !this.sttApiKey.trim()) {
+ this.errorMessage = "Please enter keys for both LLM and STT.";
+ return;
}
- console.log("Starting API key validation...")
- this.isLoading = true
- this.errorMessage = ""
- this.requestUpdate()
+ this.isLoading = true;
+ this.errorMessage = "";
+ this.requestUpdate();
- const apiKey = this.apiKey.trim()
- const isValid = false
- try {
- const isValid = await this.validateApiKey(this.apiKey.trim(), this.selectedProvider)
+ const { ipcRenderer } = window.require('electron');
- if (isValid) {
- console.log("API key valid - starting slide out animation")
- this.startSlideOutAnimation()
- this.validatedApiKey = this.apiKey.trim()
- this.validatedProvider = this.selectedProvider
- } else {
- this.errorMessage = "Invalid API key - please check and try again"
- console.log("API key validation failed")
- }
- } catch (error) {
- console.error("API key validation error:", error)
- this.errorMessage = "Validation error - please try again"
- } finally {
- this.isLoading = false
- this.requestUpdate()
- }
- }
+ console.log('[ApiKeyHeader] handleSubmit: Validating LLM key...');
+ const llmValidation = ipcRenderer.invoke('model:validate-key', { provider: this.llmProvider, key: this.llmApiKey.trim() });
+ const sttValidation = ipcRenderer.invoke('model:validate-key', { provider: this.sttProvider, key: this.sttApiKey.trim() });
- async validateApiKey(apiKey, provider = "openai") {
- if (!apiKey || apiKey.length < 15) return false
+ const [llmResult, sttResult] = await Promise.all([llmValidation, sttValidation]);
- if (provider === "openai") {
- if (!apiKey.match(/^[A-Za-z0-9_-]+$/)) return false
-
- try {
- console.log("Validating OpenAI API key...")
-
- const response = await fetch("https://api.openai.com/v1/models", {
- headers: {
- "Content-Type": "application/json",
- Authorization: `Bearer ${apiKey}`,
- },
- })
-
- if (response.ok) {
- const data = await response.json()
-
- const hasGPTModels = data.data && data.data.some((m) => m.id.startsWith("gpt-"))
- if (hasGPTModels) {
- console.log("OpenAI API key validation successful")
- return true
- } else {
- console.log("API key valid but no GPT models available")
- return false
- }
- } else {
- const errorData = await response.json().catch(() => ({}))
- console.log("API key validation failed:", response.status, errorData.error?.message || "Unknown error")
- return false
- }
- } catch (error) {
- console.error("API key validation network error:", error)
- return apiKey.length >= 20 // Fallback for network issues
- }
- } else if (provider === "gemini") {
- // Gemini API keys typically start with 'AIza'
- if (!apiKey.match(/^[A-Za-z0-9_-]+$/)) return false
-
- try {
- console.log("Validating Gemini API key...")
-
- // Test the API key with a simple models list request
- const response = await fetch(`https://generativelanguage.googleapis.com/v1beta/models?key=${apiKey}`)
-
- if (response.ok) {
- const data = await response.json()
- if (data.models && data.models.length > 0) {
- console.log("Gemini API key validation successful")
- return true
- }
- }
-
- console.log("Gemini API key validation failed")
- return false
- } catch (error) {
- console.error("Gemini API key validation network error:", error)
- return apiKey.length >= 20 // Fallback
- }
- } else if (provider === "anthropic") {
- // Anthropic API keys typically start with 'sk-ant-'
- if (!apiKey.startsWith("sk-ant-") || !apiKey.match(/^[A-Za-z0-9_-]+$/)) return false
-
- try {
- console.log("Validating Anthropic API key...")
-
- // Test the API key with a simple request
- const response = await fetch("https://api.anthropic.com/v1/messages", {
- method: "POST",
- headers: {
- "Content-Type": "application/json",
- "x-api-key": apiKey,
- "anthropic-version": "2023-06-01",
- },
- body: JSON.stringify({
- model: "claude-3-haiku-20240307",
- max_tokens: 10,
- messages: [{ role: "user", content: "Hi" }],
- }),
- })
-
- if (response.ok || response.status === 400) {
- // 400 is also acceptable as it means the API key is valid but request format might be wrong
- console.log("Anthropic API key validation successful")
- return true
- }
-
- console.log("Anthropic API key validation failed:", response.status)
- return false
- } catch (error) {
- console.error("Anthropic API key validation network error:", error)
- return apiKey.length >= 20 // Fallback
- }
+ if (llmResult.success && sttResult.success) {
+ console.log('[ApiKeyHeader] handleSubmit: Both LLM and STT keys are valid.');
+ this.startSlideOutAnimation();
+ } else {
+ console.log('[ApiKeyHeader] handleSubmit: Validation failed.');
+ let errorParts = [];
+ if (!llmResult.success) errorParts.push(`LLM Key: ${llmResult.error || 'Invalid'}`);
+ if (!sttResult.success) errorParts.push(`STT Key: ${sttResult.error || 'Invalid'}`);
+ this.errorMessage = errorParts.join(' | ');
}
- return false
- }
+ this.isLoading = false;
+ this.requestUpdate();
+}
+//////// after_modelStateService ////////
+
startSlideOutAnimation() {
+ console.log('[ApiKeyHeader] startSlideOutAnimation: Starting slide out animation.');
this.classList.add("sliding-out")
}
@@ -567,25 +488,18 @@ export class ApiKeyHeader extends LitElement {
}
}
+
+ //////// after_modelStateService ////////
handleAnimationEnd(e) {
- if (e.target !== this) return
-
- if (this.classList.contains("sliding-out")) {
- this.classList.remove("sliding-out")
- this.classList.add("hidden")
-
- if (this.validatedApiKey) {
- if (window.require) {
- window.require("electron").ipcRenderer.invoke("api-key-validated", {
- apiKey: this.validatedApiKey,
- provider: this.validatedProvider || "openai",
- })
- }
- this.validatedApiKey = null
- this.validatedProvider = null
- }
- }
+ if (e.target !== this || !this.classList.contains('sliding-out')) return;
+ this.classList.remove("sliding-out");
+ this.classList.add("hidden");
+ window.require('electron').ipcRenderer.invoke('get-current-user').then(userState => {
+ console.log('[ApiKeyHeader] handleAnimationEnd: User state updated:', userState);
+ this.stateUpdateCallback?.(userState);
+ });
}
+//////// after_modelStateService ////////
connectedCallback() {
super.connectedCallback()
@@ -598,64 +512,40 @@ export class ApiKeyHeader extends LitElement {
}
render() {
- const isButtonDisabled = this.isLoading || !this.apiKey || !this.apiKey.trim()
- console.log("Rendering with provider:", this.selectedProvider)
+ const isButtonDisabled = this.isLoading || !this.llmApiKey.trim() || !this.sttApiKey.trim();
return html`
-
-
-
Choose how to power your AI
+
+
Enter Your API Keys
-
-
${this.errorMessage}
-
Select AI Provider:
-
+ `;
+}
}
customElements.define("apikey-header", ApiKeyHeader)
diff --git a/src/app/HeaderController.js b/src/app/HeaderController.js
index 26313f1..e2c5fe8 100644
--- a/src/app/HeaderController.js
+++ b/src/app/HeaderController.js
@@ -15,7 +15,11 @@ class HeaderTransitionManager {
* @param {'apikey'|'main'|'permission'} type
*/
this.ensureHeader = (type) => {
- if (this.currentHeaderType === type) return;
+ console.log('[HeaderController] ensureHeader: Ensuring header of type:', type);
+ if (this.currentHeaderType === type) {
+ console.log('[HeaderController] ensureHeader: Header of type:', type, 'already exists.');
+ return;
+ }
this.headerContainer.innerHTML = '';
@@ -26,6 +30,7 @@ class HeaderTransitionManager {
// Create new header element
if (type === 'apikey') {
this.apiKeyHeader = document.createElement('apikey-header');
+ this.apiKeyHeader.stateUpdateCallback = (userState) => this.handleStateUpdate(userState);
this.headerContainer.appendChild(this.apiKeyHeader);
} else if (type === 'permission') {
this.permissionHeader = document.createElement('permission-setup');
@@ -60,6 +65,11 @@ class HeaderTransitionManager {
this.apiKeyHeader.isLoading = false;
}
});
+ ipcRenderer.on('force-show-apikey-header', async () => {
+ console.log('[HeaderController] Received broadcast to show apikey header. Switching now.');
+ await this._resizeForApiKey();
+ this.ensureHeader('apikey');
+ });
}
}
@@ -83,26 +93,30 @@ class HeaderTransitionManager {
}
}
- async handleStateUpdate(userState) {
- const { isLoggedIn, hasApiKey } = userState;
- if (isLoggedIn) {
- // Firebase user: Check permissions, then show Main or Permission header
- const permissionResult = await this.checkPermissions();
- if (permissionResult.success) {
- this.transitionToMainHeader();
+ //////// after_modelStateService ////////
+ async handleStateUpdate(userState) {
+ const { ipcRenderer } = window.require('electron');
+ const isConfigured = await ipcRenderer.invoke('model:are-providers-configured');
+
+ if (isConfigured) {
+ const { isLoggedIn } = userState;
+ if (isLoggedIn) {
+ const permissionResult = await this.checkPermissions();
+ if (permissionResult.success) {
+ this.transitionToMainHeader();
+ } else {
+ this.transitionToPermissionHeader();
+ }
} else {
- this.transitionToPermissionHeader();
+ this.transitionToMainHeader();
}
- } else if (hasApiKey) {
- // API Key only user: Skip permission check, go directly to Main
- this.transitionToMainHeader();
} else {
- // No auth at all
await this._resizeForApiKey();
this.ensureHeader('apikey');
}
}
+ //////// after_modelStateService ////////
async transitionToPermissionHeader() {
// Prevent duplicate transitions
@@ -159,7 +173,7 @@ class HeaderTransitionManager {
if (!window.require) return;
return window
.require('electron')
- .ipcRenderer.invoke('resize-header-window', { width: 285, height: 300 })
+ .ipcRenderer.invoke('resize-header-window', { width: 350, height: 300 })
.catch(() => {});
}
diff --git a/src/common/ai/factory.js b/src/common/ai/factory.js
index 8d0d6b5..ea86ff0 100644
--- a/src/common/ai/factory.js
+++ b/src/common/ai/factory.js
@@ -1,68 +1,121 @@
-const providers = {
- openai: require("./providers/openai"),
- gemini: require("./providers/gemini"),
- anthropic: require("./providers/anthropic"),
- // μΆκ° providerλ μ¬κΈ°μ λ±λ‘
-}
+// factory.js
/**
- * Creates an STT session based on provider
- * @param {string} provider - Provider name ('openai', 'gemini', etc.)
- * @param {object} opts - Configuration options (apiKey, language, callbacks, etc.)
- * @returns {Promise
-
-
-
-
-
+ ${apiKeyManagementHTML}
+ ${modelSelectionHTML}
+
${this.getMainShortcuts().map(shortcut => html`
@@ -757,7 +1144,6 @@ export class SettingsView extends LitElement {
`)}
-
`;
}
+ //////// after_modelStateService ////////
}
customElements.define('settings-view', SettingsView);
\ No newline at end of file
diff --git a/src/index.js b/src/index.js
index 52c057f..31ae822 100644
--- a/src/index.js
+++ b/src/index.js
@@ -11,7 +11,7 @@ if (require('electron-squirrel-startup')) {
process.exit(0);
}
-const { app, BrowserWindow, shell, ipcMain, dialog } = require('electron');
+const { app, BrowserWindow, shell, ipcMain, dialog, desktopCapturer, session } = require('electron');
const { createWindows } = require('./electron/windowManager.js');
const ListenService = require('./features/listen/listenService');
const { initializeFirebase } = require('./common/services/firebaseClient');
@@ -25,6 +25,7 @@ const { EventEmitter } = require('events');
const askService = require('./features/ask/askService');
const settingsService = require('./features/settings/settingsService');
const sessionRepository = require('./common/repositories/session');
+const ModelStateService = require('./common/services/modelStateService');
const eventBridge = new EventEmitter();
let WEB_PORT = 3000;
@@ -33,6 +34,11 @@ const listenService = new ListenService();
// Make listenService globally accessible so other modules (e.g., windowManager, askService) can reuse the same instance
global.listenService = listenService;
+//////// after_modelStateService ////////
+const modelStateService = new ModelStateService(authService);
+global.modelStateService = modelStateService;
+//////// after_modelStateService ////////
+
// Native deep link handling - cross-platform compatible
let pendingDeepLinkUrl = null;
@@ -162,6 +168,17 @@ setupProtocolHandling();
app.whenReady().then(async () => {
+ // Setup native loopback audio capture for Windows
+ session.defaultSession.setDisplayMediaRequestHandler((request, callback) => {
+ desktopCapturer.getSources({ types: ['screen'] }).then((sources) => {
+ // Grant access to the first screen found with loopback audio
+ callback({ video: sources[0], audio: 'loopback' });
+ }).catch((error) => {
+ console.error('Failed to get desktop capturer sources:', error);
+ callback({});
+ });
+ });
+
// Initialize core services
initializeFirebase();
@@ -173,6 +190,11 @@ app.whenReady().then(async () => {
sessionRepository.endAllActiveSessions();
authService.initialize();
+
+ //////// after_modelStateService ////////
+ modelStateService.initialize();
+ //////// after_modelStateService ////////
+
listenService.setupIpcHandlers();
askService.initialize();
settingsService.initialize();