diff --git a/app/background-process/ui/subwindows/shell-menus.js b/app/background-process/ui/subwindows/shell-menus.js index 2ffe9b5f35..27b4d35f7a 100644 --- a/app/background-process/ui/subwindows/shell-menus.js +++ b/app/background-process/ui/subwindows/shell-menus.js @@ -99,8 +99,8 @@ export function reposition (parentWindow) { win.setBounds({ x: parentBounds.x + win.boundsOpt.right - 320, y: parentBounds.y + win.boundsOpt.top, - width: 320, - height: 88 + width: 350, + height: 90 }) } else if (win.menuId === 'local-path') { win.setBounds({ diff --git a/app/background-process/ui/view-manager.js b/app/background-process/ui/view-manager.js index 43e9ee3637..51b5577719 100644 --- a/app/background-process/ui/view-manager.js +++ b/app/background-process/ui/view-manager.js @@ -1,4 +1,4 @@ -import { app, dialog, BrowserView, BrowserWindow, Menu, clipboard } from 'electron' +import { app, dialog, BrowserView, BrowserWindow, Menu, clipboard, ipcMain } from 'electron' import * as beakerCore from '@beaker/core' import errorPage from '@beaker/core/lib/error-page' import path from 'path' @@ -94,6 +94,7 @@ var activeViews = {} // map of {[win.id]: Array} var closedURLs = {} // map of {[win.id]: Array} var windowEvents = {} // mapof {[win.id]: Events} var noRedirectHostnames = new Set() // set of hostnames which have dat-redirection disabled +var nextViewIsScriptCloseable = false // will the next view created be "script closable"? // classes // = @@ -131,6 +132,7 @@ class View { this.isInpageFindActive = false // is the inpage-finder UI active? this.currentInpageFindString = undefined // what's the current inpage-finder query string? this.currentInpageFindResults = undefined // what's the current inpage-finder query results? + this.isScriptClosable = takeIsScriptClosable() // can this view be closed by `window.close` ? // helper state this.peers = 0 // how many peers does the site have? @@ -149,6 +151,7 @@ class View { this.webContents.on('did-stop-loading', this.onDidStopLoading.bind(this)) this.webContents.on('did-fail-load', this.onDidFailLoad.bind(this)) this.webContents.on('update-target-url', this.onUpdateTargetUrl.bind(this)) + this.webContents.on('page-title-updated', this.onPageTitleUpdated.bind(this)) // NOTE page-title-updated isn't documented on webContents but it is supported this.webContents.on('page-favicon-updated', this.onPageFaviconUpdated.bind(this)) this.webContents.on('new-window', this.onNewWindow.bind(this)) this.webContents.on('media-started-playing', this.onMediaChange.bind(this)) @@ -219,6 +222,13 @@ class View { this.browserView.webContents.loadURL(url) } + resize () { + const win = this.browserWindow + var {width, height} = win.getContentBounds() + this.browserView.setBounds({x: 0, y: Y_POSITION, width, height: height - Y_POSITION}) + this.browserView.setAutoResize({width: true, height: true}) + } + activate () { this.isActive = true @@ -227,10 +237,7 @@ class View { permPrompt.show(this.browserView) modals.show(this.browserView) - var {width, height} = win.getBounds() - this.browserView.setBounds({x: 0, y: Y_POSITION, width, height: height - Y_POSITION}) - this.browserView.setAutoResize({width: true, height: true}) - + this.resize() this.webContents.focus() } @@ -414,7 +421,9 @@ class View { .markdown code { padding: 3px 5px; } .markdown pre > code { display: block; } `) - this.webContents.executeJavaScript(await fs.readFile(path.join(app.getAppPath(), 'markdown-renderer.build.js'), 'utf8')) + let mdpath = path.join(app.getAppPath(), 'markdown-renderer.build.js') + mdpath = mdpath.replace('app.asar', 'app.asar.unpacked') // fetch from unpacked dir + this.webContents.executeJavaScript(await fs.readFile(mdpath, 'utf8')) } // json rendering @@ -446,7 +455,9 @@ class View { background: #ddd; } `) - this.webContents.executeJavaScript(await fs.readFile(path.join(app.getAppPath(), 'json-renderer.build.js'), 'utf8')) + let jsonpath = path.join(app.getAppPath(), 'json-renderer.build.js') + jsonpath = jsonpath.replace('app.asar', 'app.asar.unpacked') // fetch from unpacked dir + this.webContents.executeJavaScript(await fs.readFile(jsonpath, 'utf8')) } } @@ -501,6 +512,7 @@ class View { // update state this.isLoading = true this.loadingURL = null + this.favicons = null this.isReceivingAssets = false this.wasDatTimeout = false @@ -592,6 +604,10 @@ class View { statusBar.set(this.browserWindow, url) } + onPageTitleUpdated (e, title) { + this.emitUpdateState() + } + onPageFaviconUpdated (e, favicons) { this.favicons = favicons && favicons[0] ? favicons : null this.emitUpdateState() @@ -625,6 +641,24 @@ class View { // = export function setup () { + // listen for webContents messages + ipcMain.on('BEAKER_MARK_NEXT_VIEW_SCRIPTCLOSEABLE', e => { + nextViewIsScriptCloseable = true + e.returnValue = true + }) + ipcMain.on('BEAKER_SCRIPTCLOSE_SELF', e => { + var browserView = BrowserView.fromWebContents(e.sender) + if (browserView) { + var view = findView(browserView) + if (view && view.isScriptClosable) { + remove(view.browserWindow, view) + e.returnValue = true + return + } + } + e.returnValue = false + }) + // track peer-counts beakerCore.dat.library.createEventStream().on('data', ([evt, {details}]) => { if (evt !== 'network-changed') return @@ -670,10 +704,20 @@ export function getActive (win) { return getAll(win).find(view => view.isActive) } -export function findContainingWindow (view) { +export function findView (browserView) { + for (let winId in activeViews) { + for (let v of activeViews[winId]) { + if (v.browserView === browserView) { + return v + } + } + } +} + +export function findContainingWindow (browserView) { for (let winId in activeViews) { for (let v of activeViews[winId]) { - if (v.browserView === view) { + if (v.browserView === browserView) { return v.browserWindow } } @@ -798,6 +842,11 @@ export function setActive (win, view) { emitReplaceState(win) } +export function resize (win) { + var active = getActive(win) + if (active) active.resize() +} + export function initializeFromSnapshot (win, snapshot) { win = getTopWindow(win) for (let url of snapshot) { @@ -840,9 +889,7 @@ export async function loadPins (win) { win = getTopWindow(win) var json = await settingsDb.get('pinned_tabs') try { JSON.parse(json).forEach(url => create(win, url, {isPinned: true})) } - catch (e) { - console.log('Failed to load pins', e) - } + catch (e) {} } export function reopenLastRemoved (win) { @@ -1174,7 +1221,7 @@ function addToNoRedirects (url) { async function fireBeforeUnloadEvent (wc) { try { - if (wc.isWaitingForResponse()) { + if (wc.isLoading() || wc.isWaitingForResponse()) { return // dont bother } return await wc.executeJavaScript(` @@ -1187,4 +1234,12 @@ async function fireBeforeUnloadEvent (wc) { } catch (e) { // ignore } +} + +// `nextViewIsScriptCloseable` is set by a message received prior to window.open() being called +// we capture the state of the flag on the next created view, then reset it +function takeIsScriptClosable () { + var b = nextViewIsScriptCloseable + nextViewIsScriptCloseable = false + return b } \ No newline at end of file diff --git a/app/background-process/ui/windows.js b/app/background-process/ui/windows.js index 30ce8849f6..e28380059b 100644 --- a/app/background-process/ui/windows.js +++ b/app/background-process/ui/windows.js @@ -225,6 +225,8 @@ export function createShellWindow (windowState) { registerGlobalKeybinding(win, 'CmdOrCtrl+[', onGoBack(win)) registerGlobalKeybinding(win, 'CmdOrCtrl+]', onGoForward(win)) registerGlobalKeybinding(win, 'Alt+D', onFocusLocation(win)) + registerGlobalKeybinding(win, 'F5', onReload(win)) + registerGlobalKeybinding(win, 'F6', onFocusLocation(win)) // register event handlers win.on('browser-backward', onGoBack(win)) @@ -232,7 +234,10 @@ export function createShellWindow (windowState) { win.on('scroll-touch-begin', sendScrollTouchBegin) win.on('scroll-touch-end', sendToWebContents('scroll-touch-end')) win.on('focus', sendToWebContents('focus')) - win.on('blur', sendToWebContents('blur')) + win.on('blur', e => { + statusBarSubwindow.set(win, false) // hide the statusbar on blur + sendToWebContents('blur')(e) + }) win.on('app-command', (e, cmd) => { onAppCommand(win, e, cmd) }) win.on('enter-full-screen', e => { // update UI @@ -251,6 +256,7 @@ export function createShellWindow (windowState) { // sendToWebContents('leave-full-screen')(e) }) win.on('resize', () => { + viewManager.resize(win) for (let k in subwindows) { subwindows[k].reposition(win) } @@ -402,6 +408,10 @@ function onGoForward (win) { return () => viewManager.getActive(win).webContents.goForward() } +function onReload (win) { + return () => viewManager.getActive(win).webContents.reload() +} + function onFocusLocation (win) { return () => win.webContents.send('command', 'focus-location') } diff --git a/app/builtin-pages/views/library-view.js b/app/builtin-pages/views/library-view.js index e85f1c0d25..90152848a5 100644 --- a/app/builtin-pages/views/library-view.js +++ b/app/builtin-pages/views/library-view.js @@ -342,6 +342,14 @@ async function gotoFileEditor (filePath) { onOpenFileEditor() } +function resolvePaymentLink (archiveUrl, paymentLink) { + if (paymentLink.indexOf('://') === -1) { + const shouldAddSlash = !archiveUrl.endsWith('/') && !paymentLink.startsWith('/') + return `${archiveUrl}${shouldAddSlash ? '/' : ''}${paymentLink}` + } + return paymentLink +} + // rendering // = @@ -835,6 +843,11 @@ function renderReadmeHint () { ` } +function renderDonationLink (archiveUrl, paymentLink) { + const url = resolvePaymentLink(archiveUrl, paymentLink) + return yo`${url}` +} + function renderSettingsView () { const isOwner = _get(archive, 'info.isOwner') @@ -1013,13 +1026,13 @@ function renderSettingsView () { ? yo`

Enter a link to your donation page and Beaker will show - a icon in your page's URL bar. + a icon in your page's URL bar. ${renderSettingsField({key: 'paymentLink', value: paymentLink, placeholder: 'Example: https://opencollective.com/beaker', onUpdate: setManifestValue})}

` : paymentLink - ? yo`

${paymentLink}

` + ? yo`

${renderDonationLink(archive.url, paymentLink)}

` : yo`

No link provided.

` } @@ -1156,7 +1169,7 @@ function renderNetworkView () { ` ] : yo`
- + Give back! Seed this project${"'"}s files to help keep them online. Learn more
` diff --git a/app/dat-daemon.js b/app/dat-daemon.js index 3b52a4c3f0..400068bfc1 100644 --- a/app/dat-daemon.js +++ b/app/dat-daemon.js @@ -6,6 +6,10 @@ process.on('uncaughtException', (err) => { console.error('Uncaught exception:', err) }) +process.on('disconnect', () => { + process.exit() +}) + process.once('message', firstMsg => { beakerCoreDatDaemon.setup({ rpcAPI, diff --git a/app/markdown-renderer.js b/app/markdown-renderer.js index f582fd2a71..499c392aa5 100644 --- a/app/markdown-renderer.js +++ b/app/markdown-renderer.js @@ -39,6 +39,9 @@ if (!document.querySelector('main')) { var navReq = await fetch('/nav.md') if (!navReq.ok) return var navMD = await navReq.text() + if (navMD.includes(' - + ` } diff --git a/app/new-shell-window/tabs.js b/app/new-shell-window/tabs.js index 0e10973003..343de036d6 100644 --- a/app/new-shell-window/tabs.js +++ b/app/new-shell-window/tabs.js @@ -240,7 +240,7 @@ ${spinnerCSS} } .tab.pinned { - width: 45px; + flex: 0 0 45px; } .tab-favicon { diff --git a/app/package.json b/app/package.json index d84ef24a5e..ee87c96dce 100644 --- a/app/package.json +++ b/app/package.json @@ -3,7 +3,7 @@ "productName": "Beaker Browser", "description": "An Experimental Peer-to-Peer Web Browser.", "homepage": "https://beakerbrowser.com/", - "version": "0.8.6", + "version": "0.8.8", "author": "Paul Frazee ", "copyright": "© 2019, Blue Link Labs", "main": "background-process.build.js", diff --git a/app/shell-menus/browser.js b/app/shell-menus/browser.js index a15662d5a3..55b431b832 100644 --- a/app/shell-menus/browser.js +++ b/app/shell-menus/browser.js @@ -167,7 +167,7 @@ class BrowserMenu extends LitElement { diff --git a/app/shell-menus/donate.js b/app/shell-menus/donate.js index 1e2cb050ff..7f1e329d6a 100644 --- a/app/shell-menus/donate.js +++ b/app/shell-menus/donate.js @@ -26,18 +26,32 @@ class DonateMenu extends LitElement { await this.requestUpdate() } + resolvePaymentLink (paymentLink) { + if (paymentLink.indexOf('://') === -1) { + const shouldAddSlash = !this.url.endsWith('/') && !paymentLink.startsWith('/') + return `${this.url}${shouldAddSlash ? '/' : ''}${paymentLink}` + } + return paymentLink + } + // rendering // = + renderDonationLink (paymentLink) { + const url = this.resolvePaymentLink(paymentLink) + return html` this.onOpenPage(url)}>${url}` + } + render () { var title = _get(this, 'datInfo.title', 'this site') - var paymentUrl = _get(this, 'datInfo.links.payment.0.href') + const paymentLink = String(_get(this, 'datInfo.links.payment.0.href')) + return html`
- +

Contribute to ${title}

@@ -46,7 +60,7 @@ class DonateMenu extends LitElement { Visit their donation page to show your appreciation!
- this.onOpenPage(paymentUrl)}>${paymentUrl} + ${this.renderDonationLink(paymentLink)}
@@ -62,6 +76,10 @@ class DonateMenu extends LitElement { } } DonateMenu.styles = [commonCSS, css` +.wrapper { + overflow: hidden; +} + .header { height: auto; line-height: inherit; @@ -74,7 +92,7 @@ DonateMenu.styles = [commonCSS, css` margin: 0; } -.header-info .far { +.header-info .fa { font-size: 14px; margin: 0 7px 0 4px; } diff --git a/app/shell-window/ui/navbar/browser-menu.js b/app/shell-window/ui/navbar/browser-menu.js index 927b6b0777..c4014af1d6 100644 --- a/app/shell-window/ui/navbar/browser-menu.js +++ b/app/shell-window/ui/navbar/browser-menu.js @@ -198,7 +198,7 @@ export class BrowserMenuNavbarBtn { diff --git a/app/shell-window/ui/navbar/donate-menu.js b/app/shell-window/ui/navbar/donate-menu.js index 81d36c5826..6cae8e1010 100644 --- a/app/shell-window/ui/navbar/donate-menu.js +++ b/app/shell-window/ui/navbar/donate-menu.js @@ -30,7 +30,7 @@ export class DonateMenuNavbarBtn { return yo` @@ -51,7 +51,7 @@ export class DonateMenuNavbarBtn {