Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: glyphr-studio/Glyphr-Studio-Desktop
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v0.4.3
Choose a base ref
...
head repository: glyphr-studio/Glyphr-Studio-Desktop
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: master
Choose a head ref

Commits on Sep 20, 2018

  1. Much needed housekeeping

    Autre31415 committed Sep 20, 2018

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    5001b5e View commit details

Commits on Sep 24, 2018

  1. README update

    kethinov committed Sep 24, 2018
    Copy the full SHA
    8091412 View commit details

Commits on Sep 26, 2018

  1. update husky to v1.0.0

    Autre31415 committed Sep 26, 2018
    Copy the full SHA
    310633f View commit details
  2. Merge pull request #35 from kethinov/README-update

    README update
    Autre31415 authored Sep 26, 2018
    Copy the full SHA
    0386759 View commit details
  3. Merge pull request #34 from Autre31415/dependencies

    Much needed housekeeping
    Autre31415 authored Sep 26, 2018
    Copy the full SHA
    ae24689 View commit details
  4. bump version

    Autre31415 committed Sep 26, 2018
    Copy the full SHA
    26a8de5 View commit details
  5. Merge pull request #36 from Autre31415/0.4.4

    bump version
    Autre31415 authored Sep 26, 2018
    Copy the full SHA
    b4ea3a1 View commit details
  6. Update download linls

    Autre31415 authored Sep 26, 2018
    Copy the full SHA
    9aa0d10 View commit details

Commits on Feb 22, 2019

  1. The big dependency update

    Autre31415 committed Feb 22, 2019
    Copy the full SHA
    e7b7352 View commit details
  2. Merge pull request #38 from Autre31415/dep-updates

    The big dependency update
    Autre31415 authored Feb 22, 2019
    Copy the full SHA
    0b650f4 View commit details

Commits on Feb 27, 2019

  1. bump version

    Autre31415 committed Feb 27, 2019
    Copy the full SHA
    222369c View commit details
  2. Merge pull request #39 from Autre31415/bump-version

    bump version
    Autre31415 authored Feb 27, 2019
    Copy the full SHA
    84e04de View commit details
  3. Update build instructions

    Autre31415 authored Feb 27, 2019
    Copy the full SHA
    65c9404 View commit details

Commits on May 12, 2019

  1. Update glyphr to v1.13.00

    Autre31415 committed May 12, 2019
    Copy the full SHA
    4cf6f1a View commit details
  2. Merge pull request #41 from Autre31415/depUpdates

    Update Dependencies
    Autre31415 authored May 12, 2019
    Copy the full SHA
    c4ace9f View commit details

Commits on May 31, 2019

  1. update dependencies

    Autre31415 committed May 31, 2019
    Copy the full SHA
    c30a4cd View commit details
  2. Copy the full SHA
    bc6e420 View commit details
  3. Merge pull request #44 from Autre31415/glyphr-1.13.01

    Glyphr 1.13.01
    Autre31415 authored May 31, 2019
    Copy the full SHA
    08dc861 View commit details

Commits on Oct 20, 2019

  1. 0.5.3

    - Update glyphr-studio to v1.13.05.
    - Reduce browser window minimum width down to 800x640. Closes #45
    - Upgrade all dependencies.
    - Refactoring based on an updated to `standard`.
    kethinov committed Oct 20, 2019
    Copy the full SHA
    fa30a5e View commit details

Commits on Oct 21, 2019

  1. tilda deps

    kethinov committed Oct 21, 2019
    Copy the full SHA
    8a54e9d View commit details
  2. Merge pull request #46 from kethinov/0.5.3

    0.5.3
    Autre31415 authored Oct 21, 2019
    Copy the full SHA
    85f714e View commit details

Commits on Dec 5, 2019

  1. 0.5.4

    Various maintenance.
    
    Also closes #47.
    kethinov committed Dec 5, 2019
    Copy the full SHA
    690e653 View commit details

Commits on Dec 14, 2019

  1. Merge pull request #49 from kethinov/0.5.4

    0.5.4
    Autre31415 authored Dec 14, 2019
    Copy the full SHA
    8a3d4e6 View commit details

Commits on Mar 13, 2020

  1. Bump acorn from 7.1.0 to 7.1.1

    Bumps [acorn](https://github.com/acornjs/acorn) from 7.1.0 to 7.1.1.
    - [Release notes](https://github.com/acornjs/acorn/releases)
    - [Commits](acornjs/acorn@7.1.0...7.1.1)
    
    Signed-off-by: dependabot[bot] <support@github.com>
    dependabot[bot] authored Mar 13, 2020
    Copy the full SHA
    57fb7ca View commit details

Commits on Mar 14, 2020

  1. Merge pull request #54 from glyphr-studio/dependabot/npm_and_yarn/aco…

    …rn-7.1.1
    
    Bump acorn from 7.1.0 to 7.1.1
    Autre31415 authored Mar 14, 2020
    Copy the full SHA
    ce36b6a View commit details

Commits on Jun 12, 2020

  1. 0.5.5

    Autre31415 committed Jun 12, 2020
    Copy the full SHA
    4e63cd9 View commit details
  2. Merge pull request #55 from Autre31415/0.5.5

    0.5.5
    Autre31415 authored Jun 12, 2020
    Copy the full SHA
    179aa55 View commit details

Commits on Jul 15, 2020

  1. Bump lodash from 4.17.15 to 4.17.19

    Bumps [lodash](https://github.com/lodash/lodash) from 4.17.15 to 4.17.19.
    - [Release notes](https://github.com/lodash/lodash/releases)
    - [Commits](lodash/lodash@4.17.15...4.17.19)
    
    Signed-off-by: dependabot[bot] <support@github.com>
    dependabot[bot] authored Jul 15, 2020
    Copy the full SHA
    7e80dd6 View commit details

Commits on Jul 22, 2020

  1. Merge pull request #57 from glyphr-studio/dependabot/npm_and_yarn/lod…

    …ash-4.17.19
    
    Bump lodash from 4.17.15 to 4.17.19
    Autre31415 authored Jul 22, 2020
    Copy the full SHA
    f8e07a7 View commit details
  2. Fixed issue #56 (#58)

    * Fixed issue #56
    
    * Change to package.json needed for the previous commit
    
    * Update package-lock.json
    
    * Removed unnecessary entry in pacage.json build.files
    
    * Updated package.json
    
    * Updated package.json
    rolandbernard authored Jul 22, 2020
    Copy the full SHA
    f73938c View commit details
  3. 0.5.6

    Autre31415 committed Jul 22, 2020
    Copy the full SHA
    76ecb08 View commit details
  4. Merge pull request #59 from Autre31415/0.5.6

    0.5.6
    Autre31415 authored Jul 22, 2020
    Copy the full SHA
    bc3f874 View commit details

Commits on Feb 6, 2024

  1. 0.6.0 (#84)

    Autre31415 authored Feb 6, 2024
    Copy the full SHA
    c58d929 View commit details
  2. Copy the full SHA
    357f3c0 View commit details
Showing with 5,989 additions and 5,818 deletions.
  1. +0 −1 .eslintignore
  2. +208 −15 .gitignore
  3. +21 −0 CHANGELOG.md
  4. +24 −20 README.md
  5. +25 −22 build.js
  6. BIN images/appicon.icns → build/icon.icns
  7. BIN images/appicon.ico → build/icon.ico
  8. BIN build/icon.png
  9. +39 −0 electron-builder.yml
  10. BIN images/appicon.png
  11. +146 −116 main.js
  12. +134 −0 menu.js
  13. +5,246 −5,447 package-lock.json
  14. +18 −20 package.json
  15. +0 −78 packager.js
  16. +13 −0 preload.js
  17. +115 −99 renderer.js
1 change: 0 additions & 1 deletion .eslintignore

This file was deleted.

223 changes: 208 additions & 15 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,22 +1,215 @@
lib-cov
*.seed
### Linux ###
*~

# temporary files which can be created if a process still has a handle open of a deleted file
.fuse_hidden*

# KDE directory preferences
.directory

# Linux trash folder which might appear on any partition or disk
.Trash-*

# .nfs files are created when an open file is removed but is still being accessed
.nfs*

### macOS ###
# General
.DS_Store
.AppleDouble
.LSOverride

# Icon must end with two \r
Icon


# Thumbnails
._*

# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent

# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk

### macOS Patch ###
# iCloud generated files
*.icloud

### Windows ###
# Windows thumbnail cache files
Thumbs.db
Thumbs.db:encryptable
ehthumbs.db
ehthumbs_vista.db

# Dump file
*.stackdump

# Folder config file
[Dd]esktop.ini

# Recycle Bin used on file shares
$RECYCLE.BIN/

# Windows Installer files
*.cab
*.msi
*.msix
*.msm
*.msp

# Windows shortcuts
*.lnk

### Node ###
# Logs
logs
*.log
*.csv
*.dat
*.out
*.pid
*.gz
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
.pnpm-debug.log*

# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json

# Runtime data
pids
logs
results
*.pid
*.seed
*.pid.lock

npm-debug.log
.DS_Store
node_modules
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# Coverage directory used by tools like istanbul
coverage
*.lcov

# nyc test coverage
.nyc_output

# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt

# Bower dependency directory (https://bower.io/)
bower_components
build

.jshintrc
# node-waf configuration
.lock-wscript

# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release

# Dependency directories
node_modules/
jspm_packages/

# Snowpack dependency directory (https://snowpack.dev/)
web_modules/

# TypeScript cache
*.tsbuildinfo

# Optional npm cache directory
.npm

# Optional eslint cache
.eslintcache

# Optional stylelint cache
.stylelintcache

# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/

# Optional REPL history
.node_repl_history

# Output of 'npm pack'
*.tgz

# Yarn Integrity file
.yarn-integrity

# dotenv environment variable files
.env
.env.development.local
.env.test.local
.env.production.local
.env.local

# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache

# Next.js build output
.next
out

# Nuxt.js build / generate output
.nuxt
dist

# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public

# vuepress build output
.vuepress/dist

# vuepress v2.x temp and cache directory
.temp

# Docusaurus cache and generated files
.docusaurus

# Serverless directories
.serverless/

# FuseBox cache
.fusebox/

# DynamoDB Local files
.dynamodb/

# TernJS port file
.tern-port

# Stores VSCode versions used for testing VSCode extensions
.vscode-test

# yarn v2
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*

### Node Patch ###
# Serverless Webpack directories
.webpack/

# Optional stylelint cache

# SvelteKit build / generate output
.svelte-kit

Glyphr_Studio_Autohacked_For_Electron.html
### Glyphyr ###
21 changes: 21 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Changelog

## 0.6.0

- Updated Glyphr-Studio to v1.14.0.
- Note: This is the final 1.0 version.
- Updated Electron to v28.2.1.
- Dropped support for Windows 7.
- Dropped support for macOS 10.14 (Mojave) and below.
- Improved support for modern versions of operating systems.
- [Disabled browser node integration and enable context isolation](https://www.electronjs.org/docs/latest/tutorial/context-isolation).
- [Enabled browser process sandboxing](https://www.electronjs.org/docs/latest/tutorial/sandbox).
- Added "Save Image As" item to context menu when right clicking canvas.
- Added arm64 distributions.
- Added more native menu items.
- Added window size and positioning persistence.
- Updated save confirmation dialog to only appear when closing the app with unsaved changes.
- Removed save popups when saving a pre-existing project.
- Fixed a bug where save buttons would sometimes save with the wrong file type.
- Fixed a bug where ⌘/CTRL+O would open a browser tab.
- Updated all dependencies, including replacing obsolete ones with newer ones.
44 changes: 24 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,22 +1,28 @@
# Glyphr Studio Desktop

Desktop application for [Glyphr Studio](http://glyphrstudio.com) built in [Electron](https://electron.atom.io/)!
Desktop application for [Glyphr Studio](http://glyphrstudio.com) built in [Electron](https://www.electronjs.org/)!

## Download

- [Linux 32-bit](https://github.com/glyphr-studio/Glyphr-Studio-Desktop/releases/download/v0.4.3/Glyphr.Studio-linux-ia32.zip)
- [Linux 64-bit](https://github.com/glyphr-studio/Glyphr-Studio-Desktop/releases/download/v0.4.3/Glyphr.Studio-linux-x64.zip)
- [macOS](https://github.com/glyphr-studio/Glyphr-Studio-Desktop/releases/download/v0.4.3/Glyphr.Studio-darwin-x64.zip)
- [Windows 32-bit](https://github.com/glyphr-studio/Glyphr-Studio-Desktop/releases/download/v0.4.3/Glyphr.Studio-win32-ia32.zip)
- [Windows 64-bit](https://github.com/glyphr-studio/Glyphr-Studio-Desktop/releases/download/v0.4.3/Glyphr.Studio-win32-x64.zip)
### macOS

[Download](https://github.com/glyphr-studio/Glyphr-Studio-Desktop/releases/latest) the `.dmg` file.

### Windows 10+

[Download](https://github.com/glyphr-studio/Glyphr-Studio-Desktop/releases/latest) the `.exe` file.

### Linux

[Download](https://github.com/glyphr-studio/Glyphr-Studio-Desktop/releases/latest) the `.AppImage`, `.deb`, or `.snap` file

## How to run from source

Be sure to have [Node.js](https://nodejs.org) and [git](https://git-scm.com) installed.

Then:

```
```bash
git clone https://github.com/glyphr-studio/Glyphr-Studio-Desktop.git
cd Glyphr-Studio-Desktop
npm i
@@ -25,33 +31,31 @@ npm start

## Build

Builds are constructed with [electron-packager](https://github.com/maxogden/electron-packager).
Builds are constructed with [electron-builder](https://github.com/electron-userland/electron-builder).

Be sure to have [Node.js](https://nodejs.org) and [git](https://git-scm.com) installed.
Be sure to have [Node.js](https://nodejs.org) and [git](https://git-scm.com) installed. Linux/Mac users who wish to do builds for Windows will need to have [WINE](https://winehq.org) installed. Mac users who wish to do builds for Windows will need to [XQuartz](https://www.xquartz.org) installed in order to run WINE. It is recommended that Mac users install both Wine and XQuartz via [Homebrew](https://brew.sh).

First, be sure to run:

```
```bash
git clone https://github.com/glyphr-studio/Glyphr-Studio-Desktop.git
cd Glyphr-Studio-Desktop
npm i
```

Then:

All Platforms: `npm run package`

64-Bit Platforms Only: `npm run package -- --64`

Linux 32-Bit: `npm run package -- --linux32`
To build package zips use:

Linux 64-Bit: `npm run package -- --linux`

macOS: `npm run package -- --mac`
```bash
npm run pack
```

Windows 32-Bit: `npm run package -- --win32`
To build full distributions use:

Windows 64-Bit: `npm run package -- --win`
```bash
npm run dist
```

## Troubleshooting

47 changes: 25 additions & 22 deletions build.js
Original file line number Diff line number Diff line change
@@ -1,32 +1,35 @@
const fs = require('fs')
const fs = require('fs-extra')

let glyphrPath = 'node_modules/Glyphr-Studio/dev/Glyphr_Studio.html'
let glyphrElectron = 'node_modules/Glyphr-Studio/dev/Glyphr_Studio_Electron.html'
let rendererJs = '<script>require(\'../../../renderer.js\')</script></body>'
let rendererCss = 'href="Glyphr_Studio.css" />\n<link rel="stylesheet" type="text/css" href="../../../renderer.css" />'
let html = fs.readFileSync(glyphrPath, 'utf8')
;(async () => {
const glyphrElectron = 'node_modules/Glyphr-Studio/dev/Glyphr_Studio_Electron.html'
const rendererJs = '<script type="module" src="../../../renderer.js"></script></body>'
const rendererCss = 'href="Glyphr_Studio.css" />\n<link rel="stylesheet" type="text/css" href="../../../renderer.css" />'
const html = await fs.readFile('node_modules/Glyphr-Studio/dev/Glyphr_Studio.html', 'utf8')

fs.readFile(glyphrElectron, 'utf8', (err, data) => {
if (err) {
generateGlyphrElectron()
if (!await fs.pathExists(glyphrElectron)) {
// generate electron html if it doesn't exist
await generateGlyphrElectron()
} else {
data = data
// only generate electron html if original has been modified
const electronFileData = await fs.readFile(glyphrElectron, 'utf8')
const reversedFileData = electronFileData
.replace(rendererJs, '</body>')
.replace(rendererCss, 'href="Glyphr_Studio.css" />')
if (data !== html) {
generateGlyphrElectron()

if (html !== reversedFileData) {
await generateGlyphrElectron()
}
}
})

function generateGlyphrElectron () {
let htmlWithRenderer = html
// add renderer.js
.replace('</body>', rendererJs)
async function generateGlyphrElectron () {
console.log('Building Glyphr_Studio_Electron.html...')
const htmlWithRenderer = html
// add renderer.js
.replace('</body>', rendererJs)

// add renderer.css
.replace('href="Glyphr_Studio.css" />', rendererCss)
// add renderer.css
.replace('href="Glyphr_Studio.css" />', rendererCss)

console.log('Building Glyphr_Studio_Electron.html...')
fs.writeFileSync(glyphrElectron, htmlWithRenderer)
}
await fs.outputFile(glyphrElectron, htmlWithRenderer)
}
})()
File renamed without changes.
File renamed without changes.
Binary file added build/icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
39 changes: 39 additions & 0 deletions electron-builder.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
appId: com.glyphrstudio.desktop
productName: Glyphr Studio
mac:
category: public.app-category.graphics-design
target:
- target: default
arch:
- arm64
- x64
dmg:
iconSize: 140
iconTextSize: 18
win:
verifyUpdateCodeSignature: false
artifactName: ${productName} Setup ${version}-${arch}.${ext}
target:
- target: nsis
arch:
- arm64
- x64
linux:
target:
- target: AppImage
arch:
- arm64
- x64
- target: deb
arch:
- arm64
- x64
- target: snap
arch:
- arm64
- x64
synopsis: Glyphr Studio is a free, web based font designer
description: Font design has a high barrier of entry. Professional font design programs are very complex, and/or quite expensive. Glyphr Studio is streamlined and made for font design hobbyists... and it's free!
category: Graphics
snap:
base: core20
Binary file removed images/appicon.png
Binary file not shown.
262 changes: 146 additions & 116 deletions main.js
Original file line number Diff line number Diff line change
@@ -1,148 +1,178 @@
const electron = require('electron')
const {app, BrowserWindow, Menu} = electron
const open = require('open')
const { app, BrowserWindow, ipcMain: ipc, dialog, Menu } = electron
const contextMenu = require('electron-context-menu')
const { download } = require('electron-dl')
const windowStateKeeper = require('electron-window-state')
const path = require('path')
const fs = require('fs-extra')

// menu template
const template = [
{
label: 'File',
submenu: [
{
label: 'Save',
accelerator: 'CmdOrCtrl+S',
enabled: false,
click () {
win.webContents.send('save', '')
}
},
{
label: 'Save As',
accelerator: 'CmdOrCtrl+Shift+S',
enabled: false,
click () {
win.webContents.send('saveas', '')
}
}
]
},
{
label: 'Edit',
submenu: [
{ label: 'Undo', accelerator: 'CmdOrCtrl+Z', selector: 'undo:' },
{ label: 'Redo', accelerator: 'Shift+CmdOrCtrl+Z', selector: 'redo:' },
{ type: 'separator' },
{ label: 'Cut', accelerator: 'CmdOrCtrl+X', selector: 'cut:' },
{ label: 'Copy', accelerator: 'CmdOrCtrl+C', selector: 'copy:' },
{ label: 'Paste', accelerator: 'CmdOrCtrl+V', selector: 'paste:' },
{ label: 'Select All', accelerator: 'CmdOrCtrl+A', selector: 'selectAll:' }
]
},
{
label: 'View',
submenu: [
{role: 'resetzoom'},
{role: 'zoomin'},
{role: 'zoomout'},
{type: 'separator'},
{role: 'togglefullscreen'}
]
},
{
role: 'window',
submenu: [
{role: 'minimize'},
{role: 'close'}
]
},
{
role: 'help',
submenu: [
{
label: 'Learn More',
click () {
electron.shell.openExternal('http://glyphrstudio.com')
}
}
]
let win
let menuTemplate

/**
* IPC Handlers
*/

// handle app exit
ipc.handle('exit', () => {
app.exit()
})

// handle close confirmation dialog
ipc.handle('confirmClose', async (event, fileName) => {
let name = 'your project'

if (fileName) {
name = path.basename(fileName)
}
]

if (process.platform === 'darwin') {
template.unshift({
label: 'Glyphr Studio',
submenu: [
{role: 'about'},
{type: 'separator'},
{role: 'services', submenu: []},
{type: 'separator'},
{role: 'hide'},
{role: 'hideothers'},
{role: 'unhide'},
{type: 'separator'},
{role: 'quit'}
]

const result = await dialog.showMessageBox({
type: 'question',
title: 'Confirm',
buttons: ['Save', 'Cancel', 'Don\'t Save'],
message: `Would you like to save the changes you made to ${name} before closing?`
})

// Window menu
template[3].submenu = [
{role: 'close'},
{role: 'minimize'},
{role: 'zoom'},
{type: 'separator'},
{role: 'front'}
]
}
return result
})

let win
// handle saving canvas as image
ipc.handle('saveCanvasImage', async (event, data) => {
await download(win, data, { saveAs: true })
})

// handle saving new project with confirmation dialog
ipc.handle('saveProject', async (event, fname, buffer) => {
const { filePath } = await dialog.showSaveDialog({
properties: ['openFile'],
title: 'Choose where to save project...',
defaultPath: process.env.HOME + '/' + fname
})

if (filePath) {
await fs.outputFile(filePath, buffer)
}

return filePath
})

// handle quick overwrite save of project in progress
ipc.handle('saveProjectOverwrite', async (event, file, buffer) => {
await fs.outputFile(file, buffer)
})

// handle enabling of save menu
ipc.handle('enableSaveMenu', () => {
// overrride some renderer keyboard shortcuts in favor of electron accelerators
win.webContents.on('before-input-event', (event, input) => {
// save
if ((input.control || input.meta) && input.key.toLowerCase() === 's') {
event.preventDefault()
win.webContents.send('ping', 'save')
}

// save as
if ((input.control || input.meta) && input.shift && input.key.toLowerCase() === 's') {
event.preventDefault()
win.webContents.send('ping', 'saveas')
}
})

// menu positioning differs depending on the OS
let i = 0
if (process.platform === 'darwin') {
i = 1
}

// enable save buttons in menu template
menuTemplate[i].submenu[0].enabled = true
menuTemplate[i].submenu[1].enabled = true

// rebuild updated menu
const menu = Menu.buildFromTemplate(menuTemplate)
Menu.setApplicationMenu(menu)
})

function createWindow () {
const mainWindowState = windowStateKeeper({
defaultWidth: 1300,
defaultHeight: 900
})

win = new BrowserWindow({
width: 1300,
height: 900,
minWidth: 1300,
minHeight: 900,
icon: process.platform === 'linux' && path.join(__dirname, '/images/appicon.png')
x: mainWindowState.x,
y: mainWindowState.y,
width: mainWindowState.width,
height: mainWindowState.height,
minWidth: 800,
minHeight: 640,
icon: process.platform === 'linux' && path.join(__dirname, '/build/icon.png'),
webPreferences: {
preload: path.resolve(app.getAppPath(), 'preload.js'),
contextIsolation: true,
sandbox: true
}
})
win.loadURL(path.join('file://', __dirname, '/node_modules/Glyphr-Studio/dev/Glyphr_Studio_Electron.html'))

let webContents = win.webContents
win.loadFile('node_modules/Glyphr-Studio/dev/Glyphr_Studio_Electron.html')

// save window position/size across loads
mainWindowState.manage(win)

// create the menubar and get a copy of the template
menuTemplate = require('./menu')(win)

// set up app context menu
contextMenu({
prepend: (defaultActions, parameters, browserWindow) => [
{
label: 'Save Image As...',
visiable: parameters.mediaType === 'canvas',
click: () => {
win.webContents.send('ping', 'saveImage')
}
}
]
})

// configure about app panel
app.setAboutPanelOptions({
applicationName: app.getName(),
applicationVersion: app.getVersion(),
website: 'http://glyphrstudio.com/v1'
})

const webContents = win.webContents

// enable for debugging
// win.webContents.openDevTools();
// win.webContents.openDevTools()

webContents.on('new-window', async (event, url) => {
const open = await import('open')

webContents.on('new-window', function (event, url) {
event.preventDefault()
open(url)
})

win.on('closed', function () {
win = null
// inform renderer of close event
win.on('close', event => {
event.preventDefault()
win.webContents.send('ping', 'confirmClose')
})

const menu = Menu.buildFromTemplate(template)
Menu.setApplicationMenu(menu)
win.on('closed', () => {
win = null
})
}

app.on('ready', createWindow)

app.on('window-all-closed', function () {
app.on('window-all-closed', () => {
app.quit()
})

app.on('activate', function () {
app.on('activate', () => {
if (win === null) {
createWindow()
}
})

app.on('enableSaveMenu', function () {
let i = 0
if (process.platform === 'darwin') {
i = 1
}
template[i].submenu[0].enabled = true
template[i].submenu[1].enabled = true
const menu = Menu.buildFromTemplate(template)
Menu.setApplicationMenu(menu)
})
134 changes: 134 additions & 0 deletions menu.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
const { app, Menu } = require('electron')

const isMac = process.platform === 'darwin'

module.exports = window => {
const template = [
// { role: 'appMenu' }
...(isMac
? [{
label: app.name,
submenu: [
{ role: 'about' },
{ type: 'separator' },
{ role: 'services' },
{ type: 'separator' },
{ role: 'hide' },
{ role: 'hideOthers' },
{ role: 'unhide' },
{ type: 'separator' },
{ role: 'quit' }
]
}]
: []),
// { role: 'fileMenu' }
{
label: 'File',
submenu: [
{
label: 'Save',
accelerator: 'CmdOrCtrl+S',
enabled: false,
click () {
window.webContents.send('ping', 'save')
}
},
{
label: 'Save As',
accelerator: 'CmdOrCtrl+Shift+S',
enabled: false,
click () {
window.webContents.send('ping', 'saveas')
}
},
...(isMac ? [{ role: 'close' }] : [{ role: 'quit' }])
]
},
// { role: 'editMenu' }
{
label: 'Edit',
submenu: [
{ role: 'undo' },
{ role: 'redo' },
{ type: 'separator' },
{ role: 'cut' },
{ role: 'copy' },
{ role: 'paste' },
...(isMac
? [
{ role: 'pasteAndMatchStyle' },
{ role: 'delete' },
{ role: 'selectAll' },
{ type: 'separator' },
{
label: 'Speech',
submenu: [
{ role: 'startSpeaking' },
{ role: 'stopSpeaking' }
]
}
]
: [
{ role: 'delete' },
{ type: 'separator' },
{ role: 'selectAll' }
])
]
},
// { role: 'viewMenu' }
{
label: 'View',
submenu: [
{ role: 'reload' },
{ role: 'forceReload' },
{ role: 'toggleDevTools' },
{ type: 'separator' },
{ role: 'resetZoom' },
{ role: 'zoomIn' },
{ role: 'zoomOut' },
{ type: 'separator' },
{ role: 'togglefullscreen' }
]
},
// { role: 'windowMenu' }
{
label: 'Window',
submenu: [
{ role: 'minimize' },
{ role: 'zoom' },
...(isMac
? [
{ type: 'separator' },
{ role: 'front' },
{ type: 'separator' },
{ role: 'window' }
]
: [
{ role: 'close' }
])
]
},
{
role: 'help',
submenu: [
{
label: 'Learn More',
click: async () => {
const { shell } = require('electron')
await shell.openExternal('http://glyphrstudio.com/v1')
}
},
...(!isMac
? [
{ role: 'about' }
]
: [])
]
}
]

const menu = Menu.buildFromTemplate(template)
Menu.setApplicationMenu(menu)

return template
}
10,693 changes: 5,246 additions & 5,447 deletions package-lock.json

Large diffs are not rendered by default.

38 changes: 18 additions & 20 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
{
"name": "glyphr-studio-desktop",
"productName": "Glyphr Studio",
"version": "0.4.3",
"version": "0.6.0",
"description": "A desktop client for Glyphr Studio",
"author": "Glyphr Studio team <mail@glyphrstudio.com>",
"contributors": [
"Matt LaGrandeur <mail@glyphrstudio.com>",
"Mateusz Zawartka <mail@glyphrstudio.com>",
"Troy Coutu <autre31415@gmail.com>",
"Troy Coutu <troy.d.coutu@gmail.com>",
"Eric Newport <kethinov@gmail.com>"
],
"license": "GPL-3.0",
@@ -17,31 +17,29 @@
},
"main": "main.js",
"dependencies": {
"Glyphr-Studio": "glyphr-studio/Glyphr-Studio-1#v1.10.01",
"electron-editor-context-menu": "1.1.1",
"open": "0.0.5"
"Glyphr-Studio": "glyphr-studio/Glyphr-Studio-1#v1.14.0",
"electron-context-menu": "3.6.1",
"electron-dl": "3.5.2",
"electron-window-state": "5.0.3",
"fs-extra": "11.2.0",
"open": "10.0.3"
},
"devDependencies": {
"archiver": "2.1.1",
"electron": "1.8.4",
"electron-packager": "11.2.0",
"husky": "0.14.3",
"lint-staged": "7.0.4",
"standard": "11.0.1"
"electron": "28.2.1",
"electron-builder": "24.9.1",
"standard": "17.1.0"
},
"standard": {
"globals": [
"Blob",
"saveGlyphrProjectFile"
"ignore": [
"dist",
"build"
]
},
"scripts": {
"start": "node build.js && electron .",
"package": "node build.js && node packager.js",
"test": "standard",
"precommit": "lint-staged"
},
"lint-staged": {
"*.js": "standard"
"pack": "node build.js && electron-builder --dir",
"dist": "node build.js && electron-builder -mwl",
"lint": "standard --fix",
"test": "standard"
}
}
78 changes: 0 additions & 78 deletions packager.js

This file was deleted.

13 changes: 13 additions & 0 deletions preload.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
process.once('loaded', () => {
const { contextBridge, ipcRenderer: ipc } = require('electron')

contextBridge.exposeInMainWorld('electron', {
confirmClose: async (...params) => await ipc.invoke('confirmClose', ...params),
enableSaveMenu: async (...params) => await ipc.invoke('enableSaveMenu', ...params),
exitApp: async () => await ipc.invoke('exit'),
listen: callback => ipc.on('ping', callback),
saveCanvasImage: async (...params) => await ipc.invoke('saveCanvasImage', ...params),
saveProject: async (...params) => await ipc.invoke('saveProject', ...params),
saveProjectOverwrite: async (...params) => await ipc.invoke('saveProjectOverwrite', ...params)
})
})
214 changes: 115 additions & 99 deletions renderer.js
Original file line number Diff line number Diff line change
@@ -1,49 +1,119 @@
/* global alert */
const electron = require('electron')
const {remote} = electron
const {dialog} = electron.remote
const fs = require('fs')
/* global Blob, saveGlyphrProjectFile, handleDrop, navigate, _UI */

// api provided by preload.js
const electron = window.electron
let saveQuit = false
let editor = false
let currentProjectPath = ''

// eliminate onbeforeunload trigger to prevent unexpected close behavior
delete window.onbeforeunload

window.addEventListener('beforeunload', confirmClose)
// disable dev mode
_UI.devmode = false

function confirmClose (event) {
// listen to events fired by the main process
electron.listen(async (event, message) => {
switch (message) {
case 'confirmClose': {
await confirmClose()
break
}
case 'save': {
saveGlyphrProjectFile()
break
}
case 'saveas': {
currentProjectPath = ''
saveGlyphrProjectFile()
break
}
case 'saveImage': {
saveCanvasImage()
break
}
}
})

/**
* Event handler for closing out the app
*/
async function confirmClose () {
// if no project is open, close immediately
if (document.getElementById('splashscreenlogo')) {
return
await electron.exitApp()
}

event.returnValue = 'false'

return new Promise((resolve, reject) => {
dialog.showMessageBox({
type: 'question',
title: 'Confirm',
buttons: ['Yes', 'No', 'Cancel'],
message: 'Would you like to save before closing?'
}, function (response) {
if (response === 0) { // yes
saveQuit = true
saveGlyphrProjectFile()
} else if (response === 2) { // cancel
return false
} else {
window.removeEventListener('beforeunload', confirmClose)
electron.remote.app.emit('window-all-closed')
}
})
})
// close immediately if project has no unsaved changes
if (_UI.projectsaved) {
await electron.exitApp()
}

// otherwise bring up a save confirmation dialog first
const { response } = await electron.confirmClose(currentProjectPath)

if (response === 0) { // yes
saveQuit = true
saveGlyphrProjectFile()
} else if (response === 1) { // cancel
saveQuit = false
return false
} else { // no
await electron.exitApp()
}
}

/**
* Handle saving the canvas as an image
*/
async function saveCanvasImage () {
const canvas = document.querySelector('canvas')

await electron.saveCanvasImage(canvas.toDataURL())
}

// store references to some glyphr functions that will be overidden
const glyphrHandleDrop = handleDrop
const glyphrNavigate = navigate

// override the glyphr handleDrop event to add desktop specific functionality
handleDrop = function (evt) { // eslint-disable-line
// logic for deriving file data duplicated from original handleDrop
let f = evt.dataTransfer || document.getElementById('filechooser')
f = f.files[0]

// store context related to which project file was imported
if (f.path.includes('txt')) {
currentProjectPath = f.path
}

// call the original function
glyphrHandleDrop(evt)
}

// override the glyphr navigation event and use it to know when a project is open
navigate = async function (oa) { // eslint-disable-line
// call the original function
glyphrNavigate(oa)

// detect if we landed on the project page for the first time
if (!editor && _UI.current_page === 'glyph edit') {
editor = true
await electron.enableSaveMenu()
}
}

saveFile = function (fname, buffer, ftype) { // eslint-disable-line
let fblob = new Blob([buffer], {
'type': ftype || 'text/plain;charset=utf-8',
'endings': 'native'
// override glyphr saveFile function
saveFile = async function (fname, buffer, ftype) { // eslint-disable-line
const fblob = new Blob([buffer], {
type: ftype || 'text/plain;charset=utf-8',
endings: 'native'
})
let link
let event

if (fname.includes('SVG') || ftype === 'font/opentype') {
// handle SVG export logic
if (fname.includes('SVG')) {
link = document.createElement('a')
window.URL = window.URL || window.webkitURL
link.href = window.URL.createObjectURL(fblob)
@@ -53,75 +123,21 @@ saveFile = function (fname, buffer, ftype) { // eslint-disable-line
event.initEvent('click', true, false)
link.dispatchEvent(event)
} else {
if (window.saveFileOverwrite && window.saveFileOverwriteFile) {
fs.writeFileSync(window.saveFileOverwriteFile, buffer)
alert('Saved to ' + window.saveFileOverwriteFile)
// project file export logic
if (currentProjectPath) {
// overwrite project if saving one in progress
await electron.saveProjectOverwrite(currentProjectPath, buffer)
} else {
dialog.showSaveDialog({
properties: ['openFile'],
title: 'Choose where to save project...',
defaultPath: process.env.HOME + '/' + fname
}, function (destination) {
if (destination !== undefined) {
fs.writeFileSync(destination, buffer)
window.saveFileOverwriteFile = destination
}
if (saveQuit) {
window.removeEventListener('beforeunload', confirmClose)
electron.remote.app.emit('window-all-closed')
}
})
}
}
}

// native save menus
electron.ipcRenderer.on('save', function (event, message) {
saveGlyphrProjectFile(true) // overwrite file if a previously saved file exists
})

electron.ipcRenderer.on('saveas', function (event, message) {
saveGlyphrProjectFile()
})

// hijack save button event
document.body.addEventListener('click', function () {
// mouseover needed to outpace main project's continual redraw of the button
document.getElementById('npSave').addEventListener('mouseover', hijackSaveButton)
hijackSaveButton()
})
// spin up save confirmation dialog for saving new project
const destination = await electron.saveProject(fname, buffer)

let saveMenuEnabled = false
if (destination) {
currentProjectPath = destination
}

function hijackSaveButton () {
// delay to give time for the element to render
setTimeout(function () {
let button = document.querySelector('[onclick="saveGlyphrProjectFile();"]')
if (button) {
button.removeAttribute('onclick') // gotta remove the old onclick attribute to prevent the old and new from fighting with each other
button.onclick = function () {
saveGlyphrProjectFile(true) // overwrite file if one exists
if (saveQuit) {
await electron.exitApp()
}
}
if (!saveMenuEnabled && !document.getElementById('splashscreenlogo')) {
electron.remote.app.emit('enableSaveMenu')
saveMenuEnabled = true
}
}, 100)
}
}

let buildEditorContextMenu = remote.require('electron-editor-context-menu')

window.addEventListener('contextmenu', function (e) {
// Only show the context menu in text editors.
if (!e.target.closest('textarea, input, [contenteditable="true"]')) return

var menu = buildEditorContextMenu()

// The 'contextmenu' event is emitted after 'selectionchange' has fired but possibly before the
// visible selection has changed. Try to wait to show the menu until after that, otherwise the
// visible selection will update after the menu dismisses and look weird.
setTimeout(function () {
menu.popup(remote.getCurrentWindow())
}, 30)
})