@su-record/vibe 2.7.13 → 2.7.15
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/.env.example +37 -37
- package/CLAUDE.md +134 -126
- package/LICENSE +21 -21
- package/README.md +449 -449
- package/agents/architect-low.md +41 -41
- package/agents/architect-medium.md +59 -59
- package/agents/architect.md +80 -80
- package/agents/build-error-resolver.md +115 -115
- package/agents/compounder.md +261 -261
- package/agents/diagrammer.md +178 -178
- package/agents/docs/api-documenter.md +99 -99
- package/agents/docs/changelog-writer.md +93 -93
- package/agents/e2e-tester.md +294 -294
- package/agents/explorer-low.md +42 -42
- package/agents/explorer-medium.md +59 -59
- package/agents/explorer.md +48 -48
- package/agents/implementer-low.md +43 -43
- package/agents/implementer-medium.md +52 -52
- package/agents/implementer.md +54 -54
- package/agents/junior-mentor.md +141 -141
- package/agents/planning/requirements-analyst.md +84 -84
- package/agents/planning/ux-advisor.md +83 -83
- package/agents/qa/acceptance-tester.md +86 -86
- package/agents/qa/edge-case-finder.md +93 -93
- package/agents/refactor-cleaner.md +143 -143
- package/agents/research/best-practices-agent.md +199 -199
- package/agents/research/codebase-patterns-agent.md +157 -157
- package/agents/research/framework-docs-agent.md +188 -188
- package/agents/research/security-advisory-agent.md +213 -213
- package/agents/review/architecture-reviewer.md +107 -107
- package/agents/review/complexity-reviewer.md +116 -116
- package/agents/review/data-integrity-reviewer.md +88 -88
- package/agents/review/git-history-reviewer.md +103 -103
- package/agents/review/performance-reviewer.md +86 -86
- package/agents/review/python-reviewer.md +150 -150
- package/agents/review/rails-reviewer.md +139 -139
- package/agents/review/react-reviewer.md +144 -144
- package/agents/review/security-reviewer.md +80 -80
- package/agents/review/simplicity-reviewer.md +140 -140
- package/agents/review/test-coverage-reviewer.md +116 -116
- package/agents/review/typescript-reviewer.md +127 -127
- package/agents/searcher.md +54 -54
- package/agents/simplifier.md +120 -120
- package/agents/tester.md +49 -49
- package/agents/ui/ui-a11y-auditor.md +93 -93
- package/agents/ui/ui-antipattern-detector.md +94 -94
- package/agents/ui/ui-dataviz-advisor.md +69 -69
- package/agents/ui/ui-design-system-gen.md +57 -57
- package/agents/ui/ui-industry-analyzer.md +49 -49
- package/agents/ui/ui-layout-architect.md +65 -65
- package/agents/ui/ui-stack-implementer.md +68 -68
- package/agents/ui/ux-compliance-reviewer.md +81 -81
- package/agents/ui-previewer.md +258 -260
- package/commands/vibe.analyze.md +11 -13
- package/commands/vibe.review.md +43 -1
- package/commands/vibe.run.md +2124 -2078
- package/commands/vibe.spec.md +9 -4
- package/commands/vibe.spec.review.md +569 -565
- package/commands/vibe.utils.md +413 -413
- package/commands/vibe.verify.md +33 -8
- package/dist/cli/collaborator.js +52 -52
- package/dist/cli/commands/evolution.js +12 -12
- package/dist/cli/commands/info.js +54 -54
- package/dist/cli/commands/init.js +5 -5
- package/dist/cli/commands/remove.js +14 -14
- package/dist/cli/commands/sentinel.js +27 -27
- package/dist/cli/commands/skills.js +5 -5
- package/dist/cli/commands/slack.js +10 -10
- package/dist/cli/commands/telegram.js +12 -12
- package/dist/cli/detect.js +32 -32
- package/dist/cli/index.js +51 -51
- package/dist/cli/llm/claude-commands.js +16 -16
- package/dist/cli/llm/config.js +19 -19
- package/dist/cli/llm/config.js.map +1 -1
- package/dist/cli/llm/gemini-commands.js +16 -16
- package/dist/cli/llm/gpt-commands.js +19 -19
- package/dist/cli/llm/help.js +21 -21
- package/dist/cli/postinstall/cursor-agents.js +32 -32
- package/dist/cli/postinstall/cursor-rules.js +83 -83
- package/dist/cli/postinstall/cursor-skills.js +743 -743
- package/dist/cli/setup/Provisioner.js +42 -42
- package/dist/cli/types.d.ts +0 -2
- package/dist/cli/types.d.ts.map +1 -1
- package/dist/infra/lib/DeepInit.js +24 -24
- package/dist/infra/lib/IterationTracker.js +11 -11
- package/dist/infra/lib/PythonParser.js +108 -108
- package/dist/infra/lib/ReviewRace.js +96 -96
- package/dist/infra/lib/SkillFrontmatter.js +28 -28
- package/dist/infra/lib/SkillQualityGate.js +9 -9
- package/dist/infra/lib/SkillRepository.js +159 -159
- package/dist/infra/lib/UltraQA.js +99 -99
- package/dist/infra/lib/autonomy/AuditStore.js +41 -41
- package/dist/infra/lib/autonomy/ConfirmationStore.js +30 -30
- package/dist/infra/lib/autonomy/EventOutbox.js +38 -38
- package/dist/infra/lib/autonomy/PolicyEngine.js +18 -18
- package/dist/infra/lib/autonomy/SecuritySentinel.js +1 -1
- package/dist/infra/lib/autonomy/SuggestionStore.js +33 -33
- package/dist/infra/lib/embedding/VectorStore.js +22 -22
- package/dist/infra/lib/evolution/AgentAnalyzer.js +10 -10
- package/dist/infra/lib/evolution/DescriptionOptimizer.js +21 -21
- package/dist/infra/lib/evolution/GenerationRegistry.js +36 -36
- package/dist/infra/lib/evolution/InsightStore.js +90 -90
- package/dist/infra/lib/evolution/RollbackManager.js +5 -5
- package/dist/infra/lib/evolution/SkillBenchmark.js +23 -23
- package/dist/infra/lib/evolution/SkillEvalRunner.js +50 -50
- package/dist/infra/lib/evolution/SkillGapDetector.js +10 -10
- package/dist/infra/lib/evolution/UsageTracker.js +28 -28
- package/dist/infra/lib/gemini/orchestration.js +5 -5
- package/dist/infra/lib/gpt/orchestration.js +4 -4
- package/dist/infra/lib/memory/KnowledgeGraph.js +4 -4
- package/dist/infra/lib/memory/MemorySearch.js +57 -57
- package/dist/infra/lib/memory/MemoryStorage.js +181 -181
- package/dist/infra/lib/memory/ObservationStore.js +28 -28
- package/dist/infra/lib/memory/ReflectionStore.js +30 -30
- package/dist/infra/lib/memory/SessionRAGRetriever.js +7 -7
- package/dist/infra/lib/memory/SessionRAGStore.js +225 -225
- package/dist/infra/lib/memory/SessionSummarizer.js +9 -9
- package/dist/infra/orchestrator/AgentManager.js +12 -12
- package/dist/infra/orchestrator/AgentRegistry.js +65 -65
- package/dist/infra/orchestrator/MultiLlmResearch.js +8 -8
- package/dist/infra/orchestrator/SwarmOrchestrator.test.js +16 -16
- package/dist/infra/orchestrator/parallelResearch.js +24 -24
- package/dist/tools/convention/analyzeComplexity.test.js +115 -115
- package/dist/tools/convention/validateCodeQuality.test.js +104 -104
- package/dist/tools/memory/createMemoryTimeline.js +10 -10
- package/dist/tools/memory/getMemoryGraph.js +12 -12
- package/dist/tools/memory/getSessionContext.js +9 -9
- package/dist/tools/memory/linkMemories.js +14 -14
- package/dist/tools/memory/listMemories.js +4 -4
- package/dist/tools/memory/recallMemory.js +4 -4
- package/dist/tools/memory/saveMemory.js +4 -4
- package/dist/tools/memory/searchMemoriesAdvanced.js +23 -23
- package/dist/tools/semantic/analyzeDependencyGraph.js +12 -12
- package/dist/tools/semantic/astGrep.test.js +6 -6
- package/dist/tools/spec/prdParser.test.js +171 -171
- package/dist/tools/spec/specGenerator.js +169 -169
- package/dist/tools/spec/traceabilityMatrix.js +64 -64
- package/dist/tools/spec/traceabilityMatrix.test.js +28 -28
- package/hooks/gemini-hooks.json +73 -73
- package/hooks/hooks.json +137 -137
- package/hooks/scripts/code-check.js +77 -70
- package/hooks/scripts/context-save.js +212 -212
- package/hooks/scripts/hud-status.js +291 -291
- package/hooks/scripts/keyword-detector.js +214 -214
- package/hooks/scripts/llm-orchestrate.js +475 -475
- package/hooks/scripts/post-edit.js +32 -32
- package/hooks/scripts/pre-tool-guard.js +125 -125
- package/hooks/scripts/prompt-dispatcher.js +185 -185
- package/hooks/scripts/sentinel-guard.js +104 -104
- package/hooks/scripts/session-start.js +106 -106
- package/hooks/scripts/stop-notify.js +209 -209
- package/hooks/scripts/utils.js +100 -100
- package/languages/csharp-unity.md +515 -515
- package/languages/gdscript-godot.md +470 -470
- package/languages/ruby-rails.md +489 -489
- package/languages/typescript-angular.md +433 -433
- package/languages/typescript-astro.md +416 -416
- package/languages/typescript-electron.md +406 -406
- package/languages/typescript-nestjs.md +524 -524
- package/languages/typescript-svelte.md +407 -407
- package/languages/typescript-tauri.md +365 -365
- package/package.json +121 -121
- package/skills/agents-md/SKILL.md +120 -120
- package/skills/arch-guard/SKILL.md +180 -180
- package/skills/brand-assets/SKILL.md +146 -146
- package/skills/capability-loop/SKILL.md +167 -167
- package/skills/characterization-test/SKILL.md +206 -206
- package/skills/commerce-patterns/SKILL.md +59 -59
- package/skills/commit-push-pr/SKILL.md +75 -75
- package/skills/context7-usage/SKILL.md +105 -105
- package/skills/core-capabilities/SKILL.md +48 -48
- package/skills/e2e-commerce/SKILL.md +57 -57
- package/skills/exec-plan/SKILL.md +147 -147
- package/skills/frontend-design/SKILL.md +73 -73
- package/skills/git-worktree/SKILL.md +72 -72
- package/skills/handoff/SKILL.md +109 -109
- package/skills/parallel-research/SKILL.md +87 -87
- package/skills/priority-todos/SKILL.md +63 -63
- package/skills/seo-checklist/SKILL.md +57 -57
- package/skills/techdebt/SKILL.md +122 -122
- package/skills/tool-fallback/SKILL.md +103 -103
- package/skills/typescript-advanced-types/SKILL.md +66 -65
- package/skills/ui-ux-pro-max/SKILL.md +206 -206
- package/skills/vercel-react-best-practices/SKILL.md +59 -59
- package/skills/video-production/SKILL.md +51 -51
- package/vibe/config.json +29 -29
- package/vibe/constitution.md +227 -227
- package/vibe/rules/principles/communication-guide.md +98 -98
- package/vibe/rules/principles/development-philosophy.md +52 -52
- package/vibe/rules/principles/quick-start.md +102 -102
- package/vibe/rules/quality/bdd-contract-testing.md +393 -393
- package/vibe/rules/quality/checklist.md +276 -276
- package/vibe/rules/quality/performance.md +236 -236
- package/vibe/rules/quality/testing-strategy.md +440 -440
- package/vibe/rules/standards/anti-patterns.md +541 -541
- package/vibe/rules/standards/code-structure.md +291 -291
- package/vibe/rules/standards/complexity-metrics.md +313 -313
- package/vibe/rules/standards/git-workflow.md +237 -237
- package/vibe/rules/standards/naming-conventions.md +198 -198
- package/vibe/rules/standards/security.md +305 -305
- package/vibe/rules/writing/document-style.md +74 -74
- package/vibe/setup.sh +31 -31
- package/vibe/templates/constitution-template.md +252 -252
- package/vibe/templates/contract-backend-template.md +526 -526
- package/vibe/templates/contract-frontend-template.md +599 -599
- package/vibe/templates/feature-template.md +96 -96
- package/vibe/templates/spec-template.md +221 -221
- package/vibe/ui-ux-data/charts.csv +26 -26
- package/vibe/ui-ux-data/colors.csv +97 -97
- package/vibe/ui-ux-data/icons.csv +101 -101
- package/vibe/ui-ux-data/landing.csv +31 -31
- package/vibe/ui-ux-data/products.csv +96 -96
- package/vibe/ui-ux-data/react-performance.csv +45 -45
- package/vibe/ui-ux-data/stacks/astro.csv +54 -54
- package/vibe/ui-ux-data/stacks/flutter.csv +53 -53
- package/vibe/ui-ux-data/stacks/html-tailwind.csv +56 -56
- package/vibe/ui-ux-data/stacks/jetpack-compose.csv +53 -53
- package/vibe/ui-ux-data/stacks/nextjs.csv +53 -53
- package/vibe/ui-ux-data/stacks/nuxt-ui.csv +51 -51
- package/vibe/ui-ux-data/stacks/nuxtjs.csv +59 -59
- package/vibe/ui-ux-data/stacks/react-native.csv +52 -52
- package/vibe/ui-ux-data/stacks/react.csv +54 -54
- package/vibe/ui-ux-data/stacks/shadcn.csv +61 -61
- package/vibe/ui-ux-data/stacks/svelte.csv +54 -54
- package/vibe/ui-ux-data/stacks/swiftui.csv +51 -51
- package/vibe/ui-ux-data/stacks/vue.csv +50 -50
- package/vibe/ui-ux-data/styles.csv +68 -68
- package/vibe/ui-ux-data/typography.csv +57 -57
- package/vibe/ui-ux-data/ui-reasoning.csv +101 -101
- package/vibe/ui-ux-data/ux-guidelines.csv +99 -99
- package/vibe/ui-ux-data/version.json +31 -31
- package/vibe/ui-ux-data/web-interface.csv +31 -31
|
@@ -1,406 +1,406 @@
|
|
|
1
|
-
# TypeScript + Electron Quality Rules
|
|
2
|
-
|
|
3
|
-
## Core Principles (inherited from core)
|
|
4
|
-
|
|
5
|
-
```markdown
|
|
6
|
-
# Core Principles (inherited from core)
|
|
7
|
-
Single Responsibility (SRP)
|
|
8
|
-
No Duplication (DRY)
|
|
9
|
-
Reusability
|
|
10
|
-
Low Complexity
|
|
11
|
-
Function <= 30 lines
|
|
12
|
-
Nesting <= 3 levels
|
|
13
|
-
Cyclomatic complexity <= 10
|
|
14
|
-
```
|
|
15
|
-
|
|
16
|
-
## Electron Architecture Understanding
|
|
17
|
-
|
|
18
|
-
```text
|
|
19
|
-
Main Process (Node.js)
|
|
20
|
-
- App lifecycle management
|
|
21
|
-
- System APIs (file, network)
|
|
22
|
-
- BrowserWindow creation/management
|
|
23
|
-
|
|
24
|
-
Preload Script (Isolated Context)
|
|
25
|
-
- Expose APIs via contextBridge
|
|
26
|
-
- Main <-> Renderer bridge
|
|
27
|
-
|
|
28
|
-
Renderer Process (Chromium)
|
|
29
|
-
- UI rendering (React/Vue/etc)
|
|
30
|
-
- Use window.electronAPI
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
## TypeScript/Electron Specific Rules
|
|
34
|
-
|
|
35
|
-
### 1. Process Separation Required
|
|
36
|
-
|
|
37
|
-
```typescript
|
|
38
|
-
// Bad: Direct Node.js usage in Renderer (security vulnerability)
|
|
39
|
-
// nodeIntegration: true is prohibited!
|
|
40
|
-
|
|
41
|
-
// Good: Main Process (main.ts)
|
|
42
|
-
import { app, BrowserWindow, ipcMain } from 'electron';
|
|
43
|
-
import path from 'path';
|
|
44
|
-
|
|
45
|
-
function createWindow(): BrowserWindow {
|
|
46
|
-
const win = new BrowserWindow({
|
|
47
|
-
width: 800,
|
|
48
|
-
height: 600,
|
|
49
|
-
webPreferences: {
|
|
50
|
-
preload: path.join(__dirname, 'preload.js'),
|
|
51
|
-
contextIsolation: true, // Required!
|
|
52
|
-
nodeIntegration: false, // Required!
|
|
53
|
-
sandbox: true // Recommended
|
|
54
|
-
}
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
win.loadFile('index.html');
|
|
58
|
-
return win;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
app.whenReady().then(createWindow);
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
### 2. Preload Script Pattern
|
|
65
|
-
|
|
66
|
-
```typescript
|
|
67
|
-
// preload.ts
|
|
68
|
-
import { contextBridge, ipcRenderer } from 'electron';
|
|
69
|
-
|
|
70
|
-
// Good: Type definition
|
|
71
|
-
interface ElectronAPI {
|
|
72
|
-
readFile: (path: string) => Promise<string>;
|
|
73
|
-
writeFile: (path: string, content: string) => Promise<void>;
|
|
74
|
-
onFileChanged: (callback: (path: string) => void) => () => void;
|
|
75
|
-
platform: NodeJS.Platform;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
// Good: Safely expose API
|
|
79
|
-
contextBridge.exposeInMainWorld('electronAPI', {
|
|
80
|
-
readFile: (path: string) => ipcRenderer.invoke('read-file', path),
|
|
81
|
-
writeFile: (path: string, content: string) =>
|
|
82
|
-
ipcRenderer.invoke('write-file', path, content),
|
|
83
|
-
onFileChanged: (callback: (path: string) => void) => {
|
|
84
|
-
const handler = (_event: Electron.IpcRendererEvent, path: string) => callback(path);
|
|
85
|
-
ipcRenderer.on('file-changed', handler);
|
|
86
|
-
return () => ipcRenderer.removeListener('file-changed', handler);
|
|
87
|
-
},
|
|
88
|
-
platform: process.platform
|
|
89
|
-
} satisfies ElectronAPI);
|
|
90
|
-
|
|
91
|
-
// Good: Type declaration (for use in renderer)
|
|
92
|
-
declare global {
|
|
93
|
-
interface Window {
|
|
94
|
-
electronAPI: ElectronAPI;
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
```
|
|
98
|
-
|
|
99
|
-
### 3. IPC Communication Type Safety
|
|
100
|
-
|
|
101
|
-
```typescript
|
|
102
|
-
// shared/ipc-types.ts
|
|
103
|
-
export interface IpcChannels {
|
|
104
|
-
'read-file': { args: [string]; return: string };
|
|
105
|
-
'write-file': { args: [string, string]; return: void };
|
|
106
|
-
'get-app-info': { args: []; return: AppInfo };
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
export interface AppInfo {
|
|
110
|
-
version: string;
|
|
111
|
-
name: string;
|
|
112
|
-
paths: {
|
|
113
|
-
userData: string;
|
|
114
|
-
temp: string;
|
|
115
|
-
};
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
// main.ts
|
|
119
|
-
import { ipcMain } from 'electron';
|
|
120
|
-
import fs from 'fs/promises';
|
|
121
|
-
|
|
122
|
-
// Good: Type-safe handler
|
|
123
|
-
ipcMain.handle('read-file', async (_event, path: string): Promise<string> => {
|
|
124
|
-
return fs.readFile(path, 'utf-8');
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
ipcMain.handle('write-file', async (_event, path: string, content: string): Promise<void> => {
|
|
128
|
-
await fs.writeFile(path, content, 'utf-8');
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
ipcMain.handle('get-app-info', async (): Promise<AppInfo> => {
|
|
132
|
-
return {
|
|
133
|
-
version: app.getVersion(),
|
|
134
|
-
name: app.getName(),
|
|
135
|
-
paths: {
|
|
136
|
-
userData: app.getPath('userData'),
|
|
137
|
-
temp: app.getPath('temp')
|
|
138
|
-
}
|
|
139
|
-
};
|
|
140
|
-
});
|
|
141
|
-
```
|
|
142
|
-
|
|
143
|
-
### 4. IPC Usage in Renderer
|
|
144
|
-
|
|
145
|
-
```typescript
|
|
146
|
-
// renderer/hooks/useElectron.ts
|
|
147
|
-
|
|
148
|
-
// Good: Custom Hook
|
|
149
|
-
function useFileReader() {
|
|
150
|
-
const [content, setContent] = useState<string | null>(null);
|
|
151
|
-
const [loading, setLoading] = useState(false);
|
|
152
|
-
const [error, setError] = useState<string | null>(null);
|
|
153
|
-
|
|
154
|
-
const readFile = useCallback(async (path: string) => {
|
|
155
|
-
setLoading(true);
|
|
156
|
-
setError(null);
|
|
157
|
-
try {
|
|
158
|
-
const result = await window.electronAPI.readFile(path);
|
|
159
|
-
setContent(result);
|
|
160
|
-
return result;
|
|
161
|
-
} catch (e) {
|
|
162
|
-
const msg = e instanceof Error ? e.message : 'Unknown error';
|
|
163
|
-
setError(msg);
|
|
164
|
-
throw e;
|
|
165
|
-
} finally {
|
|
166
|
-
setLoading(false);
|
|
167
|
-
}
|
|
168
|
-
}, []);
|
|
169
|
-
|
|
170
|
-
return { content, loading, error, readFile };
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
// Good: Event subscription Hook
|
|
174
|
-
function useFileWatcher(onChanged: (path: string) => void) {
|
|
175
|
-
useEffect(() => {
|
|
176
|
-
const unsubscribe = window.electronAPI.onFileChanged(onChanged);
|
|
177
|
-
return unsubscribe;
|
|
178
|
-
}, [onChanged]);
|
|
179
|
-
}
|
|
180
|
-
```
|
|
181
|
-
|
|
182
|
-
### 5. Window Management
|
|
183
|
-
|
|
184
|
-
```typescript
|
|
185
|
-
// main.ts
|
|
186
|
-
import { BrowserWindow, screen } from 'electron';
|
|
187
|
-
|
|
188
|
-
// Good: Save/restore window state
|
|
189
|
-
interface WindowState {
|
|
190
|
-
x?: number;
|
|
191
|
-
y?: number;
|
|
192
|
-
width: number;
|
|
193
|
-
height: number;
|
|
194
|
-
isMaximized: boolean;
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
function createWindowWithState(): BrowserWindow {
|
|
198
|
-
const state = loadWindowState();
|
|
199
|
-
|
|
200
|
-
const win = new BrowserWindow({
|
|
201
|
-
x: state.x,
|
|
202
|
-
y: state.y,
|
|
203
|
-
width: state.width,
|
|
204
|
-
height: state.height,
|
|
205
|
-
webPreferences: {
|
|
206
|
-
preload: path.join(__dirname, 'preload.js'),
|
|
207
|
-
contextIsolation: true,
|
|
208
|
-
nodeIntegration: false
|
|
209
|
-
}
|
|
210
|
-
});
|
|
211
|
-
|
|
212
|
-
if (state.isMaximized) {
|
|
213
|
-
win.maximize();
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
// Save state on change
|
|
217
|
-
win.on('close', () => {
|
|
218
|
-
saveWindowState({
|
|
219
|
-
...win.getBounds(),
|
|
220
|
-
isMaximized: win.isMaximized()
|
|
221
|
-
});
|
|
222
|
-
});
|
|
223
|
-
|
|
224
|
-
return win;
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
// Good: Multiple window management
|
|
228
|
-
const windows = new Map<string, BrowserWindow>();
|
|
229
|
-
|
|
230
|
-
function getOrCreateWindow(id: string): BrowserWindow {
|
|
231
|
-
const existing = windows.get(id);
|
|
232
|
-
if (existing && !existing.isDestroyed()) {
|
|
233
|
-
existing.focus();
|
|
234
|
-
return existing;
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
const win = new BrowserWindow({ /* ... */ });
|
|
238
|
-
windows.set(id, win);
|
|
239
|
-
win.on('closed', () => windows.delete(id));
|
|
240
|
-
return win;
|
|
241
|
-
}
|
|
242
|
-
```
|
|
243
|
-
|
|
244
|
-
### 6. Menu Configuration
|
|
245
|
-
|
|
246
|
-
```typescript
|
|
247
|
-
import { Menu, MenuItemConstructorOptions } from 'electron';
|
|
248
|
-
|
|
249
|
-
// Good: Platform-specific menu
|
|
250
|
-
function createMenu(): Menu {
|
|
251
|
-
const isMac = process.platform === 'darwin';
|
|
252
|
-
|
|
253
|
-
const template: MenuItemConstructorOptions[] = [
|
|
254
|
-
...(isMac ? [{
|
|
255
|
-
label: app.name,
|
|
256
|
-
submenu: [
|
|
257
|
-
{ role: 'about' as const },
|
|
258
|
-
{ type: 'separator' as const },
|
|
259
|
-
{ role: 'quit' as const }
|
|
260
|
-
]
|
|
261
|
-
}] : []),
|
|
262
|
-
{
|
|
263
|
-
label: 'File',
|
|
264
|
-
submenu: [
|
|
265
|
-
{
|
|
266
|
-
label: 'Open',
|
|
267
|
-
accelerator: 'CmdOrCtrl+O',
|
|
268
|
-
click: () => handleOpen()
|
|
269
|
-
},
|
|
270
|
-
{
|
|
271
|
-
label: 'Save',
|
|
272
|
-
accelerator: 'CmdOrCtrl+S',
|
|
273
|
-
click: () => handleSave()
|
|
274
|
-
},
|
|
275
|
-
{ type: 'separator' },
|
|
276
|
-
isMac ? { role: 'close' } : { role: 'quit' }
|
|
277
|
-
]
|
|
278
|
-
}
|
|
279
|
-
];
|
|
280
|
-
|
|
281
|
-
return Menu.buildFromTemplate(template);
|
|
282
|
-
}
|
|
283
|
-
```
|
|
284
|
-
|
|
285
|
-
### 7. Auto Update
|
|
286
|
-
|
|
287
|
-
```typescript
|
|
288
|
-
import { autoUpdater } from 'electron-updater';
|
|
289
|
-
|
|
290
|
-
// Good: Auto update setup
|
|
291
|
-
function setupAutoUpdater(): void {
|
|
292
|
-
autoUpdater.autoDownload = false;
|
|
293
|
-
autoUpdater.autoInstallOnAppQuit = true;
|
|
294
|
-
|
|
295
|
-
autoUpdater.on('update-available', (info) => {
|
|
296
|
-
// Notify user
|
|
297
|
-
dialog.showMessageBox({
|
|
298
|
-
type: 'info',
|
|
299
|
-
title: 'Update Available',
|
|
300
|
-
message: `Version ${info.version} is available.`,
|
|
301
|
-
buttons: ['Download', 'Later']
|
|
302
|
-
}).then(({ response }) => {
|
|
303
|
-
if (response === 0) {
|
|
304
|
-
autoUpdater.downloadUpdate();
|
|
305
|
-
}
|
|
306
|
-
});
|
|
307
|
-
});
|
|
308
|
-
|
|
309
|
-
autoUpdater.on('update-downloaded', () => {
|
|
310
|
-
dialog.showMessageBox({
|
|
311
|
-
type: 'info',
|
|
312
|
-
title: 'Update Ready',
|
|
313
|
-
message: 'Restart to install update?',
|
|
314
|
-
buttons: ['Restart', 'Later']
|
|
315
|
-
}).then(({ response }) => {
|
|
316
|
-
if (response === 0) {
|
|
317
|
-
autoUpdater.quitAndInstall();
|
|
318
|
-
}
|
|
319
|
-
});
|
|
320
|
-
});
|
|
321
|
-
|
|
322
|
-
// Check for updates on app start
|
|
323
|
-
autoUpdater.checkForUpdates();
|
|
324
|
-
}
|
|
325
|
-
```
|
|
326
|
-
|
|
327
|
-
### 8. Security Checklist
|
|
328
|
-
|
|
329
|
-
```typescript
|
|
330
|
-
// Good: Validate security settings
|
|
331
|
-
function validateSecuritySettings(win: BrowserWindow): void {
|
|
332
|
-
const webPrefs = win.webContents.getWebPreferences();
|
|
333
|
-
|
|
334
|
-
if (webPrefs.nodeIntegration) {
|
|
335
|
-
console.error('SECURITY: nodeIntegration should be false');
|
|
336
|
-
}
|
|
337
|
-
if (!webPrefs.contextIsolation) {
|
|
338
|
-
console.error('SECURITY: contextIsolation should be true');
|
|
339
|
-
}
|
|
340
|
-
if (!webPrefs.sandbox) {
|
|
341
|
-
console.warn('SECURITY: sandbox is recommended');
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
// Good: Handle external links
|
|
346
|
-
win.webContents.setWindowOpenHandler(({ url }) => {
|
|
347
|
-
// Open external URLs in system browser
|
|
348
|
-
if (url.startsWith('https://')) {
|
|
349
|
-
shell.openExternal(url);
|
|
350
|
-
}
|
|
351
|
-
return { action: 'deny' };
|
|
352
|
-
});
|
|
353
|
-
```
|
|
354
|
-
|
|
355
|
-
## Recommended Folder Structure
|
|
356
|
-
|
|
357
|
-
```text
|
|
358
|
-
my-electron-app/
|
|
359
|
-
├── src/
|
|
360
|
-
│ ├── main/ # Main Process
|
|
361
|
-
│ │ ├── main.ts
|
|
362
|
-
│ │ ├── ipc-handlers.ts
|
|
363
|
-
│ │ └── menu.ts
|
|
364
|
-
│ ├── preload/ # Preload Scripts
|
|
365
|
-
│ │ └── preload.ts
|
|
366
|
-
│ ├── renderer/ # Renderer (React/Vue)
|
|
367
|
-
│ │ ├── components/
|
|
368
|
-
│ │ ├── hooks/
|
|
369
|
-
│ │ └── App.tsx
|
|
370
|
-
│ └── shared/ # Shared types
|
|
371
|
-
│ └── ipc-types.ts
|
|
372
|
-
├── electron-builder.yml
|
|
373
|
-
└── package.json
|
|
374
|
-
```
|
|
375
|
-
|
|
376
|
-
## Build Configuration (electron-builder)
|
|
377
|
-
|
|
378
|
-
```yaml
|
|
379
|
-
# electron-builder.yml
|
|
380
|
-
appId: com.example.myapp
|
|
381
|
-
productName: MyApp
|
|
382
|
-
directories:
|
|
383
|
-
output: dist
|
|
384
|
-
files:
|
|
385
|
-
- "build/**/*"
|
|
386
|
-
- "node_modules/**/*"
|
|
387
|
-
mac:
|
|
388
|
-
target: [dmg, zip]
|
|
389
|
-
category: public.app-category.developer-tools
|
|
390
|
-
win:
|
|
391
|
-
target: [nsis, portable]
|
|
392
|
-
linux:
|
|
393
|
-
target: [AppImage, deb]
|
|
394
|
-
```
|
|
395
|
-
|
|
396
|
-
## Checklist
|
|
397
|
-
|
|
398
|
-
- [ ] `contextIsolation: true` configured
|
|
399
|
-
- [ ] `nodeIntegration: false` configured
|
|
400
|
-
- [ ] Expose APIs only through preload script
|
|
401
|
-
- [ ] Define IPC channel types
|
|
402
|
-
- [ ] Handle external links (setWindowOpenHandler)
|
|
403
|
-
- [ ] Save/restore window state
|
|
404
|
-
- [ ] Auto update setup
|
|
405
|
-
- [ ] Platform-specific menu configuration
|
|
406
|
-
- [ ] CSP header configured
|
|
1
|
+
# TypeScript + Electron Quality Rules
|
|
2
|
+
|
|
3
|
+
## Core Principles (inherited from core)
|
|
4
|
+
|
|
5
|
+
```markdown
|
|
6
|
+
# Core Principles (inherited from core)
|
|
7
|
+
Single Responsibility (SRP)
|
|
8
|
+
No Duplication (DRY)
|
|
9
|
+
Reusability
|
|
10
|
+
Low Complexity
|
|
11
|
+
Function <= 30 lines
|
|
12
|
+
Nesting <= 3 levels
|
|
13
|
+
Cyclomatic complexity <= 10
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Electron Architecture Understanding
|
|
17
|
+
|
|
18
|
+
```text
|
|
19
|
+
Main Process (Node.js)
|
|
20
|
+
- App lifecycle management
|
|
21
|
+
- System APIs (file, network)
|
|
22
|
+
- BrowserWindow creation/management
|
|
23
|
+
|
|
24
|
+
Preload Script (Isolated Context)
|
|
25
|
+
- Expose APIs via contextBridge
|
|
26
|
+
- Main <-> Renderer bridge
|
|
27
|
+
|
|
28
|
+
Renderer Process (Chromium)
|
|
29
|
+
- UI rendering (React/Vue/etc)
|
|
30
|
+
- Use window.electronAPI
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## TypeScript/Electron Specific Rules
|
|
34
|
+
|
|
35
|
+
### 1. Process Separation Required
|
|
36
|
+
|
|
37
|
+
```typescript
|
|
38
|
+
// Bad: Direct Node.js usage in Renderer (security vulnerability)
|
|
39
|
+
// nodeIntegration: true is prohibited!
|
|
40
|
+
|
|
41
|
+
// Good: Main Process (main.ts)
|
|
42
|
+
import { app, BrowserWindow, ipcMain } from 'electron';
|
|
43
|
+
import path from 'path';
|
|
44
|
+
|
|
45
|
+
function createWindow(): BrowserWindow {
|
|
46
|
+
const win = new BrowserWindow({
|
|
47
|
+
width: 800,
|
|
48
|
+
height: 600,
|
|
49
|
+
webPreferences: {
|
|
50
|
+
preload: path.join(__dirname, 'preload.js'),
|
|
51
|
+
contextIsolation: true, // Required!
|
|
52
|
+
nodeIntegration: false, // Required!
|
|
53
|
+
sandbox: true // Recommended
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
win.loadFile('index.html');
|
|
58
|
+
return win;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
app.whenReady().then(createWindow);
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### 2. Preload Script Pattern
|
|
65
|
+
|
|
66
|
+
```typescript
|
|
67
|
+
// preload.ts
|
|
68
|
+
import { contextBridge, ipcRenderer } from 'electron';
|
|
69
|
+
|
|
70
|
+
// Good: Type definition
|
|
71
|
+
interface ElectronAPI {
|
|
72
|
+
readFile: (path: string) => Promise<string>;
|
|
73
|
+
writeFile: (path: string, content: string) => Promise<void>;
|
|
74
|
+
onFileChanged: (callback: (path: string) => void) => () => void;
|
|
75
|
+
platform: NodeJS.Platform;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Good: Safely expose API
|
|
79
|
+
contextBridge.exposeInMainWorld('electronAPI', {
|
|
80
|
+
readFile: (path: string) => ipcRenderer.invoke('read-file', path),
|
|
81
|
+
writeFile: (path: string, content: string) =>
|
|
82
|
+
ipcRenderer.invoke('write-file', path, content),
|
|
83
|
+
onFileChanged: (callback: (path: string) => void) => {
|
|
84
|
+
const handler = (_event: Electron.IpcRendererEvent, path: string) => callback(path);
|
|
85
|
+
ipcRenderer.on('file-changed', handler);
|
|
86
|
+
return () => ipcRenderer.removeListener('file-changed', handler);
|
|
87
|
+
},
|
|
88
|
+
platform: process.platform
|
|
89
|
+
} satisfies ElectronAPI);
|
|
90
|
+
|
|
91
|
+
// Good: Type declaration (for use in renderer)
|
|
92
|
+
declare global {
|
|
93
|
+
interface Window {
|
|
94
|
+
electronAPI: ElectronAPI;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### 3. IPC Communication Type Safety
|
|
100
|
+
|
|
101
|
+
```typescript
|
|
102
|
+
// shared/ipc-types.ts
|
|
103
|
+
export interface IpcChannels {
|
|
104
|
+
'read-file': { args: [string]; return: string };
|
|
105
|
+
'write-file': { args: [string, string]; return: void };
|
|
106
|
+
'get-app-info': { args: []; return: AppInfo };
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export interface AppInfo {
|
|
110
|
+
version: string;
|
|
111
|
+
name: string;
|
|
112
|
+
paths: {
|
|
113
|
+
userData: string;
|
|
114
|
+
temp: string;
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// main.ts
|
|
119
|
+
import { ipcMain } from 'electron';
|
|
120
|
+
import fs from 'fs/promises';
|
|
121
|
+
|
|
122
|
+
// Good: Type-safe handler
|
|
123
|
+
ipcMain.handle('read-file', async (_event, path: string): Promise<string> => {
|
|
124
|
+
return fs.readFile(path, 'utf-8');
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
ipcMain.handle('write-file', async (_event, path: string, content: string): Promise<void> => {
|
|
128
|
+
await fs.writeFile(path, content, 'utf-8');
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
ipcMain.handle('get-app-info', async (): Promise<AppInfo> => {
|
|
132
|
+
return {
|
|
133
|
+
version: app.getVersion(),
|
|
134
|
+
name: app.getName(),
|
|
135
|
+
paths: {
|
|
136
|
+
userData: app.getPath('userData'),
|
|
137
|
+
temp: app.getPath('temp')
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
});
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### 4. IPC Usage in Renderer
|
|
144
|
+
|
|
145
|
+
```typescript
|
|
146
|
+
// renderer/hooks/useElectron.ts
|
|
147
|
+
|
|
148
|
+
// Good: Custom Hook
|
|
149
|
+
function useFileReader() {
|
|
150
|
+
const [content, setContent] = useState<string | null>(null);
|
|
151
|
+
const [loading, setLoading] = useState(false);
|
|
152
|
+
const [error, setError] = useState<string | null>(null);
|
|
153
|
+
|
|
154
|
+
const readFile = useCallback(async (path: string) => {
|
|
155
|
+
setLoading(true);
|
|
156
|
+
setError(null);
|
|
157
|
+
try {
|
|
158
|
+
const result = await window.electronAPI.readFile(path);
|
|
159
|
+
setContent(result);
|
|
160
|
+
return result;
|
|
161
|
+
} catch (e) {
|
|
162
|
+
const msg = e instanceof Error ? e.message : 'Unknown error';
|
|
163
|
+
setError(msg);
|
|
164
|
+
throw e;
|
|
165
|
+
} finally {
|
|
166
|
+
setLoading(false);
|
|
167
|
+
}
|
|
168
|
+
}, []);
|
|
169
|
+
|
|
170
|
+
return { content, loading, error, readFile };
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Good: Event subscription Hook
|
|
174
|
+
function useFileWatcher(onChanged: (path: string) => void) {
|
|
175
|
+
useEffect(() => {
|
|
176
|
+
const unsubscribe = window.electronAPI.onFileChanged(onChanged);
|
|
177
|
+
return unsubscribe;
|
|
178
|
+
}, [onChanged]);
|
|
179
|
+
}
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### 5. Window Management
|
|
183
|
+
|
|
184
|
+
```typescript
|
|
185
|
+
// main.ts
|
|
186
|
+
import { BrowserWindow, screen } from 'electron';
|
|
187
|
+
|
|
188
|
+
// Good: Save/restore window state
|
|
189
|
+
interface WindowState {
|
|
190
|
+
x?: number;
|
|
191
|
+
y?: number;
|
|
192
|
+
width: number;
|
|
193
|
+
height: number;
|
|
194
|
+
isMaximized: boolean;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function createWindowWithState(): BrowserWindow {
|
|
198
|
+
const state = loadWindowState();
|
|
199
|
+
|
|
200
|
+
const win = new BrowserWindow({
|
|
201
|
+
x: state.x,
|
|
202
|
+
y: state.y,
|
|
203
|
+
width: state.width,
|
|
204
|
+
height: state.height,
|
|
205
|
+
webPreferences: {
|
|
206
|
+
preload: path.join(__dirname, 'preload.js'),
|
|
207
|
+
contextIsolation: true,
|
|
208
|
+
nodeIntegration: false
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
if (state.isMaximized) {
|
|
213
|
+
win.maximize();
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// Save state on change
|
|
217
|
+
win.on('close', () => {
|
|
218
|
+
saveWindowState({
|
|
219
|
+
...win.getBounds(),
|
|
220
|
+
isMaximized: win.isMaximized()
|
|
221
|
+
});
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
return win;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// Good: Multiple window management
|
|
228
|
+
const windows = new Map<string, BrowserWindow>();
|
|
229
|
+
|
|
230
|
+
function getOrCreateWindow(id: string): BrowserWindow {
|
|
231
|
+
const existing = windows.get(id);
|
|
232
|
+
if (existing && !existing.isDestroyed()) {
|
|
233
|
+
existing.focus();
|
|
234
|
+
return existing;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
const win = new BrowserWindow({ /* ... */ });
|
|
238
|
+
windows.set(id, win);
|
|
239
|
+
win.on('closed', () => windows.delete(id));
|
|
240
|
+
return win;
|
|
241
|
+
}
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
### 6. Menu Configuration
|
|
245
|
+
|
|
246
|
+
```typescript
|
|
247
|
+
import { Menu, MenuItemConstructorOptions } from 'electron';
|
|
248
|
+
|
|
249
|
+
// Good: Platform-specific menu
|
|
250
|
+
function createMenu(): Menu {
|
|
251
|
+
const isMac = process.platform === 'darwin';
|
|
252
|
+
|
|
253
|
+
const template: MenuItemConstructorOptions[] = [
|
|
254
|
+
...(isMac ? [{
|
|
255
|
+
label: app.name,
|
|
256
|
+
submenu: [
|
|
257
|
+
{ role: 'about' as const },
|
|
258
|
+
{ type: 'separator' as const },
|
|
259
|
+
{ role: 'quit' as const }
|
|
260
|
+
]
|
|
261
|
+
}] : []),
|
|
262
|
+
{
|
|
263
|
+
label: 'File',
|
|
264
|
+
submenu: [
|
|
265
|
+
{
|
|
266
|
+
label: 'Open',
|
|
267
|
+
accelerator: 'CmdOrCtrl+O',
|
|
268
|
+
click: () => handleOpen()
|
|
269
|
+
},
|
|
270
|
+
{
|
|
271
|
+
label: 'Save',
|
|
272
|
+
accelerator: 'CmdOrCtrl+S',
|
|
273
|
+
click: () => handleSave()
|
|
274
|
+
},
|
|
275
|
+
{ type: 'separator' },
|
|
276
|
+
isMac ? { role: 'close' } : { role: 'quit' }
|
|
277
|
+
]
|
|
278
|
+
}
|
|
279
|
+
];
|
|
280
|
+
|
|
281
|
+
return Menu.buildFromTemplate(template);
|
|
282
|
+
}
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
### 7. Auto Update
|
|
286
|
+
|
|
287
|
+
```typescript
|
|
288
|
+
import { autoUpdater } from 'electron-updater';
|
|
289
|
+
|
|
290
|
+
// Good: Auto update setup
|
|
291
|
+
function setupAutoUpdater(): void {
|
|
292
|
+
autoUpdater.autoDownload = false;
|
|
293
|
+
autoUpdater.autoInstallOnAppQuit = true;
|
|
294
|
+
|
|
295
|
+
autoUpdater.on('update-available', (info) => {
|
|
296
|
+
// Notify user
|
|
297
|
+
dialog.showMessageBox({
|
|
298
|
+
type: 'info',
|
|
299
|
+
title: 'Update Available',
|
|
300
|
+
message: `Version ${info.version} is available.`,
|
|
301
|
+
buttons: ['Download', 'Later']
|
|
302
|
+
}).then(({ response }) => {
|
|
303
|
+
if (response === 0) {
|
|
304
|
+
autoUpdater.downloadUpdate();
|
|
305
|
+
}
|
|
306
|
+
});
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
autoUpdater.on('update-downloaded', () => {
|
|
310
|
+
dialog.showMessageBox({
|
|
311
|
+
type: 'info',
|
|
312
|
+
title: 'Update Ready',
|
|
313
|
+
message: 'Restart to install update?',
|
|
314
|
+
buttons: ['Restart', 'Later']
|
|
315
|
+
}).then(({ response }) => {
|
|
316
|
+
if (response === 0) {
|
|
317
|
+
autoUpdater.quitAndInstall();
|
|
318
|
+
}
|
|
319
|
+
});
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
// Check for updates on app start
|
|
323
|
+
autoUpdater.checkForUpdates();
|
|
324
|
+
}
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
### 8. Security Checklist
|
|
328
|
+
|
|
329
|
+
```typescript
|
|
330
|
+
// Good: Validate security settings
|
|
331
|
+
function validateSecuritySettings(win: BrowserWindow): void {
|
|
332
|
+
const webPrefs = win.webContents.getWebPreferences();
|
|
333
|
+
|
|
334
|
+
if (webPrefs.nodeIntegration) {
|
|
335
|
+
console.error('SECURITY: nodeIntegration should be false');
|
|
336
|
+
}
|
|
337
|
+
if (!webPrefs.contextIsolation) {
|
|
338
|
+
console.error('SECURITY: contextIsolation should be true');
|
|
339
|
+
}
|
|
340
|
+
if (!webPrefs.sandbox) {
|
|
341
|
+
console.warn('SECURITY: sandbox is recommended');
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
// Good: Handle external links
|
|
346
|
+
win.webContents.setWindowOpenHandler(({ url }) => {
|
|
347
|
+
// Open external URLs in system browser
|
|
348
|
+
if (url.startsWith('https://')) {
|
|
349
|
+
shell.openExternal(url);
|
|
350
|
+
}
|
|
351
|
+
return { action: 'deny' };
|
|
352
|
+
});
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
## Recommended Folder Structure
|
|
356
|
+
|
|
357
|
+
```text
|
|
358
|
+
my-electron-app/
|
|
359
|
+
├── src/
|
|
360
|
+
│ ├── main/ # Main Process
|
|
361
|
+
│ │ ├── main.ts
|
|
362
|
+
│ │ ├── ipc-handlers.ts
|
|
363
|
+
│ │ └── menu.ts
|
|
364
|
+
│ ├── preload/ # Preload Scripts
|
|
365
|
+
│ │ └── preload.ts
|
|
366
|
+
│ ├── renderer/ # Renderer (React/Vue)
|
|
367
|
+
│ │ ├── components/
|
|
368
|
+
│ │ ├── hooks/
|
|
369
|
+
│ │ └── App.tsx
|
|
370
|
+
│ └── shared/ # Shared types
|
|
371
|
+
│ └── ipc-types.ts
|
|
372
|
+
├── electron-builder.yml
|
|
373
|
+
└── package.json
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
## Build Configuration (electron-builder)
|
|
377
|
+
|
|
378
|
+
```yaml
|
|
379
|
+
# electron-builder.yml
|
|
380
|
+
appId: com.example.myapp
|
|
381
|
+
productName: MyApp
|
|
382
|
+
directories:
|
|
383
|
+
output: dist
|
|
384
|
+
files:
|
|
385
|
+
- "build/**/*"
|
|
386
|
+
- "node_modules/**/*"
|
|
387
|
+
mac:
|
|
388
|
+
target: [dmg, zip]
|
|
389
|
+
category: public.app-category.developer-tools
|
|
390
|
+
win:
|
|
391
|
+
target: [nsis, portable]
|
|
392
|
+
linux:
|
|
393
|
+
target: [AppImage, deb]
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
## Checklist
|
|
397
|
+
|
|
398
|
+
- [ ] `contextIsolation: true` configured
|
|
399
|
+
- [ ] `nodeIntegration: false` configured
|
|
400
|
+
- [ ] Expose APIs only through preload script
|
|
401
|
+
- [ ] Define IPC channel types
|
|
402
|
+
- [ ] Handle external links (setWindowOpenHandler)
|
|
403
|
+
- [ ] Save/restore window state
|
|
404
|
+
- [ ] Auto update setup
|
|
405
|
+
- [ ] Platform-specific menu configuration
|
|
406
|
+
- [ ] CSP header configured
|