Skip to content

Commit 78944df

Browse files
committed
Merge branch 'dev' into sean
1 parent 1cacfeb commit 78944df

File tree

8 files changed

+979
-0
lines changed

8 files changed

+979
-0
lines changed

app/.electron/main.ts

Lines changed: 510 additions & 0 deletions
Large diffs are not rendered by default.

app/.electron/menu.ts

Lines changed: 248 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,248 @@
1+
import { Menu, BrowserWindow, Shell } from 'electron';
2+
const isMac = process.platform === 'darwin';
3+
import Protocol from './protocol';
4+
/*
5+
DESCRIPTION: This file generates an array containing a menu based on the operating system the user is running.
6+
menuBuilder: The entire file is encompassed in menuBuilder. Ultimately, menuBuilder returns a function called
7+
buildMenu that uses defaultTemplate to construct a menu at the top of the application (as invoked in main.js)
8+
9+
Standard menu roles (e.g., undo, redo, quit, paste, etc.) come from Electron API and need not be separately coded
10+
11+
openTutorial: opens browser window containing tutorial on how to use the app
12+
-Creates a browser window
13+
-Tutorial is invoked within the "Help" menu
14+
15+
defaultTemplate: returns an array of submenus (each an array)
16+
-First, checks whether user is on a Mac (node returns 'darwin' for process.platform)
17+
-Then generates a dropdown menu at the top of the screen (e.g., "File") accordingly
18+
-The Mac check is necessary primarily for the first menu column, which is the name of the app
19+
-If user is not on a Mac, alternative menus are generated
20+
-Each menu:
21+
-"label" is the field at the top of each menu (e.g., "File", "Edit", "View", etc.)
22+
-"role" is a subitem within each menu (e.g., under "File," "Quit")
23+
-"type: separator" creates a horizontal line in a menu (e.g., under "Redo" in the "Edit" menu)
24+
*/
25+
26+
// Create a template for a menu and create menu using that template
27+
var MenuBuilder = function (mainWindow, appName) {
28+
// https://electronjs.org/docs/api/menu#main-process
29+
// "roles" are predefined by Electron and used for standard actions
30+
// https://www.electronjs.org/docs/api/menu-item
31+
// you can also create custom menu items with their own "on click" functionality if you need to
32+
// different roles are available between mac and windows
33+
34+
function openTutorial(): void {
35+
const tutorial = new BrowserWindow({
36+
width: 1180,
37+
height: 900,
38+
minWidth: 665,
39+
title: 'Tutorial',
40+
webPreferences: {
41+
nodeIntegration: true,
42+
nodeIntegrationInWorker: false,
43+
nodeIntegrationInSubFrames: false,
44+
contextIsolation: true,
45+
enableRemoteModule: true,
46+
zoomFactor: 1.0,
47+
devTools: false
48+
}
49+
});
50+
if (process.env.NODE_ENV === 'development') {
51+
tutorial.loadURL(`http://localhost:8080/#/tutorial`);
52+
} else {
53+
tutorial.loadURL(`${Protocol.scheme}://rse/index-prod.html#/tutorial`);
54+
}
55+
tutorial.show();
56+
}
57+
58+
const defaultTemplate= (): Electron.MenuItemConstructorOptions[] => [
59+
...(isMac
60+
? [
61+
{
62+
// on Mac, the first menu item name should be the name of the app
63+
label: appName,
64+
submenu: [
65+
{role: 'about'},
66+
{type: 'separator'},
67+
{role: 'services'},
68+
{type: 'separator'},
69+
{role: 'hide'},
70+
{role: 'hideothers'},
71+
{role: 'unhide'},
72+
{type: 'separator'},
73+
{role: 'quit'}
74+
] as Electron.MenuItemConstructorOptions[],
75+
} ,
76+
]
77+
: []),Electron.MenuItemConstructorOptions[],
78+
{
79+
label: 'File',
80+
submenu: [
81+
isMac
82+
? {
83+
role: 'close'
84+
}
85+
: {
86+
role: 'quit'
87+
}
88+
]
89+
},
90+
{
91+
label: 'Edit',
92+
submenu: [
93+
{
94+
role: 'undo'
95+
},
96+
{
97+
role: 'redo'
98+
},
99+
{
100+
type: 'separator'
101+
},
102+
{
103+
role: 'cut'
104+
},
105+
{
106+
role: 'copy'
107+
},
108+
{
109+
role: 'paste'
110+
},
111+
...(isMac
112+
? [
113+
{
114+
role: 'pasteAndMatchStyle'
115+
},
116+
{
117+
role: 'delete'
118+
},
119+
{
120+
role: 'selectAll'
121+
},
122+
{
123+
type: 'separator'
124+
},
125+
{
126+
label: 'Speech',
127+
submenu: [
128+
{
129+
role: 'startspeaking'
130+
},
131+
{
132+
role: 'stopspeaking'
133+
}
134+
]
135+
}
136+
]
137+
: [
138+
{
139+
role: 'delete'
140+
},
141+
{
142+
type: 'separator'
143+
},
144+
{
145+
role: 'selectAll'
146+
}
147+
])
148+
]
149+
},
150+
{
151+
label: 'View',
152+
submenu: [
153+
{
154+
role: 'reload'
155+
},
156+
{
157+
role: 'forcereload'
158+
},
159+
{
160+
role: 'toggledevtools'
161+
},
162+
{
163+
type: 'separator'
164+
},
165+
{
166+
role: 'resetzoom'
167+
},
168+
{
169+
role: 'zoomin'
170+
},
171+
{
172+
role: 'zoomout'
173+
},
174+
{
175+
type: 'separator'
176+
},
177+
{
178+
role: 'togglefullscreen'
179+
}
180+
]
181+
},
182+
183+
{
184+
label: 'Window',
185+
submenu: [
186+
{
187+
role: 'minimize'
188+
},
189+
{
190+
role: 'zoom'
191+
},
192+
...(isMac
193+
? [
194+
{
195+
type: 'separator'
196+
},
197+
{
198+
role: 'front'
199+
},
200+
{
201+
type: 'separator'
202+
},
203+
{
204+
role: 'window'
205+
}
206+
]
207+
: [
208+
{
209+
role: 'close'
210+
}
211+
])
212+
]
213+
},
214+
{
215+
role: 'help',
216+
submenu: [
217+
{
218+
label: 'Learn More',
219+
click: async () => {
220+
const { shell } = require('electron');
221+
await shell.openExternal(
222+
'https://github.com/open-source-labs/ReacType'
223+
);
224+
}
225+
},
226+
{
227+
label: 'Tutorial',
228+
click: () => openTutorial()
229+
}
230+
]
231+
}
232+
];
233+
234+
return template;
235+
}
236+
237+
// constructs menu from default template
238+
return {
239+
buildMenu: function () {
240+
const menu = Menu.buildFromTemplate(defaultTemplate());
241+
Menu.setApplicationMenu(menu);
242+
243+
return menu;
244+
}
245+
};
246+
};
247+
248+
export { MenuBuilder };

app/.electron/preload.ts

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// contextBridge is what allows context to be translated between the main process and the render process
2+
import { contextBridge } from 'electron';
3+
import { existsSync, writeFileSync, mkdirSync, writeFile } from 'fs';
4+
import formatCode from './preloadFunctions/format';
5+
import {
6+
chooseAppDir,
7+
addAppDirChosenListener,
8+
removeAllAppDirChosenListeners
9+
} from './preloadFunctions/chooseAppDir';
10+
import {
11+
setCookie,
12+
getCookie,
13+
delCookie,
14+
github,
15+
tutorial
16+
} from './preloadFunctions/cookies';
17+
18+
/*
19+
DESCRIPTION: This file appears to limit the node methods the Electron app can access.
20+
21+
Per the docs:
22+
-Main World is the JavaScript context in which the renderer code runs (that is, the page)
23+
-Isolated World is where preload scripts run
24+
-contextBridge is a module that safely exposes APIs from the isolated context in which preload scripts run
25+
to the context in which the website or application runs (i.e., from Isolated World to Main World)
26+
27+
We likely should not change this file unless we determine additional methods are necessary
28+
or some methods are not used.
29+
30+
*/
31+
32+
// Expose protected methods that allow the renderer process to use select node methods
33+
// without exposing all node functionality. This is a critical security feature
34+
// 'mainWorld" is the context that the main renderer runs in
35+
// with contextIsolation on (see webpreferences on main.js), this preload script runs isolated
36+
// "api" is the key that injects the api into the window
37+
// to access these keys in the renderer process, we'll do window.api
38+
// the api object (second arg) can contain functions, strings, bools, numbers, arrays, obects in value
39+
// data primitives sent on the bridge are immutable and changes in one context won't carry over to another context
40+
contextBridge.exposeInMainWorld('api', {
41+
formatCode,
42+
chooseAppDir,
43+
addAppDirChosenListener,
44+
removeAllAppDirChosenListeners,
45+
existsSync,
46+
writeFileSync,
47+
mkdirSync,
48+
writeFile,
49+
setCookie,
50+
getCookie,
51+
delCookie,
52+
github,
53+
tutorial
54+
});
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { ipcRenderer, IpcRendererEvent } from 'electron';
2+
import { type } from 'os';
3+
4+
type AppDirSelectedCallback = (path: string) => void;
5+
6+
const chooseAppDir = (): void => {
7+
ipcRenderer.send('choose_app_dir');
8+
};
9+
10+
// once an app directory is chosen, the main process will send an "app_dir_selected" event
11+
// when this event occurs, exucte the callback passed in by the user
12+
const addAppDirChosenListener = (callback: AppDirSelectedCallback): void => {
13+
ipcRenderer.on(
14+
'app_dir_selected',
15+
(event: IpcRendererEvent, path: string) => {
16+
callback(path);
17+
}
18+
);
19+
};
20+
21+
// removes all listeners for the app_dir_selected event
22+
// this is important because otherwise listeners will pile up and events will trigger multiple events
23+
const removeAllAppDirChosenListeners = (): void => {
24+
ipcRenderer.removeAllListeners('app_dir_selected');
25+
};
26+
27+
export {
28+
chooseAppDir,
29+
addAppDirChosenListener,
30+
removeAllAppDirChosenListeners
31+
};
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { ipcRenderer, IpcRendererEvent } from 'electron';
2+
3+
type GetCookieCallback = (cookie: string) => void;
4+
5+
const setCookie = (): void => {
6+
ipcRenderer.send('set_cookie');
7+
};
8+
9+
const getCookie = (callback: GetCookieCallback): void => {
10+
ipcRenderer.on('give_cookie', (event: IpcRendererEvent, cookie: string) => {
11+
callback(cookie);
12+
});
13+
};
14+
15+
const delCookie = (): void => {
16+
ipcRenderer.send('delete_cookie');
17+
};
18+
19+
const github = (): void => {
20+
ipcRenderer.send('github');
21+
};
22+
23+
const tutorial = (): void => {
24+
ipcRenderer.send('tutorial');
25+
};
26+
27+
export { setCookie, getCookie, delCookie, github, tutorial };
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { format } from 'prettier';
2+
3+
// format code using prettier
4+
// this format function is used in the render process to format the code in the code preview
5+
// the format function is defined in the main process because it needs to access node functionality ('fs')
6+
const formatCode = (code: string): string => {
7+
return format(code, {
8+
singleQuote: true,
9+
trailingComma: 'es5',
10+
bracketSpacing: true,
11+
jsxBracketSameLine: true,
12+
parser: 'typescript'
13+
});
14+
};
15+
16+
export default formatCode;

0 commit comments

Comments
 (0)