-
-
Notifications
You must be signed in to change notification settings - Fork 811
Add customizable homepage #2648
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
22a7b11
8021bfb
826ddae
08bf957
b8ef6ea
b2a1484
9b59089
f0194a7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -43,14 +43,28 @@ function addTab (tabId = tabs.add(), options = {}) { | |
| destroyTab(tabs.getSelected()) | ||
| } | ||
|
|
||
| // if the tab is a new, blank tab, replace it with the custom new tab page | ||
| let isCustomUrl = false | ||
| if (!tabs.get(tabId).url && !tabs.get(tabId).private) { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it might be possible for a site to create a popup with no URL (I'm not sure exactly), in which case we shouldn't show the NTP. Perhaps we could solve that by also checking |
||
| const newTabUrl = settings.get('newTabUrl') | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since this setting isn't a URL, could we rename it to something like |
||
| if (newTabUrl === 'customUrl') { | ||
| const customUrl = settings.get('newTabCustomUrl') | ||
| if (customUrl) { | ||
| tabs.update(tabId, { url: customUrl }) | ||
| isCustomUrl = true | ||
| } | ||
| } | ||
| } | ||
|
|
||
| tabBar.addTab(tabId) | ||
| webviews.add(tabId) | ||
|
|
||
| if (!options.openInBackground) { | ||
| switchToTab(tabId, { | ||
| focusWebview: options.enterEditMode === false | ||
| }) | ||
| if (options.enterEditMode !== false) { | ||
| // Don't enter edit mode for custom URLs - just show the website | ||
| if (options.enterEditMode !== false && !isCustomUrl) { | ||
| tabEditor.show(tabId) | ||
| } | ||
| } else { | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -5,6 +5,8 @@ var urlParser = require('util/urlParser.js') | |||||||||
| var keyboardNavigationHelper = require('util/keyboardNavigationHelper.js') | ||||||||||
| var bookmarkStar = require('navbar/bookmarkStar.js') | ||||||||||
| var contentBlockingToggle = require('navbar/contentBlockingToggle.js') | ||||||||||
| var newTabPage = require('newTabPage.js') | ||||||||||
| var settings = require('util/settings/settings.js') | ||||||||||
|
|
||||||||||
| const tabEditor = { | ||||||||||
| container: document.getElementById('tab-editor'), | ||||||||||
|
|
@@ -45,8 +47,18 @@ const tabEditor = { | |||||||||
| if (showSearchbar !== false) { | ||||||||||
| if (editingValue) { | ||||||||||
| searchbar.showResults(editingValue, null) | ||||||||||
| searchbar.el.classList.remove('searchbar-hidden') | ||||||||||
| } else { | ||||||||||
| searchbar.showResults('', null) | ||||||||||
| const newTabUrlMode = settings.get('newTabUrl') | ||||||||||
| const isNTP = (newTabUrlMode === 'backgroundImage' && currentURL === '') || | ||||||||||
| (newTabUrlMode === 'customUrl' && currentURL === settings.get('newTabCustomUrl')) | ||||||||||
|
|
||||||||||
|
Comment on lines
+55
to
+56
|
||||||||||
| (newTabUrlMode === 'customUrl' && currentURL === settings.get('newTabCustomUrl')) | |
| (newTabUrlMode === 'customUrl' && currentURL === settings.get('newTabCustomUrl')) || | |
| (newTabUrlMode === 'blank' && currentURL === '') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we could just skip calling searchbar.showResults in this case (perhaps calling searchbar.hide instead if that's necessary), and then we no longer need the hidden class.
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,5 +1,7 @@ | ||||||||||||||||||||||||||
| const path = require('path') | ||||||||||||||||||||||||||
| const statistics = require('js/statistics.js') | ||||||||||||||||||||||||||
| const settings = require('util/settings/settings.js') | ||||||||||||||||||||||||||
| const webviews = require('webviews.js') | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| const newTabPage = { | ||||||||||||||||||||||||||
| background: document.getElementById('ntp-background'), | ||||||||||||||||||||||||||
|
|
@@ -9,6 +11,15 @@ const newTabPage = { | |||||||||||||||||||||||||
| imagePath: path.join(window.globalArgs['user-data-path'], 'newTabBackground'), | ||||||||||||||||||||||||||
| blobInstance: null, | ||||||||||||||||||||||||||
| reloadBackground: function () { | ||||||||||||||||||||||||||
| const newTabUrl = settings.get('newTabUrl') | ||||||||||||||||||||||||||
| if (newTabUrl === 'blank') { | ||||||||||||||||||||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If the setting has never been modified, settings.get will return undefined, which this also needs to check for (ie since a blank page is the default value, undefined and "blank" should be treated the same way) |
||||||||||||||||||||||||||
| newTabPage.background.hidden = true | ||||||||||||||||||||||||||
| newTabPage.hasBackground = false | ||||||||||||||||||||||||||
| document.body.classList.remove('ntp-has-background') | ||||||||||||||||||||||||||
| newTabPage.deleteBackground.hidden = true | ||||||||||||||||||||||||||
| return | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| fs.readFile(newTabPage.imagePath, function (err, data) { | ||||||||||||||||||||||||||
| if (newTabPage.blobInstance) { | ||||||||||||||||||||||||||
| URL.revokeObjectURL(newTabPage.blobInstance) | ||||||||||||||||||||||||||
|
|
@@ -32,32 +43,52 @@ const newTabPage = { | |||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||
| pickImage: async function () { | ||||||||||||||||||||||||||
| var filePath = await ipc.invoke('showOpenDialog', { | ||||||||||||||||||||||||||
| filters: [ | ||||||||||||||||||||||||||
| { name: 'Image files', extensions: ['jpg', 'jpeg', 'png', 'gif', 'webp'] } | ||||||||||||||||||||||||||
| ] | ||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| if (!filePath || filePath.length === 0) { | ||||||||||||||||||||||||||
| // User cancelled the file picker - reset to blank if no image was previously set | ||||||||||||||||||||||||||
| if (!newTabPage.hasBackground) { | ||||||||||||||||||||||||||
| settings.set('newTabUrl', 'blank') | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| return | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| await fs.promises.copyFile(filePath[0], newTabPage.imagePath) | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
| await fs.promises.copyFile(filePath[0], newTabPage.imagePath) | |
| try { | |
| await fs.promises.copyFile(filePath[0], newTabPage.imagePath) | |
| } catch (err) { | |
| console.error('Failed to copy new tab background image:', err) | |
| return | |
| } |
Copilot
AI
Jan 5, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing error handling for the file unlink operation. If removing the file fails (e.g., file doesn't exist, permissions issue), the error will be unhandled. Consider wrapping this in a try-catch block to handle errors gracefully.
| await fs.promises.unlink(newTabPage.imagePath) | |
| try { | |
| await fs.promises.unlink(newTabPage.imagePath) | |
| } catch (err) { | |
| // Ignore if the file does not exist; log and abort for other errors. | |
| if (err && err.code === 'ENOENT') { | |
| // File is already gone; continue to update settings/UI. | |
| } else { | |
| console.error('Failed to remove new tab background image:', err) | |
| return | |
| } | |
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -179,6 +179,12 @@ | |
| "settingsStartupCreateTask": "Re-open previous tabs", | ||
| "settingsStartupNewTaskandBackground": "Move previous tabs to a background task", | ||
| "settingsStartupNewTaskOnly": "Open a new blank task", | ||
| "settingsNewTabUrl": "New Tab Page:", | ||
| "settingsNewTabUrlBlank": "Blank Page", | ||
| "settingsNewTabUrlCustomUrl": "Custom URL", | ||
| "settingsNewTabUrlBackgroundImage": "Background Image", | ||
| "settingsNewTabUrlUploadImage": "Upload Image", | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It would be useful to also add these as the title attributes of the camera and delete buttons on the new tab page. |
||
| "settingsNewTabUrlRemoveImage": "Remove Image", | ||
| "settingsNewWindowOptions": "In new windows: ", | ||
| "settingsNewWindowCreateTask": "Create a new task", | ||
| "settingsNewWindowPickTask": "Show task list", | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -257,24 +257,6 @@ function buildAppMenu (options = {}) { | |
| sendIPCToWindow(window, 'zoomOut') | ||
| } | ||
| }, | ||
| // Hidden item to enable shortcut on numpad | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This seems like an accidental change, can you revert it? |
||
| { | ||
| label: l('appMenuZoomIn'), | ||
| accelerator: 'CmdOrCtrl+numadd', | ||
| click: function (item, window) { | ||
| sendIPCToWindow(window, 'zoomIn') | ||
| }, | ||
| visible: false | ||
| }, | ||
| // Hidden item to enable shortcut on numpad | ||
| { | ||
| label: l('appMenuZoomOut'), | ||
| accelerator: 'CmdOrCtrl+numsub', | ||
| click: function (item, window) { | ||
| sendIPCToWindow(window, 'zoomOut') | ||
| }, | ||
| visible: false | ||
| }, | ||
| { | ||
| label: l('appMenuActualSize'), | ||
| accelerator: 'CmdOrCtrl+0', | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -151,6 +151,21 @@ <h3 data-string="settingsAdditionalFeaturesHeading"></h3> | |||||||||||||||||||
| </select> | ||||||||||||||||||||
| </div> | ||||||||||||||||||||
|
|
||||||||||||||||||||
| <div class="setting-section"> | ||||||||||||||||||||
| <label for="newTabUrl" data-string="settingsNewTabUrl"></label> | ||||||||||||||||||||
| <select id="new-tab-url-dropdown" name="newTabUrl"> | ||||||||||||||||||||
| <option value="blank" data-string="settingsNewTabUrlBlank"></option> | ||||||||||||||||||||
| <option value="customUrl" data-string="settingsNewTabUrlCustomUrl"></option> | ||||||||||||||||||||
| <option value="backgroundImage" data-string="settingsNewTabUrlBackgroundImage"></option> | ||||||||||||||||||||
| </select> | ||||||||||||||||||||
| <input | ||||||||||||||||||||
| type="text" | ||||||||||||||||||||
| id="new-tab-custom-url-input" | ||||||||||||||||||||
| style="margin-left: 0.5em; min-width: 325px" | ||||||||||||||||||||
| hidden | ||||||||||||||||||||
|
Comment on lines
+162
to
+165
|
||||||||||||||||||||
| type="text" | |
| id="new-tab-custom-url-input" | |
| style="margin-left: 0.5em; min-width: 325px" | |
| hidden | |
| type="url" | |
| id="new-tab-custom-url-input" | |
| style="margin-left: 0.5em; min-width: 325px" | |
| hidden | |
| placeholder="https://example.com" |
| Original file line number | Diff line number | Diff line change | ||
|---|---|---|---|---|
|
|
@@ -243,6 +243,43 @@ startupSettingInput.addEventListener('change', function() { | |||
| settings.set('startupTabOption', parseInt(this.value)) | ||||
| }) | ||||
|
|
||||
| /* new tab settings */ | ||||
|
|
||||
| var newTabUrlDropdown = document.getElementById('new-tab-url-dropdown') | ||||
| var newTabCustomUrlInput = document.getElementById('new-tab-custom-url-input') | ||||
|
|
||||
| settings.get('newTabUrl', function (value = 'blank') { | ||||
| newTabUrlDropdown.value = value | ||||
| if (value === 'customUrl') { | ||||
| newTabCustomUrlInput.hidden = false | ||||
| } | ||||
| }) | ||||
|
|
||||
| settings.listen('newTabUrl', function (value) { | ||||
| newTabUrlDropdown.value = value | ||||
| newTabCustomUrlInput.hidden = value !== 'customUrl' | ||||
| }) | ||||
|
|
||||
| settings.get('newTabCustomUrl', function (value = '') { | ||||
| newTabCustomUrlInput.value = value | ||||
| }) | ||||
|
|
||||
| newTabUrlDropdown.addEventListener('change', function () { | ||||
| settings.set('newTabUrl', this.value) | ||||
| newTabCustomUrlInput.hidden = true | ||||
|
||||
| newTabCustomUrlInput.hidden = true |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think there should be a case added to this if statement to destroy the current tab if it equals the NTP URL. Currently, if I close all my tabs, I get a single tab with the NTP; then if I create a new private tab, I get two tabs: one with the NTP, and a second with the private tab. I'd expect that the NTP would close when I create the new tab.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems the desired functionality is already achieved for the New Tab Picture. Are you asking for this behavior to be applied for the custom URL as well?
2026-01-04.20-04-10.mp4