adde desktop-angular

This commit is contained in:
2025-10-03 15:05:48 +06:00
parent 2c2725cd19
commit cce64b19e8
54 changed files with 23820 additions and 0 deletions

View File

@@ -0,0 +1,154 @@
# Electron-native Modals Guide
This guide documents the Electron-native modal dialogs implemented in this app. These modals are opened from the Electron main process (via a small HTML dialog window) and are configurable at runtime (text, buttons, and colors).
## Why Electron-native modals?
- Work even if the Angular UI is busy/frozen
- True application-level modal (owned by main process)
- One-shot dialogs you can call from anywhere in renderer through IPC
- Per-dialog runtime theming (background/text/button colors)
## File Map
- `src/main/main.js`
- IPC handler `dialog:custom`
- Helper `openCustomModalWindow(config)` that creates a modal `BrowserWindow`
- `src/main/modal.html`
- Self-contained dialog UI (HTML/CSS/JS)
- Listens for `custom-modal:config` and renders content/buttons
- Sends result back via `custom-modal:result`
- `src/preload/preload.js`
- Exposes `showNativeModal(config)` to `window.api`
- `src/frontend/src/app/ipc.service.ts`
- Adds `showNativeModal(config)` convenience wrapper for Angular code
## Runtime API
Call from Angular (renderer):
```ts
const result = await this.ipc.showNativeModal({
title: 'Confirm Deletion',
message: 'Are you sure you want to delete the item?',
buttons: [
{ id: 'cancel', label: 'Cancel', style: 'secondary' },
{ id: 'delete', label: 'Delete', style: 'danger' }
],
colors: {
background: '#aa1c3a',
text: '#ffffff',
buttonBg: '#ffffff',
buttonText: '#aa1c3a',
secondaryBg: 'rgba(255,255,255,0.1)',
secondaryText: '#ffffff'
},
buttonStyles: {
delete: { bg: '#e53935', text: '#fff' },
cancel: { bg: 'rgba(255,255,255,0.1)', text: '#ffffff' }
}
});
// result => { buttonId: string, buttonIndex: number, buttonLabel?: string }
```
### Config Schema
```ts
interface NativeModalButton {
id: string; // identifier returned as buttonId
label: string; // text on the button
style?: 'primary' | 'secondary' | 'danger'; // default visual style
}
interface NativeModalConfig {
title?: string; // dialog title
message?: string; // main message (plain text)
buttons?: NativeModalButton[]; // 1..3 buttons (extra are ignored)
colors?: { // global color overrides
background?: string; // dialog background
text?: string; // title/message text color
buttonBg?: string; // primary button bg
buttonText?: string; // primary button text color
secondaryBg?: string; // secondary button bg
secondaryText?: string; // secondary button text color
};
buttonStyles?: { // per-button overrides by button id
[buttonId: string]: {
bg?: string; // background + border color
text?: string; // text color
}
};
}
```
## Color Model
The dialog uses CSS variables in `modal.html` with sensible defaults matching the app footer theme:
- `--bg` (default `#aa1c3a`)
- `--text` (default `#ffffff`)
- `--btn-bg` / `--btn-text` (primary buttons)
- `--btn-sec-bg` / `--btn-sec-text` (secondary buttons)
You can override them per-dialog via `colors` and refine specific buttons through `buttonStyles`.
## Button Styles
- `primary`: white background with red text (matches app footer theme)
- `secondary`: translucent white background with white text
- `danger`: red background (`#e53935`) with white text
You can still override any of these via `buttonStyles` per button.
## Result Contract
The renderer receives:
```ts
{ buttonId: string, buttonIndex: number, buttonLabel?: string }
```
If the dialog window is closed without a click, youll get `{ buttonId: 'closed', buttonIndex: -1 }`.
## How it Works (Flow)
1. Renderer calls `window.api.showNativeModal(config)` (exposed by preload)
2. Main process handles `dialog:custom`, opens a modal `BrowserWindow` and loads `modal.html`
3. After the page loads, main sends `custom-modal:config` with the payload
4. The page renders content and buttons; on click it emits `custom-modal:result`
5. Main resolves the original IPC with `{ buttonId, buttonIndex, buttonLabel }`
## Example: Yes/No/Cancel With Custom Colors
```ts
await this.ipc.showNativeModal({
title: 'Save Changes',
message: 'Save before closing?',
buttons: [
{ id: 'yes', label: 'Yes', style: 'primary' },
{ id: 'no', label: 'No', style: 'secondary' },
{ id: 'cancel', label: 'Cancel', style: 'danger' }
],
colors: {
background: '#1e293b',
text: '#e2e8f0',
buttonBg: '#e2e8f0',
buttonText: '#1e293b',
secondaryBg: 'rgba(226,232,240,0.12)',
secondaryText: '#e2e8f0'
}
});
```
## Notes & Considerations
- Maximum 3 buttons are rendered (extra are ignored)
- Message is plain text (no HTML injection)
- The dialog is frameless and always-on-top, sized 560x340 by default
- Parent is the currently focused window, when available
- Closing the dialog without a click returns `{ buttonId: 'closed' }`
## Rationale
- Keep Angular modal for in-app UX consistency and speed
- Add Electron-native modal for cases where UI thread may be busy, or when deep theming and app-level modality are desired