@react-spa-scaffold/mcp 2.3.0 → 2.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/constants.d.ts +1 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +1 -0
- package/dist/constants.js.map +1 -1
- package/dist/features/definitions/api.d.ts.map +1 -1
- package/dist/features/definitions/api.js +2 -1
- package/dist/features/definitions/api.js.map +1 -1
- package/dist/features/definitions/electron.d.ts +3 -0
- package/dist/features/definitions/electron.d.ts.map +1 -0
- package/dist/features/definitions/electron.js +23 -0
- package/dist/features/definitions/electron.js.map +1 -0
- package/dist/features/definitions/index.d.ts +1 -0
- package/dist/features/definitions/index.d.ts.map +1 -1
- package/dist/features/definitions/index.js +1 -0
- package/dist/features/definitions/index.js.map +1 -1
- package/dist/features/registry.d.ts.map +1 -1
- package/dist/features/registry.js +2 -1
- package/dist/features/registry.js.map +1 -1
- package/dist/features/types.d.ts +1 -0
- package/dist/features/types.d.ts.map +1 -1
- package/dist/features/types.test.js +3 -2
- package/dist/features/types.test.js.map +1 -1
- package/dist/tools/get-features.test.js +7 -0
- package/dist/tools/get-features.test.js.map +1 -1
- package/dist/tools/get-scaffold.d.ts +1 -0
- package/dist/tools/get-scaffold.d.ts.map +1 -1
- package/dist/tools/get-scaffold.js +4 -1
- package/dist/tools/get-scaffold.js.map +1 -1
- package/dist/tools/get-scaffold.test.js +50 -0
- package/dist/tools/get-scaffold.test.js.map +1 -1
- package/dist/utils/scaffold/claude-md/index.d.ts.map +1 -1
- package/dist/utils/scaffold/claude-md/index.js +2 -1
- package/dist/utils/scaffold/claude-md/index.js.map +1 -1
- package/dist/utils/scaffold/claude-md/sections.d.ts +1 -0
- package/dist/utils/scaffold/claude-md/sections.d.ts.map +1 -1
- package/dist/utils/scaffold/claude-md/sections.js +42 -0
- package/dist/utils/scaffold/claude-md/sections.js.map +1 -1
- package/dist/utils/scaffold/compute.d.ts.map +1 -1
- package/dist/utils/scaffold/compute.js +3 -1
- package/dist/utils/scaffold/compute.js.map +1 -1
- package/dist/utils/scaffold/generators.d.ts +5 -0
- package/dist/utils/scaffold/generators.d.ts.map +1 -1
- package/dist/utils/scaffold/generators.js +43 -0
- package/dist/utils/scaffold/generators.js.map +1 -1
- package/package.json +1 -1
- package/templates/.github/workflows/ci.yml +45 -1
- package/templates/.github/workflows/deploy.yml +18 -31
- package/templates/CLAUDE.md +3 -0
- package/templates/docs/DEPLOYMENT.md +32 -4
- package/templates/forge.config.js +53 -0
- package/templates/package.json +5 -1
- package/templates/src/components/shared/ProfileSync/ProfileSync.tsx +2 -2
- package/templates/src/contexts/queryContext.tsx +9 -8
- package/templates/src/main.ts +227 -0
- package/templates/src/main.tsx +15 -3
- package/templates/src/preload.ts +26 -0
- package/templates/src/types/global.d.ts +28 -0
- package/templates/vite.main.config.mjs +20 -0
- package/templates/vite.preload.config.mjs +17 -0
- package/templates/vite.renderer.config.mjs +52 -0
|
@@ -6,4 +6,9 @@ export declare function generateViteEnvDts(featureIds: FeatureId[]): string;
|
|
|
6
6
|
export declare function generateEnvTs(featureIds: FeatureId[]): string;
|
|
7
7
|
/** Generates routes.ts content based on selected features. */
|
|
8
8
|
export declare function generateRoutesTs(featureIds: FeatureId[]): string;
|
|
9
|
+
/**
|
|
10
|
+
* Generates global.d.ts content based on selected features.
|
|
11
|
+
* Currently only includes ElectronAPI types when electron feature is selected.
|
|
12
|
+
*/
|
|
13
|
+
export declare function generateGlobalDts(featureIds: FeatureId[]): string | undefined;
|
|
9
14
|
//# sourceMappingURL=generators.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generators.d.ts","sourceRoot":"","sources":["../../../src/utils/scaffold/generators.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAEzD,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAExD,iEAAiE;AACjE,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,SAAS,EAAE,GAAG,MAAM,CAqDlE;AAED,0DAA0D;AAC1D,wBAAgB,aAAa,CAAC,UAAU,EAAE,SAAS,EAAE,GAAG,MAAM,CA2G7D;AAED,8DAA8D;AAC9D,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,SAAS,EAAE,GAAG,MAAM,CAqBhE"}
|
|
1
|
+
{"version":3,"file":"generators.d.ts","sourceRoot":"","sources":["../../../src/utils/scaffold/generators.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAEzD,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAExD,iEAAiE;AACjE,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,SAAS,EAAE,GAAG,MAAM,CAqDlE;AAED,0DAA0D;AAC1D,wBAAgB,aAAa,CAAC,UAAU,EAAE,SAAS,EAAE,GAAG,MAAM,CA2G7D;AAED,8DAA8D;AAC9D,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,SAAS,EAAE,GAAG,MAAM,CAqBhE;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,SAAS,EAAE,GAAG,MAAM,GAAG,SAAS,CAyC7E"}
|
|
@@ -170,4 +170,47 @@ ${routes.join('\n')}
|
|
|
170
170
|
export type AppRoute = (typeof ROUTES)[keyof typeof ROUTES];
|
|
171
171
|
`;
|
|
172
172
|
}
|
|
173
|
+
/**
|
|
174
|
+
* Generates global.d.ts content based on selected features.
|
|
175
|
+
* Currently only includes ElectronAPI types when electron feature is selected.
|
|
176
|
+
*/
|
|
177
|
+
export function generateGlobalDts(featureIds) {
|
|
178
|
+
const sections = [];
|
|
179
|
+
// Electron API types
|
|
180
|
+
if (featureIds.includes(FEATURE.ELECTRON)) {
|
|
181
|
+
sections.push(`/**
|
|
182
|
+
* Global type declarations for Electron integration.
|
|
183
|
+
* These types are automatically available in the renderer process
|
|
184
|
+
* when running inside Electron.
|
|
185
|
+
*/
|
|
186
|
+
|
|
187
|
+
interface ElectronWindowAPI {
|
|
188
|
+
setAlwaysOnTop: (enable: boolean) => Promise<boolean>;
|
|
189
|
+
setContentProtection: (enable: boolean) => Promise<boolean>;
|
|
190
|
+
getState: () => Promise<{
|
|
191
|
+
alwaysOnTop: boolean;
|
|
192
|
+
contentProtection: boolean;
|
|
193
|
+
}>;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
interface ElectronAPI {
|
|
197
|
+
window: ElectronWindowAPI;
|
|
198
|
+
platform: NodeJS.Platform;
|
|
199
|
+
isElectron: true;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
declare global {
|
|
203
|
+
interface Window {
|
|
204
|
+
electronAPI?: ElectronAPI;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
export {};`);
|
|
209
|
+
}
|
|
210
|
+
// Return undefined if no content needed
|
|
211
|
+
if (sections.length === 0) {
|
|
212
|
+
return undefined;
|
|
213
|
+
}
|
|
214
|
+
return sections.join('\n\n') + '\n';
|
|
215
|
+
}
|
|
173
216
|
//# sourceMappingURL=generators.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generators.js","sourceRoot":"","sources":["../../../src/utils/scaffold/generators.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"generators.js","sourceRoot":"","sources":["../../../src/utils/scaffold/generators.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -20,6 +20,7 @@ jobs:
|
|
|
20
20
|
- uses: ./.github/actions/setup-node-deps
|
|
21
21
|
- run: npm run lint
|
|
22
22
|
- run: npm run format:check
|
|
23
|
+
- run: npm run sync:check
|
|
23
24
|
|
|
24
25
|
typecheck:
|
|
25
26
|
name: Type Check
|
|
@@ -41,7 +42,7 @@ jobs:
|
|
|
41
42
|
|
|
42
43
|
build:
|
|
43
44
|
name: Build
|
|
44
|
-
needs: [lint, typecheck]
|
|
45
|
+
needs: [lint, typecheck, security]
|
|
45
46
|
runs-on: ubuntu-latest
|
|
46
47
|
timeout-minutes: 10
|
|
47
48
|
steps:
|
|
@@ -134,3 +135,46 @@ jobs:
|
|
|
134
135
|
name: ${{ matrix.report-name }}
|
|
135
136
|
path: playwright-report/
|
|
136
137
|
retention-days: ${{ matrix.type == 'performance' && 14 || 7 }}
|
|
138
|
+
|
|
139
|
+
deploy:
|
|
140
|
+
name: Deploy
|
|
141
|
+
needs: [build, test, test-e2e]
|
|
142
|
+
runs-on: ubuntu-latest
|
|
143
|
+
timeout-minutes: 10
|
|
144
|
+
permissions:
|
|
145
|
+
pull-requests: write
|
|
146
|
+
deployments: write
|
|
147
|
+
steps:
|
|
148
|
+
- uses: actions/download-artifact@v6
|
|
149
|
+
with:
|
|
150
|
+
name: dist
|
|
151
|
+
path: dist/
|
|
152
|
+
|
|
153
|
+
- name: Deploy Preview
|
|
154
|
+
if: github.event_name == 'pull_request'
|
|
155
|
+
uses: nwtgck/actions-netlify@v3
|
|
156
|
+
with:
|
|
157
|
+
publish-dir: './dist'
|
|
158
|
+
production-deploy: false
|
|
159
|
+
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
160
|
+
deploy-message: 'Preview deploy from PR #${{ github.event.number }}'
|
|
161
|
+
enable-pull-request-comment: true
|
|
162
|
+
enable-commit-comment: false
|
|
163
|
+
overwrites-pull-request-comment: true
|
|
164
|
+
alias: pr-${{ github.event.number }}
|
|
165
|
+
env:
|
|
166
|
+
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
|
|
167
|
+
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }}
|
|
168
|
+
|
|
169
|
+
- name: Deploy Production
|
|
170
|
+
if: github.event_name == 'push'
|
|
171
|
+
uses: nwtgck/actions-netlify@v3
|
|
172
|
+
with:
|
|
173
|
+
publish-dir: './dist'
|
|
174
|
+
production-deploy: true
|
|
175
|
+
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
176
|
+
deploy-message: 'Production deploy from ${{ github.sha }}'
|
|
177
|
+
enable-commit-comment: true
|
|
178
|
+
env:
|
|
179
|
+
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
|
|
180
|
+
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }}
|
|
@@ -1,24 +1,24 @@
|
|
|
1
|
-
name: Deploy
|
|
1
|
+
name: Deploy (Manual)
|
|
2
2
|
|
|
3
3
|
on:
|
|
4
|
-
push:
|
|
5
|
-
branches: [main, master]
|
|
6
|
-
pull_request:
|
|
7
|
-
branches: [main, master]
|
|
8
4
|
workflow_dispatch:
|
|
5
|
+
inputs:
|
|
6
|
+
environment:
|
|
7
|
+
description: 'Deploy environment'
|
|
8
|
+
required: true
|
|
9
|
+
default: 'preview'
|
|
10
|
+
type: choice
|
|
11
|
+
options:
|
|
12
|
+
- preview
|
|
13
|
+
- production
|
|
9
14
|
|
|
10
15
|
permissions:
|
|
11
16
|
contents: read
|
|
12
|
-
pull-requests: write
|
|
13
17
|
deployments: write
|
|
14
18
|
|
|
15
|
-
concurrency:
|
|
16
|
-
group: deploy-${{ github.ref }}
|
|
17
|
-
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
|
18
|
-
|
|
19
19
|
jobs:
|
|
20
20
|
deploy:
|
|
21
|
-
name: Deploy
|
|
21
|
+
name: Deploy (${{ inputs.environment }})
|
|
22
22
|
runs-on: ubuntu-latest
|
|
23
23
|
timeout-minutes: 15
|
|
24
24
|
steps:
|
|
@@ -28,32 +28,19 @@ jobs:
|
|
|
28
28
|
|
|
29
29
|
- name: Build
|
|
30
30
|
run: npm run build
|
|
31
|
-
|
|
32
|
-
- name: Deploy Preview
|
|
33
|
-
if: github.event_name == 'pull_request'
|
|
34
|
-
uses: nwtgck/actions-netlify@v3
|
|
35
|
-
with:
|
|
36
|
-
publish-dir: './dist'
|
|
37
|
-
production-deploy: false
|
|
38
|
-
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
39
|
-
deploy-message: 'Preview deploy from PR #${{ github.event.number }}'
|
|
40
|
-
enable-pull-request-comment: true
|
|
41
|
-
enable-commit-comment: false
|
|
42
|
-
overwrites-pull-request-comment: true
|
|
43
|
-
alias: pr-${{ github.event.number }}
|
|
44
31
|
env:
|
|
45
|
-
|
|
46
|
-
|
|
32
|
+
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
|
|
33
|
+
SENTRY_ORG: ${{ secrets.SENTRY_ORG }}
|
|
34
|
+
SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }}
|
|
47
35
|
|
|
48
|
-
- name: Deploy
|
|
49
|
-
if: github.event_name == 'push' || github.event_name == 'workflow_dispatch'
|
|
36
|
+
- name: Deploy
|
|
50
37
|
uses: nwtgck/actions-netlify@v3
|
|
51
38
|
with:
|
|
52
39
|
publish-dir: './dist'
|
|
53
|
-
production-deploy:
|
|
40
|
+
production-deploy: ${{ inputs.environment == 'production' }}
|
|
54
41
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
55
|
-
deploy-message: '
|
|
56
|
-
enable-commit-comment:
|
|
42
|
+
deploy-message: 'Manual ${{ inputs.environment }} deploy from ${{ github.sha }}'
|
|
43
|
+
enable-commit-comment: ${{ inputs.environment == 'production' }}
|
|
57
44
|
env:
|
|
58
45
|
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
|
|
59
46
|
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }}
|
package/templates/CLAUDE.md
CHANGED
|
@@ -22,6 +22,8 @@ npm run i18n:extract # Extract translations to .po
|
|
|
22
22
|
npm run db:types # Generate Supabase TypeScript types
|
|
23
23
|
npm run db:push # Push database migrations
|
|
24
24
|
npm run db:studio # Open Supabase Studio
|
|
25
|
+
npm run sync:check # Check monorepo dependency versions
|
|
26
|
+
npm run sync:fix # Auto-fix version mismatches
|
|
25
27
|
```
|
|
26
28
|
|
|
27
29
|
## Project Structure
|
|
@@ -396,3 +398,4 @@ it('handles error', async () => {
|
|
|
396
398
|
6. **Clerk auth required** when auth feature is enabled - set `VITE_CLERK_PUBLISHABLE_KEY` in `.env`
|
|
397
399
|
7. **Supabase requires Clerk** - SupabaseProvider must be inside ClerkProvider
|
|
398
400
|
8. **RLS policies required** - All Supabase tables should have Row Level Security enabled
|
|
401
|
+
9. **Monorepo deps** - Run `npm run sync:check` to detect version mismatches across packages
|
|
@@ -19,11 +19,36 @@ Push to main → GitHub Actions → Build → Netlify Production
|
|
|
19
19
|
- Automatic preview deploys for pull requests
|
|
20
20
|
- Production deploys on push to main/master
|
|
21
21
|
- PR comments with preview URLs
|
|
22
|
-
- Manual deploy via workflow_dispatch
|
|
22
|
+
- Manual deploy via workflow_dispatch (with environment selector)
|
|
23
23
|
- Security headers pre-configured
|
|
24
|
+
- Sentry source map upload (when configured)
|
|
25
|
+
- **CI-gated**: Deploy only runs after all checks pass
|
|
24
26
|
|
|
25
27
|
**Note:** Enable branch protection rules to require CI to pass before merging to main.
|
|
26
28
|
|
|
29
|
+
### How It Works
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
Push/PR → CI Workflow → lint, typecheck, security, build, test, e2e → deploy
|
|
33
|
+
↓
|
|
34
|
+
(all must pass)
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
**Two workflows:**
|
|
38
|
+
|
|
39
|
+
1. **CI workflow** (`ci.yml`): Runs all checks, then deploys if all pass
|
|
40
|
+
2. **Manual deploy** (`deploy.yml`): Emergency escape hatch, skips CI checks
|
|
41
|
+
|
|
42
|
+
**Two deployment modes:**
|
|
43
|
+
|
|
44
|
+
1. **GitHub Actions (primary)**: Builds in CI, uploads pre-built files to Netlify
|
|
45
|
+
2. **Netlify CLI (fallback)**: Use `netlify build` and `netlify deploy` locally
|
|
46
|
+
|
|
47
|
+
The `netlify.toml` file configures both:
|
|
48
|
+
|
|
49
|
+
- `[build]` section: Used by Netlify CLI and `netlify dev`
|
|
50
|
+
- `[[headers]]` and `[[redirects]]`: Applied at CDN level regardless of build method
|
|
51
|
+
|
|
27
52
|
---
|
|
28
53
|
|
|
29
54
|
## Netlify Setup
|
|
@@ -151,11 +176,14 @@ Pushing to main/master triggers production deployment:
|
|
|
151
176
|
|
|
152
177
|
### Manual Deploys
|
|
153
178
|
|
|
154
|
-
Use
|
|
179
|
+
Use the manual deploy workflow for emergency or ad-hoc deploys (skips CI checks):
|
|
155
180
|
|
|
156
|
-
1. Go to Actions → Deploy → Run workflow
|
|
181
|
+
1. Go to Actions → Deploy (Manual) → Run workflow
|
|
157
182
|
2. Select branch
|
|
158
|
-
3.
|
|
183
|
+
3. Choose environment: `preview` or `production`
|
|
184
|
+
4. Click "Run workflow"
|
|
185
|
+
|
|
186
|
+
> **Warning:** Manual deploys skip CI checks. Use only for emergencies or hotfixes.
|
|
159
187
|
|
|
160
188
|
---
|
|
161
189
|
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
/** @type {import('@electron-forge/shared-types').ForgeConfig} */
|
|
4
|
+
const config = {
|
|
5
|
+
packagerConfig: {
|
|
6
|
+
name: 'ReactSPAScaffold',
|
|
7
|
+
appBundleId: 'com.example.react-spa-scaffold',
|
|
8
|
+
// No code signing for internal/dev use
|
|
9
|
+
osxSign: undefined,
|
|
10
|
+
osxNotarize: undefined,
|
|
11
|
+
},
|
|
12
|
+
rebuildConfig: {},
|
|
13
|
+
makers: [
|
|
14
|
+
{
|
|
15
|
+
name: '@electron-forge/maker-zip',
|
|
16
|
+
platforms: ['darwin'],
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
name: '@electron-forge/maker-dmg',
|
|
20
|
+
config: {
|
|
21
|
+
format: 'ULFO',
|
|
22
|
+
name: 'ReactSPAScaffold',
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
],
|
|
26
|
+
plugins: [
|
|
27
|
+
{
|
|
28
|
+
name: '@electron-forge/plugin-vite',
|
|
29
|
+
config: {
|
|
30
|
+
build: [
|
|
31
|
+
{
|
|
32
|
+
entry: 'src/main.ts',
|
|
33
|
+
config: 'vite.main.config.mjs',
|
|
34
|
+
target: 'main',
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
entry: 'src/preload.ts',
|
|
38
|
+
config: 'vite.preload.config.mjs',
|
|
39
|
+
target: 'preload',
|
|
40
|
+
},
|
|
41
|
+
],
|
|
42
|
+
renderer: [
|
|
43
|
+
{
|
|
44
|
+
name: 'main_window',
|
|
45
|
+
config: 'vite.renderer.config.mjs',
|
|
46
|
+
},
|
|
47
|
+
],
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
],
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
export default config;
|
package/templates/package.json
CHANGED
|
@@ -57,7 +57,9 @@
|
|
|
57
57
|
"mcp:inspect": "npm run inspect -w @react-spa-scaffold/mcp",
|
|
58
58
|
"changeset": "changeset",
|
|
59
59
|
"version": "changeset version",
|
|
60
|
-
"release": "changeset publish"
|
|
60
|
+
"release": "changeset publish",
|
|
61
|
+
"sync:check": "syncpack lint",
|
|
62
|
+
"sync:fix": "syncpack fix-mismatches"
|
|
61
63
|
},
|
|
62
64
|
"dependencies": {
|
|
63
65
|
"@clerk/react-router": "^2.3.7",
|
|
@@ -101,6 +103,7 @@
|
|
|
101
103
|
"@react-spa-scaffold/tsconfig": "*",
|
|
102
104
|
"@sentry/vite-plugin": "^4.6.1",
|
|
103
105
|
"@tailwindcss/vite": "^4.1.17",
|
|
106
|
+
"@tanstack/react-query-devtools": "^5.91.2",
|
|
104
107
|
"@testing-library/jest-dom": "^6.6.3",
|
|
105
108
|
"@testing-library/react": "^16.3.0",
|
|
106
109
|
"@testing-library/user-event": "^14.6.1",
|
|
@@ -127,6 +130,7 @@
|
|
|
127
130
|
"prettier-plugin-tailwindcss": "^0.7.2",
|
|
128
131
|
"shadcn": "^3.6.2",
|
|
129
132
|
"supabase": "^2.70.5",
|
|
133
|
+
"syncpack": "^13.0.4",
|
|
130
134
|
"tailwindcss": "^4.1.17",
|
|
131
135
|
"typescript": "~5.9.0",
|
|
132
136
|
"typescript-eslint": "^8.33.0",
|
|
@@ -50,7 +50,7 @@ interface ProfileSyncProps {
|
|
|
50
50
|
export function ProfileSync({ onSyncComplete, onSyncError }: ProfileSyncProps) {
|
|
51
51
|
const { user, isLoaded: isUserLoaded } = useUser();
|
|
52
52
|
const { data: profiles, isLoading: isProfileLoading } = useCurrentProfile();
|
|
53
|
-
const upsertProfile = useUpsertProfile();
|
|
53
|
+
const { mutate: upsertProfile } = useUpsertProfile();
|
|
54
54
|
|
|
55
55
|
// Track if we've already synced this session to prevent infinite loops
|
|
56
56
|
const hasSynced = useRef(false);
|
|
@@ -77,7 +77,7 @@ export function ProfileSync({ onSyncComplete, onSyncError }: ProfileSyncProps) {
|
|
|
77
77
|
// Perform the sync
|
|
78
78
|
isSyncing.current = true;
|
|
79
79
|
|
|
80
|
-
upsertProfile
|
|
80
|
+
upsertProfile(
|
|
81
81
|
{
|
|
82
82
|
id: user.id,
|
|
83
83
|
email: user.primaryEmailAddress?.emailAddress ?? '',
|
|
@@ -1,16 +1,14 @@
|
|
|
1
1
|
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
|
2
|
+
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
|
|
2
3
|
import { type ReactNode, useState } from 'react';
|
|
3
4
|
|
|
4
|
-
/**
|
|
5
|
-
* Create QueryClient with optimized defaults.
|
|
6
|
-
* Using a function ensures each provider instance gets its own client
|
|
7
|
-
*/
|
|
5
|
+
/** Create QueryClient with optimized defaults. */
|
|
8
6
|
function createQueryClient() {
|
|
9
7
|
return new QueryClient({
|
|
10
8
|
defaultOptions: {
|
|
11
9
|
queries: {
|
|
12
10
|
staleTime: 1000 * 60 * 5, // 5 minutes
|
|
13
|
-
gcTime: 1000 * 60 * 30, // 30 minutes
|
|
11
|
+
gcTime: 1000 * 60 * 30, // 30 minutes
|
|
14
12
|
retry: 1,
|
|
15
13
|
refetchOnWindowFocus: false,
|
|
16
14
|
},
|
|
@@ -19,9 +17,12 @@ function createQueryClient() {
|
|
|
19
17
|
}
|
|
20
18
|
|
|
21
19
|
export function QueryProvider({ children }: { children: ReactNode }) {
|
|
22
|
-
// Use useState to ensure queryClient is created once per component instance
|
|
23
|
-
// This is the recommended pattern from TanStack Query docs
|
|
24
20
|
const [queryClient] = useState(createQueryClient);
|
|
25
21
|
|
|
26
|
-
return
|
|
22
|
+
return (
|
|
23
|
+
<QueryClientProvider client={queryClient}>
|
|
24
|
+
{children}
|
|
25
|
+
<ReactQueryDevtools initialIsOpen={false} />
|
|
26
|
+
</QueryClientProvider>
|
|
27
|
+
);
|
|
27
28
|
}
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { fileURLToPath } from 'node:url';
|
|
3
|
+
|
|
4
|
+
import { app, BrowserWindow, ipcMain, Menu, type MenuItemConstructorOptions } from 'electron';
|
|
5
|
+
|
|
6
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
7
|
+
|
|
8
|
+
// Track window state
|
|
9
|
+
interface WindowState {
|
|
10
|
+
alwaysOnTop: boolean;
|
|
11
|
+
contentProtection: boolean;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
let mainWindow: BrowserWindow | null = null;
|
|
15
|
+
const windowState: WindowState = {
|
|
16
|
+
alwaysOnTop: false,
|
|
17
|
+
contentProtection: false,
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
function createMenu(): void {
|
|
21
|
+
const isMac = process.platform === 'darwin';
|
|
22
|
+
|
|
23
|
+
const template: MenuItemConstructorOptions[] = [
|
|
24
|
+
// App menu (macOS only)
|
|
25
|
+
...(isMac
|
|
26
|
+
? [
|
|
27
|
+
{
|
|
28
|
+
label: app.name,
|
|
29
|
+
submenu: [
|
|
30
|
+
{ role: 'about' as const },
|
|
31
|
+
{ type: 'separator' as const },
|
|
32
|
+
{ role: 'services' as const },
|
|
33
|
+
{ type: 'separator' as const },
|
|
34
|
+
{ role: 'hide' as const },
|
|
35
|
+
{ role: 'hideOthers' as const },
|
|
36
|
+
{ role: 'unhide' as const },
|
|
37
|
+
{ type: 'separator' as const },
|
|
38
|
+
{ role: 'quit' as const },
|
|
39
|
+
],
|
|
40
|
+
},
|
|
41
|
+
]
|
|
42
|
+
: []),
|
|
43
|
+
// File menu
|
|
44
|
+
{
|
|
45
|
+
label: 'File',
|
|
46
|
+
submenu: [isMac ? { role: 'close' as const } : { role: 'quit' as const }],
|
|
47
|
+
},
|
|
48
|
+
// Edit menu
|
|
49
|
+
{
|
|
50
|
+
label: 'Edit',
|
|
51
|
+
submenu: [
|
|
52
|
+
{ role: 'undo' as const },
|
|
53
|
+
{ role: 'redo' as const },
|
|
54
|
+
{ type: 'separator' as const },
|
|
55
|
+
{ role: 'cut' as const },
|
|
56
|
+
{ role: 'copy' as const },
|
|
57
|
+
{ role: 'paste' as const },
|
|
58
|
+
...(isMac
|
|
59
|
+
? [{ role: 'pasteAndMatchStyle' as const }, { role: 'delete' as const }, { role: 'selectAll' as const }]
|
|
60
|
+
: [{ role: 'delete' as const }, { type: 'separator' as const }, { role: 'selectAll' as const }]),
|
|
61
|
+
],
|
|
62
|
+
},
|
|
63
|
+
// View menu
|
|
64
|
+
{
|
|
65
|
+
label: 'View',
|
|
66
|
+
submenu: [
|
|
67
|
+
{ role: 'reload' as const },
|
|
68
|
+
{ role: 'forceReload' as const },
|
|
69
|
+
{ role: 'toggleDevTools' as const },
|
|
70
|
+
{ type: 'separator' as const },
|
|
71
|
+
{ role: 'resetZoom' as const },
|
|
72
|
+
{ role: 'zoomIn' as const },
|
|
73
|
+
{ role: 'zoomOut' as const },
|
|
74
|
+
{ type: 'separator' as const },
|
|
75
|
+
{ role: 'togglefullscreen' as const },
|
|
76
|
+
{ type: 'separator' as const },
|
|
77
|
+
{
|
|
78
|
+
label: 'Always on Top',
|
|
79
|
+
type: 'checkbox' as const,
|
|
80
|
+
checked: windowState.alwaysOnTop,
|
|
81
|
+
click: () => {
|
|
82
|
+
windowState.alwaysOnTop = !windowState.alwaysOnTop;
|
|
83
|
+
mainWindow?.setAlwaysOnTop(windowState.alwaysOnTop, 'floating');
|
|
84
|
+
createMenu(); // Refresh menu to update checkbox state
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
label: 'Hide from Screen Sharing',
|
|
89
|
+
type: 'checkbox' as const,
|
|
90
|
+
checked: windowState.contentProtection,
|
|
91
|
+
click: () => {
|
|
92
|
+
windowState.contentProtection = !windowState.contentProtection;
|
|
93
|
+
mainWindow?.setContentProtection(windowState.contentProtection);
|
|
94
|
+
createMenu(); // Refresh menu to update checkbox state
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
],
|
|
98
|
+
},
|
|
99
|
+
// Window menu
|
|
100
|
+
{
|
|
101
|
+
label: 'Window',
|
|
102
|
+
submenu: [
|
|
103
|
+
{ role: 'minimize' as const },
|
|
104
|
+
{ role: 'zoom' as const },
|
|
105
|
+
...(isMac
|
|
106
|
+
? [
|
|
107
|
+
{ type: 'separator' as const },
|
|
108
|
+
{ role: 'front' as const },
|
|
109
|
+
{ type: 'separator' as const },
|
|
110
|
+
{ role: 'window' as const },
|
|
111
|
+
]
|
|
112
|
+
: [{ role: 'close' as const }]),
|
|
113
|
+
],
|
|
114
|
+
},
|
|
115
|
+
];
|
|
116
|
+
|
|
117
|
+
const menu = Menu.buildFromTemplate(template);
|
|
118
|
+
Menu.setApplicationMenu(menu);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function createWindow(): void {
|
|
122
|
+
// Determine preload path based on environment
|
|
123
|
+
const preloadPath = path.join(__dirname, 'preload.js');
|
|
124
|
+
|
|
125
|
+
mainWindow = new BrowserWindow({
|
|
126
|
+
width: 1280,
|
|
127
|
+
height: 800,
|
|
128
|
+
minWidth: 800,
|
|
129
|
+
minHeight: 600,
|
|
130
|
+
webPreferences: {
|
|
131
|
+
preload: preloadPath,
|
|
132
|
+
contextIsolation: true,
|
|
133
|
+
nodeIntegration: false,
|
|
134
|
+
sandbox: true,
|
|
135
|
+
},
|
|
136
|
+
// macOS specific styling
|
|
137
|
+
titleBarStyle: process.platform === 'darwin' ? 'hiddenInset' : 'default',
|
|
138
|
+
trafficLightPosition: { x: 16, y: 16 },
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
// Load the app with retry logic for dev server
|
|
142
|
+
// Increased retries and delay to handle Vite restart during dep-scan
|
|
143
|
+
const loadApp = async (retries = 10, delay = 2000): Promise<void> => {
|
|
144
|
+
if (!mainWindow) return;
|
|
145
|
+
|
|
146
|
+
if (MAIN_WINDOW_VITE_DEV_SERVER_URL) {
|
|
147
|
+
try {
|
|
148
|
+
await mainWindow.loadURL(MAIN_WINDOW_VITE_DEV_SERVER_URL);
|
|
149
|
+
} catch (error) {
|
|
150
|
+
if (retries > 0) {
|
|
151
|
+
console.log(`Failed to load dev server, retrying in ${delay}ms... (${retries} retries left)`);
|
|
152
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
153
|
+
return loadApp(retries - 1, delay);
|
|
154
|
+
}
|
|
155
|
+
console.error('Failed to load dev server after all retries:', error);
|
|
156
|
+
}
|
|
157
|
+
} else {
|
|
158
|
+
mainWindow.loadFile(path.join(__dirname, `../renderer/${MAIN_WINDOW_VITE_NAME}/index.html`));
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
loadApp();
|
|
163
|
+
|
|
164
|
+
// Open DevTools in development
|
|
165
|
+
if (process.env.NODE_ENV === 'development' || MAIN_WINDOW_VITE_DEV_SERVER_URL) {
|
|
166
|
+
mainWindow.webContents.openDevTools();
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
mainWindow.on('closed', () => {
|
|
170
|
+
mainWindow = null;
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Setup IPC handlers
|
|
175
|
+
function setupIpcHandlers(): void {
|
|
176
|
+
// Toggle always on top
|
|
177
|
+
ipcMain.handle('window:setAlwaysOnTop', (_event, enable: boolean) => {
|
|
178
|
+
if (mainWindow) {
|
|
179
|
+
windowState.alwaysOnTop = enable;
|
|
180
|
+
mainWindow.setAlwaysOnTop(enable, 'floating');
|
|
181
|
+
createMenu(); // Update menu checkbox state
|
|
182
|
+
return true;
|
|
183
|
+
}
|
|
184
|
+
return false;
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
// Toggle content protection (hide from screen sharing)
|
|
188
|
+
ipcMain.handle('window:setContentProtection', (_event, enable: boolean) => {
|
|
189
|
+
if (mainWindow) {
|
|
190
|
+
windowState.contentProtection = enable;
|
|
191
|
+
mainWindow.setContentProtection(enable);
|
|
192
|
+
createMenu(); // Update menu checkbox state
|
|
193
|
+
return true;
|
|
194
|
+
}
|
|
195
|
+
return false;
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
// Get current window state
|
|
199
|
+
ipcMain.handle('window:getState', () => {
|
|
200
|
+
return { ...windowState };
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// App lifecycle
|
|
205
|
+
app.whenReady().then(() => {
|
|
206
|
+
setupIpcHandlers();
|
|
207
|
+
createMenu();
|
|
208
|
+
createWindow();
|
|
209
|
+
|
|
210
|
+
app.on('activate', () => {
|
|
211
|
+
// On macOS, re-create window when dock icon is clicked
|
|
212
|
+
if (BrowserWindow.getAllWindows().length === 0) {
|
|
213
|
+
createWindow();
|
|
214
|
+
}
|
|
215
|
+
});
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
app.on('window-all-closed', () => {
|
|
219
|
+
// On macOS, keep app running until explicitly quit
|
|
220
|
+
if (process.platform !== 'darwin') {
|
|
221
|
+
app.quit();
|
|
222
|
+
}
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
// Declare globals injected by Electron Forge Vite plugin
|
|
226
|
+
declare const MAIN_WINDOW_VITE_DEV_SERVER_URL: string | undefined;
|
|
227
|
+
declare const MAIN_WINDOW_VITE_NAME: string;
|