diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6704566 --- /dev/null +++ b/.gitignore @@ -0,0 +1,104 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# 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 + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# TypeScript v1 declaration files +typings/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# 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 variables file +.env +.env.test + +# parcel-bundler cache (https://parceljs.org/) +.cache + +# Next.js build output +.next + +# 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 + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..3c22702 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,52 @@ +# changelog + +if something is ~~crossed out~~, then it is no longer a feature included by default, +but can still easily be enabled by following instructions in the [docs](README.md). + +### v0.5.0 (wip) + +- new: running from the WSL. +- improved: code has been refactored and cleaned up, + inc. file renaming. +- bugfix: un-break having multiple notion windows open. + +_(forked by [@dragonwocky](https://github.com/dragonwocky).)_ + +### v0.4.1 (2020-02-13) + +- bugfix: wider table & the "+" button not working in database pages. + +> [notion-enhancer.v4.1.zip](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/d239a3cf-d553-4ef3-ab04-8b47892d9f9a/Notion_Customization_v4.1.zip) + +### v0.4.0 + +- new: tray icon. +- new: app startup options (+ saving). +- new: `Reset.py` +- improved: better output from `Customization Patcher.py`. +- bugfix: wider tables in "short page" mode. +- bugfix: unclickable buttons/draggable area (of titlebar). + +### v0.3.0 + +- new: show/hide window hotkey. +- new: app startup options. +- ~~style: smaller table icons.~~ + +> [notion-enhancer.v3.zip](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/b01aa446-5727-476a-a25e-395472bfb1be/NotionScriptsV3.zip) + +### v0.2.0 + +- new: light/dark theme support for window control buttons + scrollbars. +- new: custom styles directly linked to the enhancer resources + compatible with web version. +- ~~improved: making table column width go below 100px.~~ + +### v0.1.0 + +- new: custom window control buttons. +- removed: default titlebar/menubar. +- ~~removed: huge padding of board view.~~ +- ~~removed: huge padding of table view.~~ +- ~~optional: making table column width go below 100px.~~ +- ~~style: thinner cover image + higher content block.~~ +- style: scrollbars. diff --git a/LICENSE b/LICENSE index 5d0d497..26171ec 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,7 @@ MIT License Copyright (c) 2020 TarasokUA +Copyright (c) 2020 dragonwocky Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -18,4 +19,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +SOFTWARE. \ No newline at end of file diff --git a/Notion Customization/Customization Patcher.py b/Notion Customization/Customization Patcher.py deleted file mode 100644 index 558bd35..0000000 --- a/Notion Customization/Customization Patcher.py +++ /dev/null @@ -1,370 +0,0 @@ -""" -# Step 1 -1.Locating notion app at ./%Username%/AppData/Local/Programs/Notion -2.Unpacking app.asar in new folder at./Notion/resources/app -3.Renaming file app.asar to app.asar.bak (because instead app won't use folder "app" to get all resources from it) -4.If app.asar already unpacked - it will try to locate folder "app" and skip to next step - -# Step 2 -1. Editing userscript file to replace "full_path_to_custom_style" with full path to ./custom_style.css -2. Adding userscript from main.user.js(should be in same folder with this .py file) to the ./app/renderer/preload.js -3. If there is already userscript - it will overwrite it. - -# Step 3 -1. Adding property "frame: false" to the place where application window is creating - -# Step 4 -1. Changes "window drag" area, to make window draggable - 1.1. You can change it by yourself, if you experiencing problems with dragging window or with clicking buttons on app topbar - Because this "draggable area" is creating on top of the other stuff, so if it will have button behind it - it won't be clickable. - You should change top,left,right properties. Now it's 2px on top, 390px on left and 420px on right; -""" -import os -from time import sleep -from shutil import copyfile -import re - -try: - sleepTime = 0.5 - sleep(sleepTime) - print("========= START OF LOG =========") - sleep(sleepTime) - LOCALAPPDATA = os.getenv('LOCALAPPDATA') - notionResourcesPath = LOCALAPPDATA + '/Programs/Notion/resources' - windowToggleHotkey = "'ctrl+shift+a'" - - # Step 1 - print("Step 1") - sleep(sleepTime) - if os.path.isfile(notionResourcesPath + '/app.asar'): - sleep(sleepTime) - print(" Unpacking app.asar") - os.system("asar extract %LOCALAPPDATA%/Programs/Notion/resources/app.asar %LOCALAPPDATA%/Programs/Notion/resources/app") - sleep(sleepTime) - renameSource = notionResourcesPath + '/app.asar' - renameDestination = renameSource + '.bak' - os.rename(renameSource, renameDestination) - print(" Renaming asar.app to asar.app.bak") - sleep(sleepTime) - else: - sleep(sleepTime) - print(" There is no file at Notion/resources/app.asar") - sleep(sleepTime) - print(" Trying to locate unpacked app.asar") - sleep(sleepTime) - if os.path.exists(notionResourcesPath + '/app'): - print(" app.asar already unpacked - Moving to the next step") - sleep(sleepTime) - else: - print(" Nothing found at Notion/resources/app. Exiting. ") - input("Press Enter to exit...") - exit() - print("-Done-\n") - sleep(sleepTime) - - print("Step 2") - sleep(sleepTime) - # Step 2 - if os.path.isfile(notionResourcesPath + '/app/renderer/preload.js'): - print(" Adding userscript to Notion/resources/app/renderer/preload.js") - sleep(sleepTime) - - preload = open(notionResourcesPath + '/app/renderer/preload.js', 'rt') - preloadStr = preload.read() - preload.close() - if 'function userscript()' in preloadStr: - print(" Userscript already added. Replacing it") - sleep(sleepTime) - userscript_line = 0 - with open(notionResourcesPath + '/app/renderer/preload.js') as myFile: - for num, line in enumerate(myFile, 1): - if "function userscript()" in line: - userscript_line = num-1 - - preload = open(notionResourcesPath + '/app/renderer/preload.js', 'rt') - preloadLines = preload.readlines() - preload.close() - - with open(notionResourcesPath + '/app/renderer/preload.js', 'w') as fin: - for lineno, line in enumerate(preloadLines, 1): - if lineno < userscript_line: - fin.write(line) - - print(" Creating link to ./resources/custom_style.css") - sleep(sleepTime) - preload = open(notionResourcesPath + '/app/renderer/preload.js', 'a') - userscript = open('./resources/main.user.js') - scriptPath = os.getcwd() - scriptPath = scriptPath.replace('\\', '/') - userscriptStr = userscript.read() - userscriptStr = userscriptStr.replace('full_path_to_custom_style',scriptPath + '/resources/custom_style.css') - preload.write('\n' + userscriptStr) - preload.close() - else: - print(" There is no files at Notion/resources/app/renderer/preload.js or/and ./resources/main.user.js - Nothing was done") - sleep(sleepTime) - print("-Done-\n") - sleep(sleepTime) - - print("Step 3") - sleep(sleepTime) - # Step 3 - if os.path.isfile(notionResourcesPath + '/app/main/createWindow.js'): - print(" Making window frameless at Notion/resources/app/main/createWindow.js") - sleep(sleepTime) - - createWindow = open(notionResourcesPath + '/app/main/createWindow.js', 'rt') - createWindowText = createWindow.read() - createWindowText = createWindowText.replace('{ show: false', '{ frame: false, show: false') - - print(" Adding ""Run Hidden"" functionality at Notion/resources/app/main/createWindow.js") - sleep(sleepTime) - createWindowText = createWindowText.replace('window.show();', """ - const path = require("path"); - const Store = require(path.join(__dirname,'../','store.js')); - const store = new Store({ - configName: "user-preferences", - defaults: { - runHidden: false, - alwaysMaximized: false - }}); - - var RunHiddenCheckboxState = store.get("runHidden"); - var AlwaysMaximizedCheckboxState = store.get("alwaysMaximized"); - - if(RunHiddenCheckboxState) { - //Do nothing - } else { - if(AlwaysMaximizedCheckboxState) { - window.maximize(); - } else { - window.show() - } - }""") - createWindow.close() - - createWindow = open(notionResourcesPath + '/app/main/createWindow.js', 'wt') - createWindow.write(createWindowText) - createWindow.close() - else: - print(" There is no files at Notion/resources/app/main/createWindow.js - Nothing was done") - sleep(sleepTime) - print("-Done-\n") - sleep(sleepTime) - - print("Step 4") - sleep(sleepTime) - # Step 4 - if os.path.isfile(notionResourcesPath + '/app/renderer/index.js'): - print(" Adjusting drag area for frameless window in Notion/resources/app/renderer/index.js") - sleep(sleepTime) - createWindow = open(notionResourcesPath + '/app/renderer/index.js', 'rt') - createWindowText = createWindow.read() - topIndex = createWindowText.rfind("top") - createWindowTextSplit = createWindowText[topIndex:] - createWindowTextSplit = createWindowTextSplit.replace("right: 0", "right: 420 ") - createWindowTextSplit = createWindowTextSplit.replace("top: 0", "top: 1 ") - createWindowTextSplit = createWindowTextSplit.replace("height: 34", "height: 16") - createWindowText = createWindowText[:topIndex] + createWindowTextSplit - createWindow.close() - - createWindow = open(notionResourcesPath + '/app/renderer/index.js', 'wt') - createWindow.write(createWindowText) - createWindow.close() - else: - print(" There is no files at Notion/resources/app/renderer/index.js - Nothing was done") - sleep(sleepTime) - print("-Done-\n") - sleep(sleepTime) - - print("Step 5") - sleep(sleepTime) - # Step 5 - if os.path.isfile(notionResourcesPath + '/app/main/main.js'): - print(" Adding tray support at Notion/resources/app/main/main.js") - sleep(sleepTime) - print(" Adding context menu with settings to tray") - sleep(sleepTime) - - hotkeysCodeText = """ - const {Tray, Menu} = require("electron"); - let tray = null; - electron_1.app.on("ready", function() { - handleReady(); - - const path = require("path"); - const Store = require(path.join(__dirname,'../','store.js')); - - const store = new Store({ - configName: "user-preferences", - defaults: { - alwaysMaximized: false, - CloseToTrayCheckbox: false, - runHidden: false - } - }); - - var RunAtStartupCheckboxState = electron_1.app.getLoginItemSettings().openAtLogin; - var RunHiddenCheckboxState = store.get("runHidden"); - var AlwaysMaximizedCheckboxState = store.get("alwaysMaximized"); - var CloseToTrayCheckboxState = store.get("CloseToTrayCheckbox"); - - tray = new Tray(path.join(__dirname,"./icon.ico")); - const contextMenu = Menu.buildFromTemplate([ - { - id: "RunAtStartupCheckbox", - label: "Run at Startup", - type:"checkbox", - checked: RunAtStartupCheckboxState, - click() { - var isChecked = contextMenu.getMenuItemById("RunAtStartupCheckbox").checked; - if(isChecked) { - electron_1.app.setLoginItemSettings({ openAtLogin: true}); - } else { - electron_1.app.setLoginItemSettings({ openAtLogin: false}); - } - } - }, - { - id: "runHidden", - label: "Run Hidden", - type:"checkbox", - checked: RunHiddenCheckboxState, - click() { - var isChecked = contextMenu.getMenuItemById("runHidden").checked; - if(isChecked) { - store.set("runHidden", true); - } else { - store.set("runHidden", false); - } - } - }, - { - id: "AlwaysMaximizedCheckbox", - label: "Open Maximized", - type: "checkbox", - checked: AlwaysMaximizedCheckboxState, - click() { - var isChecked = contextMenu.getMenuItemById("AlwaysMaximizedCheckbox").checked; - if(isChecked) { - store.set("alwaysMaximized", true); - } else { - store.set("alwaysMaximized", false); - } - } - }, - { - id: "CloseToTrayCheckbox", - label: "Close To Tray", - type: "checkbox", - checked: CloseToTrayCheckboxState, - click() { - var isChecked = contextMenu.getMenuItemById("CloseToTrayCheckbox").checked; - if(isChecked) { - store.set("CloseToTrayCheckbox", true); - } else { - store.set("CloseToTrayCheckbox", false); - } - } - }, - { - type: "separator" - }, - { - label: "Quit", - role: "quit" - } - ]); - tray.setContextMenu(contextMenu); - - tray.on("click", function() { - var win = electron_1.BrowserWindow.getAllWindows()[0]; - var alwaysMax = contextMenu.getMenuItemById("AlwaysMaximizedCheckbox").checked; - if (win.isVisible()) { - if(win.isMinimized()) { - win.show() - } else { - win.hide(); - } - } else { - if(alwaysMax){ - win.maximize(); - } else { - win.show(); - } - } - }); - - var notionToggleHotkey = 'ctrl+shift+a'; - const globalShortcut = electron_1.globalShortcut; - globalShortcut.register(notionToggleHotkey, function() { - var win = electron_1.BrowserWindow.getAllWindows()[0]; - var alwaysMax = contextMenu.getMenuItemById("AlwaysMaximizedCheckbox").checked; - if (win.isVisible()) { - win.hide(); - } else { - if(alwaysMax){ - win.maximize(); - } else { - win.show(); - } - } - }); - }); - """ - mainJs = open(notionResourcesPath + '/app/main/main.js', 'rt') - mainJsText = mainJs.read() - mainJs.close() - - print(" Adding hotkey to show/hide Notion window") - sleep(sleepTime) - if "var notionToggleHotkey" in mainJsText: - mainJsText = re.sub(r"var notionToggleHotkey = '([A-Za-z0-9+_\./\\-]*)'", "var notionToggleHotkey = " + windowToggleHotkey, mainJsText) - else : - mainJsText = mainJsText.replace('electron_1.app.on("ready", handleReady);' , hotkeysCodeText) - mainJsText = mainJsText.replace('win.focus()','win.show()',1) - - print(" Copying tray icon ""icon.ico"" to /app/main/ ") - sleep(sleepTime) - copyfile('./resources/icon.ico', notionResourcesPath + '/app/main/icon.ico' ) - - print(" Copying settings saver class ""store.js"" to /app/ ") - sleep(sleepTime) - copyfile('./resources/store.js', notionResourcesPath + '/app/store.js' ) - - mainJs = open(notionResourcesPath + '/app/main/main.js', 'wt') - mainJs.write(mainJsText) - mainJs.close() - sleep(sleepTime) - else: - print(" There is no files at Notion/resources/app/main/main.js - Nothing was done") - sleep(sleepTime) - print("-Done-") - sleep(sleepTime) - - sleep(0.5) - print("========= END OF LOG =========") - sleep(0.5) - print(""" - - ____ _ _____ ____ _ _ _____ ____ - | _ \ / \|_ _/ ___| | | | ____| _ \ - | |_) / _ \ | || | | |_| | _| | | | | - | __/ ___ \| || |___| _ | |___| |_| | - |_| /_/ \_|_| \____|_| |_|_____|____/ - - """) - sleep(4) -except Exception as e: - sleep(0.5) - print("========= END OF LOG =========") - sleep(0.5) - print(""" - __________ ____ ____ ____ - / ____/ __ \/ __ \/ __ \/ __ \\ - / __/ / /_/ / /_/ / / / / /_/ / - / /___/ _, _/ _, _/ /_/ / _, _/ - /_____/_/ |_/_/ |_|\____/_/ |_| - - \n\n""" + str(e)) - os.system('pause') diff --git a/Notion Customization/Customization Remover.py b/Notion Customization/Customization Remover.py deleted file mode 100644 index 75427ad..0000000 --- a/Notion Customization/Customization Remover.py +++ /dev/null @@ -1,80 +0,0 @@ -""" -# Step 1 -1.Locating notion app at ./%Username%/AppData/Local/Programs/Notion -2.Unpacking app.asar in new folder at./Notion/resources/app -3.Renaming file app.asar to app.asar.bak (because instead app won't use folder "app" to get all resources from it) -4.If app.asar already unpacked - it will try to locate folder "app" and skip to next step - -# Step 2 -1. Editing userscript file to replace "full_path_to_custom_style" with full path to ./custom_style.css -2. Adding userscript from main.user.js(should be in same folder with this .py file) to the ./app/renderer/preload.js -3. If there is already userscript - it will overwrite it. - -# Step 3 -1. Adding property "frame: false" to the place where application window is creating - -# Step 4 -1. Changes "window drag" area, to make window draggable - 1.1. You can change it by yourself, if you experiencing problems with dragging window or with clicking buttons on app topbar - Because this "draggable area" is creating on top of the other stuff, so if it will have button behind it - it won't be clickable. - You should change top,left,right properties. Now it's 2px on top, 390px on left and 420px on right; -""" -import os -from time import sleep -from shutil import copyfile -from shutil import rmtree -import re - -try: - sleepTime = 0.5 - sleep(sleepTime) - print("========= START OF LOG =========") - sleep(sleepTime) - LOCALAPPDATA = os.getenv('LOCALAPPDATA') - LOCALAPPDATA = LOCALAPPDATA.replace('\\', '/') - notionResourcesPath = LOCALAPPDATA + '/Programs/Notion/resources' - - if os.path.exists(notionResourcesPath + '/app'): - rmtree(notionResourcesPath + '/app') - print("Removing ""app"" folder") - sleep(sleepTime) - else: - print("There is no ""app"" folder at ./Notion/resources/app - Skipping this step") - sleep(sleepTime) - - if os.path.isfile(notionResourcesPath + '/app.asar.bak'): - renameSource = notionResourcesPath + '/app.asar.bak' - renameDestination = notionResourcesPath + '/app.asar' - os.rename(renameSource, renameDestination) - print("Renaming app.asar.bak to app.asar") - sleep(sleepTime) - else: - print("There is no ""app.asar.bak"" at ./Notion/resources/app.asar.bak - Skipping this step") - sleep(sleepTime) - - sleep(0.5) - print("========= LOG =========") - sleep(0.5) - print(""" - - ____ _____ __ __ _____ _______ ____ - | _ \| ____| \/ |/ _ \ \ / | ____| _ \ - | |_) | _| | |\/| | | | \ \ / /| _| | | | | - | _ <| |___| | | | |_| |\ V / | |___| |_| | - |_| \_|_____|_| |_|\___/ \_/ |_____|____/ - - """) - sleep(2) -except Exception as e: - sleep(0.5) - print("========= END OF LOG =========") - sleep(0.5) - print(""" - __________ ____ ____ ____ - / ____/ __ \/ __ \/ __ \/ __ \\ - / __/ / /_/ / /_/ / / / / /_/ / - / /___/ _, _/ _, _/ /_/ / _, _/ - /_____/_/ |_/_/ |_|\____/_/ |_| - - \n\n""" + str(e)) - os.system('pause') diff --git a/Notion Customization/resources/custom_style.css b/Notion Customization/resources/custom_style.css deleted file mode 100644 index c65a7de..0000000 --- a/Notion Customization/resources/custom_style.css +++ /dev/null @@ -1,135 +0,0 @@ -/* Window control buttons block */ -#window-buttons-area { - padding-left: 14px; -} -/* Light Theme style for window contoll buttons */ -.notion-light-theme .window-buttons { - background: rgb(255, 255, 255); - color: black; - border: 0; - margin: 0px 0px 0px 9px; - width: 32px; - line-height: 26px; - border-radius: 4px; - font-size: 16px; - transition-duration: 0.2s; - font-weight: bold; -} -.notion-light-theme .window-buttons:hover { - background: rgb(239, 239, 239); -} -/* Light Theme style for window contoll buttons */ - - -/* Dark Theme style for window contoll buttons */ -.notion-dark-theme .window-buttons { - background: rgb(47, 52, 55); - border: 0; - margin: 0px 0px 0px 9px; - width: 32px; - line-height: 26px; - border-radius: 4px; - font-size: 16px; - transition-duration: 0.2s; -} -.notion-dark-theme .window-buttons:hover { - background: rgb(71, 76, 80); -} -/* Dark Theme style for window contoll buttons */ - - - -/* To make cursor style as pointer when hover on scrollbar */ -.notion-scroller { - cursor: auto; -} -/* Scrollbar size */ -::-webkit-scrollbar { - width: 5px; /* To change vertical scrollbar width */ - height: 12px; /* To change horizontal scrollbar height */ -} -/* Element where vertical and horizontal scrollbars converge */ -::-webkit-scrollbar-corner{ - background-color: transparent; -} - -/* Light Theme style for Scrollbars */ -.notion-light-theme ::-webkit-scrollbar-thumb { - border-radius: 5px; - background-color: #afafaf; -} -.notion-light-theme ::-webkit-scrollbar-track { - border-radius: 5px; - background-color: #e4e4e4; -} -.notion-light-theme ::-webkit-scrollbar-thumb:hover{ - background: #969696; -} -/* Light Theme style for Scrollbars */ - - -/* Dark Theme style for Scrollbars */ -.notion-dark-theme ::-webkit-scrollbar-track { - border-radius: 5px; - background-color: #3d3d42; -} -.notion-dark-theme ::-webkit-scrollbar-thumb { - border-radius: 5px; - background-color: #5d5d5d; -} -.notion-dark-theme ::-webkit-scrollbar-thumb:hover{ - background: #868686; -} -/* Dark Theme style for Scrollbars */ - - - -/* Changing table padding to make it more wider */ -[style^="flex-shrink: 0; flex-grow: 1; width: 100%; max-width: 100%; display: flex; align-items: center; flex-direction: column; font-size: 16px; color: rgba(255, 255, 255, 0.9); padding: 0px 96px 30vh;"] .notion-table-view, -[class="notion-scroller"]>.notion-table-view { - padding-left: 35px !important; - padding-right: 15px !important; - min-width: 0% !important; -} -/* Changing the width of horizontal scroller, because of wider table */ -[style^="flex-shrink: 0; flex-grow: 1; width: 100%; max-width: 100%; display: flex; align-items: center; flex-direction: column; font-size: 16px; color: rgba(255, 255, 255, 0.9); padding: 0px 96px 30vh;"] .notion-selectable .notion-scroller.horizontal::-webkit-scrollbar-track { - margin-left: 10px; - margin-right: 10px; -} -/* Hide "+" button when you hover on table row, because of it's useless for me, and this buttons takes like 15px from the left side of table */ -[style^="flex-shrink: 0; flex-grow: 1; width: 100%; max-width: 100%; display: flex; align-items: center; flex-direction: column; font-size: 16px; color: rgba(255, 255, 255, 0.9); padding: 0px 96px 30vh;"] .notion-table-view [style^="opacity:"]>[role="button"], -[class="notion-scroller"]>.notion-table-view [style^="opacity:"]>[role="button"] { - display:none !important; -} - -/* Same as with table. Makes board view more wider */ -.notion-board-view { - padding-left: 10px !important; - padding-right: 10px !important; -} -/* Changing content block position, less height - higher content block */ -[style^="position: relative; width: 100%; display: flex; flex-direction: column; align-items: center; height: 30vh;"] { - height: 12vh !important; -} -/* Changing cover image height to match higher content block */ -[style^="position: relative; width: 100%; display: flex; flex-direction: column; align-items: center; height: 30vh;"] img { - height: 20vh !important; -} -/* Changing table columns width */ -[data-block-id^="PutYourIDHere"]>[style^="display: flex; position: absolute; background: rgb(47, 52, 55); z-index: 82; height: 33px; color: rgba(255, 255, 255, 0.6);"]>div:nth-child(1)>div:nth-child(10)>div:nth-child(1), -[data-block-id^="PutYourIDHere"]>[style^="position: relative; min-width: calc(100% - 192px);"]>[data-block-id]>div:nth-child(10), -[data-block-id^="PutYourIDHere"]>div:nth-child(5)>div:nth-child(10){ - width: 45px !important; -} -/* Hiding selection, because of changing columns width bugs selection inside table */ -[data-block-id^="PutYourIDHere"] [style^="position: absolute; top: 0px; left: 0px; pointer-events: none;"]:not(.notion-presence-container) { - display: none; -} - -/* Changing table header icons to make it smaller */ -[style^="display: flex; position: absolute; background: rgb(47, 52, 55); z-index: 82; height: 33px; color: rgba(255, 255, 255, 0.6);"] div:nth-child(1) svg { - height: 10px !important; - width: 10px !important; - margin-right: -4px; -} - diff --git a/Notion Customization/resources/main.user.js b/Notion Customization/resources/main.user.js deleted file mode 100644 index 62e1267..0000000 --- a/Notion Customization/resources/main.user.js +++ /dev/null @@ -1,93 +0,0 @@ -function userscript() { - /* Style Injecting */ - var fs = require("fs"); - let css = fs.readFileSync("full_path_to_custom_style"); //will be replaced in python patcher - let head, style; - head = document.getElementsByTagName('head')[0]; - if (!head) { return; } - style = document.createElement('style'); - style.type = 'text/css'; - style.innerHTML = css; - head.appendChild(style); - /* Style Injecting */ - - /* Window Control Buttons */ - var buttonsIntervalId = window.setInterval(addButtonsOnLoad,100); - function addButtonsOnLoad() { - if(document.querySelector('div.notion-topbar > div') == undefined) { - return; - } - var browserWindow = require('electron').remote.getCurrentWindow(); - var element = document.createElement("div"); - element.id = "window-buttons-area"; - var node = document.querySelector('div.notion-topbar > div'); - node.appendChild(element); - - node = document.querySelector("#window-buttons-area"); - - /* AlwaysOnTop Button */ - element = document.createElement("button"); - element.classList.add("window-buttons"); - element.innerHTML = "🠛"; - element.onclick = function () { - if(!browserWindow.isAlwaysOnTop()) { - browserWindow.setAlwaysOnTop(true); - this.innerHTML = "🠙"; - } else { - browserWindow.setAlwaysOnTop(false); - this.innerHTML = "🠛"; - } - }; - node.appendChild(element); - /* AlwaysOnTop Button */ - - /* Minimize Button */ - element = document.createElement("button"); - element.classList.add("window-buttons"); - element.innerHTML = "⚊"; - element.onclick = function () { browserWindow.minimize(); }; - node.appendChild(element); - /* Minimize Button */ - - /* Maximize Button */ - element = document.createElement("button"); - element.classList.add("window-buttons"); - element.innerHTML = "▢"; - element.onclick = function () { - if (!browserWindow.isMaximized()) { - browserWindow.maximize(); - } else { - browserWindow.unmaximize(); - } - }; - node.appendChild(element); - /* Maximize Button */ - - /* Close Button */ - const path = require("path") - element = document.createElement("button"); - element.classList.add("window-buttons"); - element.innerHTML = "⨉"; - element.onclick = function () { - const Store = require(path.join(__dirname,'../','store.js')); - const store = new Store({ - configName: "user-preferences", - defaults: { - CloseToTrayCheckbox: false - } - }); - var CloseToTrayCheckboxState = store.get("CloseToTrayCheckbox"); - if(CloseToTrayCheckboxState) { - browserWindow.hide(); - } else { - browserWindow.close(); - } - }; - node.appendChild(element); - /* Close Button */ - - window.clearInterval(buttonsIntervalId); - } - /* Window Control Buttons */ -} -require('electron').remote.getGlobal('setTimeout')(() => { userscript();}, 100); \ No newline at end of file diff --git a/Notion Customization/resources/store.js b/Notion Customization/resources/store.js deleted file mode 100644 index 3c5737e..0000000 --- a/Notion Customization/resources/store.js +++ /dev/null @@ -1,30 +0,0 @@ -const electron = require('electron'); -const path = require('path'); -const fs = require('fs'); - -class Store { - constructor(opts) { - const userDataPath = __dirname - this.path = path.join(userDataPath, opts.configName + '.json'); - - this.data = parseDataFile(this.path, opts.defaults); - } - - get(key) { - return this.data[key]; - } - - set(key, val) { - this.data[key] = val; - fs.writeFileSync(this.path, JSON.stringify(this.data)); - } -} - -function parseDataFile(filePath, defaults) { - try { - return JSON.parse(fs.readFileSync(filePath)); - } catch(error) { - return defaults; - } -} -module.exports = Store; \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..9d846ae --- /dev/null +++ b/README.md @@ -0,0 +1,229 @@ +# notion enhancer + +an enhancer/customiser for the all-in-one productivity workspace [notion.so](https://www.notion.so/) + +## installation + +currently, only win10 is supported. it is possible to run this script via the wsl to modify the win10 notion app. + +(the [styles](#styling) should also work for the web version. +these can be installed via an extension like [stylus](https://chrome.google.com/webstore/detail/stylus/clngdbkpkpeebahjckkjfobafhncgmne?hl=en) +or a built-in feature like [userChrome.css](https://www.userchrome.org/).) + +1. install [node.js](https://nodejs.org/en/) (if using the wsl, it is recommended to install via [nvm](https://github.com/nvm-sh/nvm#install--update-script).) +2. install [python](https://www.python.org/) (if using the wsl, follow [this guide](https://docs.python-guide.org/starting/install3/linux/).) +3. reboot. +4. in cmd (on windows) or bash (with wsl), run `npm install -g asar` (check installation by running `asar`). +5. [download this enhancer](https://github.com/dragonwocky/notion-enhancer/archive/master.zip) & extract to a location it can safely remain (this must be in the windows filesystem, + even if you are running the script from the wsl). +6. ensure notion is closed. +7. optional: to remove previous versions of notion enhancer, run `cleaner.py` +8. optional: modify the `resources/user.css` files to your liking. +9. run `customiser.py` to build changes. + +done: run notion and enjoy + +**oh no, now my app won't open!** + +1. kill any notion tasks in the task manager (`ctrl+shift+esc`). +2. run `cleaner.py`. +3. reboot. +4. follow instructions above (ensuring notion _isn't_ running! again, check task manager). + +## this is a fork + +credit where credit is due, this was originally made by Uzver (github: [@TarasokUA](https://github.com/TarasokUA), +telegram: [UserFromUkraine](https://t.me/UserFromUkraine), discord: Uzver#8760). + +he has approved my go-ahead with this fork, as he himself no longer wishes to continue development on the project. + +## features + +### titlebar + +default windows titlebar/frame has been replaced by one more fitting to the theme of the app. + +this includes the addition of an extra button, "always on top" +symbolised with an arrow (4th from the right). when toggled to point up, +notion will remain the top visible window even if not focused. + +to customise which characters are used for these buttons, open in the `resources/preload.js` file, +find the relevant button (read the comments) and replace its icon with your chosen unicode character (e.g. +replacing `element.innerHTML = '▢';` -> `element.innerHTML = '🙄';`). + +### nicer scrollbars + +i mean, yeah. get rid of those ugly default scrollbars and use nice inconspicuous +ones that actually look as if they're part of notion. + +to add these to the web version, copy lines 43 - 87 from `user.css` into your css customiser. + +### hotkey + +by default, `ctrl+shift+a` (will hide/show all notion windows to/from the tray). + +to set your own, open `customiser.py` and change line 16 (`hotkey = 'ctrl+shift+a'`) +to your preference. you will need to run or re-run `customiser.py` afterwards. + +### tray + +- single-click to toggle app visibility. right click to open menu. +- settings will be saved in `%localappdata%/Programs/Notion/resources/app/user-preferences.json` +- **run on startup**: run notion on boot/startup. (default: true) +- **hide on open**: hide the launch of notion to the tray. (default: false) +- **open maximised**: maximise the app on open. (default: false) +- **close to tray**: app will close to the tray when the `⨉` button is pressed rather than closing outright. (default: false) + +## styling + +due to `customiser.py` setting up a direct link to `resources/user.css`, +changes will be applied instantly on notion reload +(no need to re-run `customiser.py` every time you want to change some styles). + +these should also work for the web version, if copied into your css customiser. + +css below will work for every instance of the element, but if you wish to hide only a specific element +(e.g. the '+ new' table row) it is recommended that you prepend each selector with `[data-block-id='ID']` ([video tutorial on fetching IDs](https://www.youtube.com/watch?v=6V7eqShm_4w)). + +#### wider page view + +```css +.notion-peek-renderer > div:nth-child(2) { + max-width: 85vw !important; +} +``` + +#### thinner cover image + +```css +[style^='position: relative; width: 100%; display: flex; flex-direction: column; align-items: center; height: 30vh;'] { + height: 12vh !important; +} +[style^='position: relative; width: 100%; display: flex; flex-direction: column; align-items: center; height: 30vh;'] + img { + height: 20vh !important; +} +``` + +#### table columns below 100px + +**not recommended!** this is unreliable and will cause bugs. +coincidentally, this is also what the youtube video linked above shows how to do. +as it is a per-table-column style, unlike all others here, it must be prepended with the block ID. + +```css +[data-block-id^='ID'] + > [style^='display: flex; position: absolute; background: rgb(47, 52, 55); z-index: 82; height: 33px; color: rgba(255, 255, 255, 0.6);'] + > div:nth-child(1) + > div:nth-child(10) + > div:nth-child(1), +[data-block-id^='ID'] + > [style^='position: relative; min-width: calc(100% - 192px);'] + > [data-block-id] + > div:nth-child(10), +[data-block-id^='ID'] > div:nth-child(5) > div:nth-child(10) { + width: 45px !important; +} +[data-block-id^='ID'] + [style^='position: absolute; top: 0px; left: 0px; pointer-events: none;']:not(.notion-presence-container) { + display: none; +} +``` + +#### hide '+ new' table row + +```css +.notion-table-view-add-row { + display: none !important; +} +``` + +#### hide calculations table row + +```css +[] .notion-table-view-add-row + div { + display: none !important; +} +``` + +#### hide '+ new' board row + +```css +.notion-board-group + [style='user-select: none; transition: background 120ms ease-in 0s; cursor: pointer; display: inline-flex; align-items: center; flex-shrink: 0; white-space: nowrap; height: 32px; border-radius: 3px; font-size: 14px; line-height: 1.2; min-width: 0px; padding-left: 6px; padding-right: 8px; color: rgba(255, 255, 255, 0.4); width: 100%;'] { + display: none !important; +} +``` + +#### hide board view hidden columns + +```css +.notion-board-view > [data-block-id] > div:nth-last-child(2), +.notion-board-view > [data-block-id] > div:first-child > div:nth-last-child(2) { + display: none !important; +} +``` + +#### hide board view 'add a group' + +```css +.notion-board-view > [data-block-id] > div:last-child, +.notion-board-view > [data-block-id] > div:first-child > div:last-child { + display: none !important; +} +``` + +#### centre-align table column headers + +```css +.notion-table-view-header-cell > div > div { + margin: 0px auto; +} +``` + +#### smaller table column header icons + +```css +[style^='display: flex; position: absolute; background: rgb(47, 52, 55); z-index: 82; height: 33px; color: rgba(255, 255, 255, 0.6);'] + div:nth-child(1) + svg { + height: 10px !important; + width: 10px !important; + margin-right: -4px; +} +``` + +#### remove icons from table column headers + +```css +.notion-table-view-header-cell [style^='margin-right: 6px;'] { + display: none !important; +} +``` + +#### removing/decreasing side padding for tables + +```css +[style^='flex-shrink: 0; flex-grow: 1; width: 100%; max-width: 100%; display: flex; align-items: center; flex-direction: column; font-size: 16px; color: rgba(255, 255, 255, 0.9); padding: 0px 96px 30vh;'] + .notion-table-view, +[class='notion-scroller'] > .notion-table-view { + padding-left: 35px !important; + padding-right: 15px !important; + min-width: 0% !important; +} +[style^='flex-shrink: 0; flex-grow: 1; width: 100%; max-width: 100%; display: flex; align-items: center; flex-direction: column; font-size: 16px; color: rgba(255, 255, 255, 0.9); padding: 0px 96px 30vh;'] + .notion-selectable + .notion-scroller.horizontal::-webkit-scrollbar-track { + margin-left: 10px; + margin-right: 10px; +} +``` + +#### removing/decreasing side padding for boards + +```css +.notion-board-view { + padding-left: 10px !important; + padding-right: 10px !important; +} +``` diff --git a/cleaner.py b/cleaner.py new file mode 100644 index 0000000..0026b0c --- /dev/null +++ b/cleaner.py @@ -0,0 +1,60 @@ + +# Notion Enhancer +# (c) 2020 dragonwocky +# (c) 2020 TarasokUA +# (https://dragonwocky.me/) under the MIT license + +import os +import sys +import platform +import subprocess +from shutil import rmtree +from time import sleep + +# f'{bold}=== title ==={normal}' = headers +# '*' = information +# '...' = actions +# '###' = warnings +# '>' = exit + +bold = subprocess.run( + ['tput', 'bold'], stdout=subprocess.PIPE).stdout.rstrip().decode('utf-8') +normal = subprocess.run( + ['tput', 'sgr0'], stdout=subprocess.PIPE).stdout.rstrip().decode('utf-8') + +print(f'{bold}=== NOTION ENHANCER CLEANING LOG ==={normal}\n') +try: + filepath = '' + if 'microsoft' in platform.uname()[3].lower() and sys.platform == 'linux': + filepath = '/mnt/c/' + \ + subprocess.run( + ['cmd.exe', '/c', 'echo', '%localappdata%'], stdout=subprocess.PIPE).stdout \ + .rstrip().decode('utf-8')[3:].replace('\\', '/') + '/Programs/Notion/resources' + elif sys.platform == 'win32': + filepath = subprocess.run(['echo', '%localappdata%'], stdout=subprocess.PIPE).stdout \ + .rstrip().decode('utf-8').replace('\\', '/') + ' /Programs/Notion/resources' + else: + print(' > script not compatible with your os!\n (report this to dragonwocky#8449 on discord)') + exit() + + if os.path.exists(filepath + '/app'): + print( + f' ...removing folder {filepath}/app/') + rmtree(filepath + '/app') + else: + print( + f' * {filepath}/app/ was not found: step skipped.') + + if os.path.isfile(filepath + '/app.asar.bak'): + print(' ...renaming asar.app.bak to asar.app') + os.rename(filepath + '/app.asar.bak', filepath + '/app.asar') + else: + print( + f' * {filepath}/app.asar.bak was not found: step skipped.') + + print(f'\n{bold}>>> SUCCESSFULLY CLEANED <<<{normal}') + +except Exception as e: + print(f'\n{bold}### ERROR ###{normal}\n{str(e)}') + +print(f'\n{bold}=== END OF LOG ==={normal}') diff --git a/customiser.py b/customiser.py new file mode 100644 index 0000000..fa7b0f2 --- /dev/null +++ b/customiser.py @@ -0,0 +1,174 @@ + +# Notion Enhancer +# (c) 2020 dragonwocky +# (c) 2020 TarasokUA +# (https://dragonwocky.me/) under the MIT license + +import re +import os +import sys +import platform +import subprocess +from shutil import copyfile +from time import sleep + + +# for toggling notion visibility +hotkey = 'ctrl+shift+a' + +# f'{bold}=== title ==={normal}' = headers +# '*' = information +# '...' = actions +# '##' = warnings +# '>' = exit + +bold = subprocess.run( + ['tput', 'bold'], stdout=subprocess.PIPE).stdout.rstrip().decode('utf-8') +normal = subprocess.run( + ['tput', 'sgr0'], stdout=subprocess.PIPE).stdout.rstrip().decode('utf-8') + +print(f'{bold}=== NOTION ENHANCER CUSTOMISATION LOG ==={normal}\n') + +try: + filepath = '' + if 'microsoft' in platform.uname()[3].lower() and sys.platform == 'linux': + filepath = '/mnt/c/' + \ + subprocess.run( + ['cmd.exe', '/c', 'echo', '%localappdata%'], stdout=subprocess.PIPE).stdout \ + .rstrip().decode('utf-8')[3:].replace('\\', '/') + '/Programs/Notion/resources' + elif sys.platform == 'win32': + filepath = subprocess.run(['echo', '%localappdata%'], stdout=subprocess.PIPE).stdout \ + .rstrip().decode('utf-8').replace('\\', '/') + ' /Programs/Notion/resources' + else: + print(' > script not compatible with your os!\n (report this to dragonwocky#8449 on discord)') + exit() + + if os.path.isfile(filepath + '/app.asar'): + print(' ...unpacking app.asar') + subprocess.run(['asar', 'extract', filepath + + '/app.asar', filepath + '/app']) + print(' ...renaming asar.app to asar.app.bak') + os.rename(filepath + '/app.asar', filepath + '/app.asar.bak') + else: + print(f' ## file {filepath}/app.asar not found!') + print(' * attempting to locate') + if os.path.exists(filepath + '/app'): + print(' * app.asar was already unpacked: step skipped.') + else: + print(' > nothing found: exiting.') + exit() + + if os.path.isfile(filepath + '/app/renderer/preload.js'): + print(f' ...adding preload.js to {filepath}/app/renderer/preload.js') + with open(filepath + '/app/renderer/preload.js') as content: + if '/* === INJECTION MARKER === */' in content.read(): + print(' * preload.js already added. replacing it.') + content.seek(0) + original = [] + for num, line in enumerate(content): + if '/* === INJECTION MARKER === */' in line: + break + original += line + with open(filepath + '/app/renderer/preload.js', 'w') as write: + write.writelines(original) + else: + with open(filepath + '/app/renderer/preload.js', 'a') as append: + append.write('\n\n') + with open(filepath + '/app/renderer/preload.js', 'a') as append: + print(' ...linking to ./resources/user.css') + with open('./resources/preload.js') as insert: + append.write(insert.read().replace( + '$$$user.css$$$', 'C:/' + + os.getcwd().replace('\\', ' / ')[6:] + + '/resources/user.css')) + else: + print( + f' * {filepath}/app/renderer/preload.js was not found: step skipped.') + + if os.path.isfile(filepath + '/app/main/createWindow.js'): + with open(filepath + '/app/main/createWindow.js') as content: + content = content.read() + print( + f' ...making window frameless @ {filepath}/app/main/createWindow.js') + if '{ frame: false, show: false' not in content: + content = content.replace( + '{ show: false', '{ frame: false, show: false') + print( + f' ...adding "open hidden" capabilities to {filepath}/app/main/createWindow.js') + content = re.sub('\\s*\\/\\* === INJECTION START === \\*\\/.*?\\/\\* === INJECTION END === \\*\\/\\s*', + 'window.show()', content, flags=re.DOTALL).replace('window.show()', """ + /* === INJECTION START === */ + const path = require('path'), + store = new (require(path.join(__dirname, '..', 'store.js')))({ + config: 'user-preferences', + defaults: { + openhidden: false, + maximised: false + } + }); + if (!store.get('openhidden') || electron_1.BrowserWindow.getAllWindows().some(win => win.isVisible())) + { window.show(); if (store.get('maximised')) window.maximize(); } + /* === INJECTION END === */ + """) + with open(filepath + '/app/main/createWindow.js', 'w') as write: + write.write(content) + else: + print( + f' * {filepath}/app/main/createWindow.js was not found: step skipped.') + + if os.path.isfile(filepath + '/app/renderer/index.js'): + with open(filepath + '/app/renderer/index.js') as content: + print( + f' ...adjusting drag area for frameless window in {filepath}/app/renderer/index.js') + content = content.read() + top = content.rfind('top') + content = content[:top] + content[top:].replace( + 'right: 0', 'right: 420').replace( + 'top: 0', 'top: 1 ').replace( + 'height: 34', 'height: 16') + with open(filepath + '/app/renderer/index.js', 'w') as write: + write.write(content) + else: + print( + f' * {filepath}/app/renderer/index.js was not found: step skipped.') + + if os.path.isfile(filepath + '/app/main/main.js'): + with open(filepath + '/app/main/main.js') as content: + print( + f' ...adding tray support (inc. context menu with settings) to {filepath}/app/main/main.js') + print( + f' ...adding window toggle hotkey to {filepath}/app/main/main.js') + content = content.read() + with open(filepath + '/app/main/main.js', 'w') as write: + if '/* === INJECTION MARKER === */' in content: + print(' * hotkey.js already added. replacing it.') + original = [] + for line in content.splitlines(): + if '/* === INJECTION MARKER === */' in line: + break + original.append(line) + write.write('\n'.join(original)) + else: + write.write(content.replace( + 'electron_1.app.on("ready", handleReady);', + 'electron_1.app.on("ready", () => handleReady() && enhancements());') + '\n') + with open(filepath + '/app/main/main.js', 'a') as append: + with open('./resources/hotkey.js') as insert: + append.write('\n' + insert.read().replace( + '$$$hotkey$$$', hotkey)) + print( + f' ...copying tray icon ./resources/notion.ico to {filepath}/app/main/') + copyfile('./resources/notion.ico', filepath + '/app/main/notion.ico') + print( + f' ...copying datastore wrapper ./resources/store.js to {filepath}/app/') + copyfile('./resources/store.js', filepath + '/app/store.js') + else: + print( + f' * {filepath}/app/main/main.js was not found: step skipped.') + + print(f'\n{bold}>>> SUCCESSFULLY CUSTOMISED <<<{normal}') + +except Exception as e: + print(f'\n{bold}### ERROR ###{normal}\n{str(e)}') + +print(f'\n{bold}=== END OF LOG ==={normal}') diff --git a/docs.json b/docs.json new file mode 100644 index 0000000..ea918d4 --- /dev/null +++ b/docs.json @@ -0,0 +1,34 @@ +{ + "title": "notion enhancer", + "primary": "rgb(75, 133, 209)", + "git": "https://github.com/dragonwocky/notion-enhancer/blob/master/", + "footer": "[Edit on GitHub](__git__) // © 2020 dragonwocky & Uzver, under the [MIT license](https://choosealicense.com/licenses/mit/).", + "card": { + "description": "an enhancer/customiser for the all-in-one productivity workspace notion.so", + "url": "https://dragonwocky.me/notion-enhancer/" + }, + "icon": { + "light": "resources/notion.ico" + }, + "overwrite": true, + "exclude": [ + "cleaner.py", + "customiser.py", + "resources/hotkey.js", + "resources/preload.js", + "resources/store.js", + "resources/user.css", + ".gitignore" + ], + "nav": [ + ["index.html", "README.md"], + "resources", + ["changelog.html", "CHANGELOG.md"], + [ + "license", + "https://github.com/dragonwocky/notion-enhancer/blob/master/LICENSE" + ], + ["github", "https://github.com/dragonwocky/notion-enhancer/"], + ["me (dragonwocky)", "https://dragonwocky.me/"] + ] +} diff --git a/docs/LICENSE b/docs/LICENSE new file mode 100644 index 0000000..26171ec --- /dev/null +++ b/docs/LICENSE @@ -0,0 +1,22 @@ +MIT License + +Copyright (c) 2020 TarasokUA +Copyright (c) 2020 dragonwocky + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/docs/changelog.html b/docs/changelog.html new file mode 100644 index 0000000..21b2faf --- /dev/null +++ b/docs/changelog.html @@ -0,0 +1,90 @@ +changelog | notion enhancer

notion enhancer

+ +
+

+ changelog +

+

if something is crossed out, then it is no longer a feature included by default, +but can still easily be enabled by following instructions in the docs.

+ +
+
+

+ v0.5.0 (wip) +

+
    +
  • new: running from the WSL.
  • +
  • improved: code has been refactored and cleaned up, +inc. file renaming.
  • +
  • bugfix: un-break having multiple notion windows open. // TODO
  • +
+

(forked by @dragonwocky.)

+ +
+
+

+ v0.4.1 (2020-02-13) +

+
    +
  • bugfix: wider table & the "+" button not working in database pages.
  • +
+
+

notion-enhancer.v4.1.zip

+
+ +
+
+

+ v0.4.0 +

+
    +
  • new: tray icon.
  • +
  • new: app startup options (+ saving).
  • +
  • new: Reset.py
  • +
  • improved: better output from Customization Patcher.py.
  • +
  • bugfix: wider tables in "short page" mode.
  • +
  • bugfix: unclickable buttons/draggable area (of titlebar).
  • +
+ +
+
+

+ v0.3.0 +

+
    +
  • new: show/hide window hotkey.
  • +
  • new: app startup options.
  • +
  • style: smaller table icons.
  • +
+
+

notion-enhancer.v3.zip

+
+ +
+
+

+ v0.2.0 +

+
    +
  • new: light/dark theme support for window control buttons + scrollbars.
  • +
  • new: custom styles directly linked to the enhancer resources + compatible with web version.
  • +
  • improved: making table column width go below 100px.
  • +
+ +
+
+

+ v0.1.0 +

+
    +
  • new: custom window control buttons.
  • +
  • removed: default titlebar/menubar.
  • +
  • removed: huge padding of board view.
  • +
  • removed: huge padding of table view.
  • +
  • optional: making table column width go below 100px.
  • +
  • style: thinner cover image + higher content block.
  • +
  • style: scrollbars.
  • +
+ +
\ No newline at end of file diff --git a/docs/docs.css b/docs/docs.css new file mode 100644 index 0000000..20da200 --- /dev/null +++ b/docs/docs.css @@ -0,0 +1,499 @@ +/* + * Documentative Styling + * (c) 2020 dragonwocky + * (https://dragonwocky.me/) under the MIT license + */ +:root { + --primary: #4b85d1; + --absolute: #000; + --contrast: #fff; + --text: rgba(0, 0, 0, 0.84); + --link: var(--primary); + --grey: #6f6f6f; + --bg: #fbfcfc; + --box: #f2f3f4; + --code: #f7f9f9; + --button: #eee; + --border: #e5e7e9; + --shadow: #eee; + --glow: transparent; + --scroll: #e9e9e9; + --hover: #dedede; + --code-lang: #555; + --hljs-html: #000080; + --hljs-attr: #008080; + --hljs-obj: #2c426b; + --hljs-string: #d14; + --hljs-builtin: #0086b3; + --hljs-keyword: rgba(0, 0, 0, 0.84); + --hljs-selector: #900; + --hljs-type: #458; + --hljs-regex: #009926; + --hljs-symbol: #990073; + --hljs-meta: #999; + --hljs-comment: #707070; + --hljs-deletion: #e8b9b8; + --hljs-deletion-text: #4c232d; + --hljs-addition: #b9e0d3; + --hljs-addition-text: #1e4839; +} +@media (prefers-color-scheme: dark) { + :root { + --absolute: #fff; + --contrast: #000; + --text: #ddd; + --link: #a6c3e8; + --grey: #52555c; + --bg: #0e0f0f; + --box: #050505; + --code: #000; + --button: #2d2d2d; + --border: #2d2e2f; + --shadow: #070707; + --glow: var(--primary); + --scroll: #202225; + --hover: #36393f; + --code-lang: #ccc; + --hljs-html: #46db8c; + --hljs-attr: #dd1111; + --hljs-obj: #c6cbda; + --hljs-string: #abcdef; + --hljs-builtin: #b8528d; + /* bd1a79, 926956 */ + --hljs-keyword: #2d8b59; + --hljs-comment: #a0a0a0; + --hljs-deletion: #4c232d; + --hljs-deletion-text: #e8b9b8; + --hljs-addition: #1e4839; + --hljs-addition-text: #b9e0d3; + } +} +* { + box-sizing: border-box; + word-break: break-word; + text-decoration: none; + text-size-adjust: 100%; +} +html, +body { + width: 100%; + height: 100%; + margin: 0; + padding: 0; +} +body { + color: var(--text); + background-color: var(--bg); + font-family: 'Nunito Sans', sans-serif; +} +::-webkit-scrollbar { + width: 2px; + height: 2px; +} +::-webkit-scrollbar-corner, +::-webkit-scrollbar-track { + background-color: transparent; +} +::-webkit-scrollbar-thumb { + background-color: var(--scroll); + border-radius: 5px; +} +::-webkit-scrollbar-thumb:hover { + background: var(--hover); +} +aside { + display: flex; + flex-direction: column; + background-color: var(--box); + overflow-x: auto; +} +aside .title { + display: flex; + flex-direction: row; +} +aside .title h1 { + font: 1.8em 'Source Code Pro', monospace; + margin: 0 0 1em 1.5rem; + padding: 1em 8px 2.5px 0; + letter-spacing: -2px; + border-bottom: 5px solid var(--primary); + color: var(--absolute); +} +aside .title .icon { + margin: auto 0.5em; +} +aside .title .icon img { + width: 2.5em; + margin: auto 0.5em; +} +aside > ul:first-child > li:first-child { + padding-top: 1em; +} +aside ul { + list-style-type: none; + padding-inline-start: 0; + margin: 0; +} +aside ul li p { + font-weight: bold; + letter-spacing: -0.5px; + margin-bottom: 0; + padding: 2px 1.3em; + font-size: 1.1em; + color: var(--hljs-comment); +} +aside ul li a { + color: var(--text); + padding-bottom: 0.1em 5em; + display: block; + padding: 2px 1.5em; +} +aside ul li a:hover, +aside ul li a:active { + background: var(--scroll); +} +aside ul li a.active { + color: var(--link); + font-weight: bold; + text-shadow: 0 0 0.75em var(--glow); +} +aside ul li.entry > a { + text-decoration: underline var(--border); +} +aside ul li.level-1 > a { + padding-left: 1.75em; +} +aside ul li.level-2 > a { + padding-left: calc(1.5em + calc(0.75em * 1)); +} +aside ul li.level-3 > a { + padding-left: calc(1.5em + calc(0.75em * 2)); +} +aside ul li.level-4 > a { + padding-left: calc(1.5em + calc(0.75em * 3)); +} +aside ul li.level-5 > a { + padding-left: calc(1.5em + calc(0.75em * 4)); +} +aside ul li.level-6 > a { + padding-left: calc(1.5em + calc(0.75em * 5)); +} +aside .mark { + text-align: right; + margin-top: auto; + padding: 1.5em 1.5em 2px 1.5em; + font-size: 0.8em; +} +aside .mark a { + color: var(--grey); +} +.wrapper { + height: 100%; + width: 100%; + overflow-y: hidden; +} +.wrapper .documentative { + height: 100%; + overflow-y: auto; + padding: 0 1.5em; + padding-bottom: 4em; + display: flex; + flex-direction: column; +} +.wrapper .documentative .block { + margin: 1.5em; + word-wrap: break-word; +} +.wrapper .documentative .block:first-child { + margin: 0 1.5em 1.5em 1.5em; +} +.wrapper .documentative .example { + margin-top: 1em; + padding: 1em; + background-color: var(--box); + box-shadow: 0.4em 0.4em 1em var(--shadow); +} +.wrapper .documentative .example p:first-child { + margin-top: 0; +} +.wrapper .documentative .example p:last-child { + margin-bottom: 0; +} +.wrapper .documentative nav { + width: 75%; + position: fixed; + bottom: 1em; + right: 0; + pointer-events: none; +} +.wrapper .documentative nav .prev { + float: left; + padding-right: 0.13em; +} +.wrapper .documentative nav .next { + float: right; + padding-left: 0.13em; +} +.wrapper .documentative nav .prev, +.wrapper .documentative nav .next { + opacity: 1; + transition: opacity 200ms ease; + pointer-events: all; + border-radius: 50%; + width: 1.75em; + height: 1.75em; + margin: 0 1em; + font: 1.5em 'Source Code Pro', monospace; + line-height: 1.75em; + text-align: center; + color: var(--text); + text-shadow: none !important; + background-color: var(--button); +} +.wrapper .documentative .footer { + text-align: right; + color: var(--grey); + margin: auto 1.5em 0; +} +.wrapper .documentative .footer hr { + border-color: var(--grey); +} +.wrapper .documentative .footer a { + color: var(--grey); + font-weight: bold; + text-shadow: none; + text-decoration: dotted underline; +} +.wrapper .documentative h1, +.wrapper .documentative h2, +.wrapper .documentative h3, +.wrapper .documentative h4, +.wrapper .documentative h5, +.wrapper .documentative h6 { + margin: 0; + padding-top: 1em; +} +.wrapper .documentative h1 a, +.wrapper .documentative h2 a, +.wrapper .documentative h3 a, +.wrapper .documentative h4 a, +.wrapper .documentative h5 a, +.wrapper .documentative h6 a { + color: var(--text); + text-shadow: none; +} +.wrapper .documentative h1 { + padding-top: 1.5em; +} +.wrapper .documentative a { + color: var(--link); + text-shadow: 0 0 0.75em var(--glow); +} +.wrapper .documentative blockquote { + margin-left: 0; + padding-left: 1em; + border-left: 0.25em solid var(--border); +} +.wrapper .documentative h1 + table, +.wrapper .documentative h2 + table, +.wrapper .documentative h3 + table, +.wrapper .documentative h4 + table, +.wrapper .documentative h5 + table, +.wrapper .documentative h6 + table { + margin-top: 1em; +} +.wrapper .documentative table { + width: 100%; + border-collapse: collapse; +} +.wrapper .documentative table, +.wrapper .documentative th, +.wrapper .documentative td { + padding: 0.2em 0.7em; + border: 1px solid var(--border); +} +.wrapper .documentative code { + padding: 0.5em; + background-color: var(--code); + border-radius: 5px; + overflow-x: auto; + position: relative; + display: block; + font-family: 'Source Code Pro', monospace; +} +.wrapper .documentative *:not(pre) > code { + line-height: 2.5em; + font-size: 0.75em; + display: inline; +} +.wrapper .documentative pre { + position: relative; +} +.wrapper .documentative pre code { + padding: 1.8em; + position: static; + font-size: 0.8em; +} +.wrapper .documentative pre code::before { + position: absolute; + right: 0; + top: 0; + color: var(--code-lang); + font-size: 0.65em; + padding: 0.5em 0.8em; +} +@media (min-width: 769px) { + body { + display: grid; + grid-template-columns: 25% 75%; + } + aside::-webkit-scrollbar-corner, + aside::-webkit-scrollbar-track { + background-color: var(--bg); + } + .toggle { + display: none; + } +} +@media (max-width: 768px) { + aside { + z-index: 1; + height: 100%; + display: flex; + position: fixed; + top: 0; + left: calc(4.5em - 100%); + width: calc(100% - 4.5em); + transition: left 300ms ease; + } + .wrapper { + display: flex; + flex-direction: column; + position: fixed; + top: 0; + left: 0; + transition: left 300ms ease; + } + .wrapper .documentative { + flex-shrink: 1; + } + .wrapper .documentative nav { + width: 100%; + } + .wrapper .toggle { + display: flex; + flex-direction: row; + flex-shrink: 0; + padding: 0.8em 0; + background-color: var(--box); + } + .wrapper .toggle h1 { + letter-spacing: -2px; + font-size: 1.8em; + padding-top: 1.5px; + margin: auto 1.5rem auto 0; + } + .wrapper .toggle button { + font-size: 1.8em; + width: 2.5em; + margin: auto 0.5em; + color: var(--absolute); + border: none; + background: none; + text-align: center; + transition: transform 150ms ease; + -webkit-appearance: none; + -moz-appearance: none; + } + .wrapper .toggle button:hover, + .wrapper .toggle button:focus { + color: var(--text); + } + .wrapper .toggle button:active { + transform: scale(0.95); + } + .mobilemenu aside { + left: 0; + } + .mobilemenu .wrapper { + left: calc(100% - 4.75em); + } + .mobilemenu .wrapper .prev, + .mobilemenu .wrapper .next { + opacity: 0 !important; + pointer-events: none !important; + } +} +.hljs-subst { + color: var(--text); +} +.hljs-comment, +.hljs-quote { + color: var(--hljs-comment); + font-style: italic; +} +.hljs-keyword, +.hljs-selector-tag { + color: var(--hljs-keyword); + font-weight: bold; +} +.hljs-attr { + color: var(--hljs-obj); +} +.hljs-number, +.hljs-literal, +.hljs-variable, +.hljs-template-variable, +.hljs-tag .hljs-attr { + color: var(--hljs-attr); +} +.hljs-string, +.hljs-doctag { + color: var(--hljs-string); +} +.hljs-name, +.hljs-attribute { + color: var(--hljs-html); +} +.hljs-built_in, +.hljs-builtin-name { + color: var(--hljs-builtin); +} +.hljs-title, +.hljs-section, +.hljs-selector-id { + color: var(--hljs-selector); + font-weight: bold; +} +.hljs-type, +.hljs-class .hljs-title { + color: var(--hljs-type); + font-weight: bold; +} +.hljs-regexp, +.hljs-link { + color: var(--hljs-regex); +} +.hljs-symbol, +.hljs-bullet { + color: var(--hljs-symbol); +} +.hljs-meta { + color: var(--hljs-meta); + font-weight: bold; +} +.hljs-deletion { + background: var(--hljs-deletion); + color: var(--hljs-deletion-text); +} +.hljs-addition { + background: var(--hljs-addition); + color: var(--hljs-addition-text); +} +.hljs-emphasis { + font-style: italic; +} +.hljs-strong { + font-weight: bold; +} +.documentative pre .lang-css::before { content: 'CSS'; } \ No newline at end of file diff --git a/docs/docs.js b/docs/docs.js new file mode 100644 index 0000000..48374c4 --- /dev/null +++ b/docs/docs.js @@ -0,0 +1,202 @@ +/* + * Documentative Scripts + * (c) 2020 dragonwocky + * (https://dragonwocky.me/) under the MIT license + */ + +class Scrollnav { + constructor(menu, content, options) { + if (!(menu instanceof HTMLElement)) + throw Error('scrollnav: invalid element provided'); + if (!(content instanceof HTMLElement)) + throw Error('scrollnav: invalid element provided'); + if (typeof options !== 'object') options = {}; + + if (Scrollnav.prototype.INITIATED) + throw Error('scrollnav: only 1 instance per page allowed!'); + Scrollnav.prototype.INITIATED = true; + + this._menu = menu; + this._content = content; + this._sections = [...this._menu.querySelectorAll('ul li a')] + .map(el => { + try { + return this._content.querySelector(el.getAttribute('href')) + .parentElement; + } catch { + return null; + } + }) + .filter(el => el); + this._topheading = this._sections[0].children[0]; + + this._scrolling = []; + this.build(); + } + async build() { + this._content.addEventListener('scroll', this.scrollwatcher.bind(this)); + + window.onhashchange = this.hashwatcher.bind(this); + [...this._menu.querySelectorAll('ul li a')] + .filter(el => el.getAttribute('href').startsWith('#')) + .forEach(el => { + el.onclick = async ev => { + ev.preventDefault(); + this.set(el.getAttribute('href')); + this.scroll(() => { + let offset = this._content.querySelector(el.getAttribute('href')) + .parentElement.offsetTop; + if (offset < this._content.clientHeight / 2) offset = 0; + this._content.scroll({ + top: offset, + behavior: 'smooth' + }); + }); + }; + }); + + this.set(); + this.showmenu(); + await this.scroll(() => { + const ID = location.hash || '#' + this._topheading.id; + try { + this._content.querySelector(ID).parentElement.scrollIntoView(true); + } catch { + location.hash = ''; + } + }); + } + + set(ID) { + if (!ID || typeof ID !== 'string') + ID = location.hash || this._topheading.id; + if (!ID.startsWith('#')) ID = '#' + ID; + if (!this._menu.querySelector(`[href="${ID}"]`)) + ID = '#' + this._topheading.id; + clearTimeout(this.hashloc); + this.hashloc = setTimeout(() => { + this._menu + .querySelectorAll('ul li a') + .forEach(el => + el.getAttribute('href') === ID + ? el.classList.add('active') + : el.classList.remove('active') + ); + if (history.replaceState) { + history.replaceState( + null, + null, + ID === '#' + this._topheading.id ? '#' : ID + ); + if (ID === '#' + this._topheading.id) + this._content.scroll({ + top: 0, + behavior: 'smooth' + }); + } else this._content.querySelector(ID).parentElement.scrollIntoView(true); + }, 100); + } + scroll(func) { + return new Promise((resolve, reject) => { + try { + this._scrolling.push(true); + func(); + setTimeout(() => { + this._scrolling.pop(); + resolve(true); + }, 750); + } catch (err) { + reject(err); + } + }); + } + showmenu(ID) { + if (!ID || typeof ID !== 'string') + ID = location.hash || this._topheading.id; + if (!ID.startsWith('#')) ID = '#' + ID; + if (!this._menu.querySelector(`[href="${ID}"]`)) + ID = '#' + this._topheading.id; + let offset = this._menu.querySelector(`[href="${ID}"]`).parentElement + .offsetTop; + if (offset < this._menu.clientHeight / 2) offset = 0; + clearTimeout(this.menupos); + this._menu.scroll({ + top: offset, + behavior: 'smooth' + }); + } + + hashwatcher(ev) { + ev.preventDefault(); + if (ev.newURL === ev.oldURL) return; + this.set(); + this.showmenu(); + } + + scrollwatcher() { + if (this._scrolling.length) return; + const viewport = this._content.clientHeight, + ID = this._sections.reduce( + (carry, el) => { + const rect = el.getBoundingClientRect(), + height = rect.bottom - rect.top, + visible = { + top: rect.top >= 0 && rect.top < viewport, + bottom: rect.bottom > 0 && rect.top < viewport + }; + let pixels = 0; + if (visible.top && visible.bottom) { + pixels = height; // whole el + } else if (visible.top) { + pixels = viewport - rect.top; + } else if (visible.bottom) { + pixels = rect.bottom; + } else if (height > viewport && rect.top < 0) { + const absolute = Math.abs(rect.top); + if (absolute < height) pixels = height - absolute; // part of el + } + pixels = (pixels / height) * 100; + return pixels > carry[0] ? [pixels, el] : carry; + }, + [0, null] + )[1].children[0].id; + + this.set(ID); + this.showmenu(ID); + } +} + +const construct = () => { + if ( + location.pathname.endsWith('index.html') && + window.location.protocol !== 'file:' + ) + location.replace('./' + location.hash); + + new Scrollnav( + document.querySelector('aside'), + document.querySelector('.documentative') + ); + + document.querySelector('.toggle button').onclick = () => + document.body.classList.toggle('mobilemenu'); + + if (window.matchMedia) { + let prev; + const links = [...document.head.querySelectorAll('link[rel*="icon"]')], + pointer = document.createElement('link'); + pointer.setAttribute('rel', 'icon'); + document.head.appendChild(pointer); + setInterval(() => { + const match = links.find(link => window.matchMedia(link.media).matches); + if (!match || match.media === prev) return; + prev = match.media; + pointer.setAttribute('href', match.getAttribute('href')); + }, 500); + links.forEach(link => document.head.removeChild(link)); + } +}; + +if (document.readyState === 'complete') { + construct(); +} else document.addEventListener('DOMContentLoaded', construct); diff --git a/docs/index.html b/docs/index.html new file mode 100644 index 0000000..14a4a4c --- /dev/null +++ b/docs/index.html @@ -0,0 +1,267 @@ +notion enhancer

notion enhancer

+ +
+

+ notion enhancer +

+

an enhancer/customiser for the all-in-one productivity workspace notion.so

+ +
+
+

+ installation +

+

currently, only win10 is supported. it is possible to run this script via the wsl to modify the win10 notion app.

+

(the styles should also work for the web version. +these can be installed via an extension like stylus +or a built-in feature like userChrome.css.)

+
    +
  1. install node.js (if using the wsl, it is recommended to install via nvm.)
  2. +
  3. install python (if using the wsl, follow this guide.)
  4. +
  5. reboot.
  6. +
  7. in cmd (on windows) or bash (with wsl), run npm install -g asar (check installation by running asar).
  8. +
  9. download + extract this enhancer to a location it can safely remain (this must be in the windows filesystem, +even if you are running the script from the wsl).
  10. +
  11. ensure notion is closed.
  12. +
  13. optional: to remove previous versions of notion enhancer, run cleaner.py
  14. +
  15. optional: modify the resources/user.css files to your liking.
  16. +
  17. run customiser.py to build changes.
  18. +
+

done: run notion and enjoy

+

oh no, now my app won't open!

+
    +
  1. kill any notion tasks in the task manager (ctrl+shift+esc).
  2. +
  3. run cleaner.py.
  4. +
  5. reboot.
  6. +
  7. follow instructions above (ensuring notion isn't running! again, check task manager).
  8. +
+ +
+
+

+ this is a fork +

+

credit where credit is due, this was originally made by Uzver (github: @TarasokUA, +telegram: UserFromUkraine, discord: Uzver#8760).

+

he has approved my go-ahead with this fork, as he himself no longer wishes to continue development on the project.

+ +
+
+

+ features +

+ +
+
+

+ titlebar +

+

default windows titlebar/frame has been replaced by one more fitting to the theme of the app.

+

this includes the addition of an extra button, "always on top" +symbolised with an arrow (4th from the right). when toggled to point up, +notion will remain the top visible window even if not focused.

+

to customise which characters are used for these buttons, open in the resources/preload.js file, +find the relevant button (read the comments) and replace its icon with your chosen unicode character (e.g. +replacing element.innerHTML = '▢'; -> element.innerHTML = '🙄';).

+ +
+
+

+ nicer scrollbars +

+

i mean, yeah. get rid of those ugly default scrollbars and use nice inconspicuous +ones that actually look as if they're part of notion.

+

to add these to the web version, copy lines 43 - 87 from user.css into your css customiser.

+ +
+
+

+ hotkey +

+

by default, ctrl+shift+a (will hide/show all notion windows to/from the tray).

+

to set your own, open customiser.py and change line 16 (hotkey = 'ctrl+shift+a') +to your preference. you will need to run or re-run customiser.py afterwards.

+ +
+
+

+ tray +

+
    +
  • single-click to toggle app visibility. right click to open menu.
  • +
  • settings will be saved in %localappdata%/Programs/Notion/resources/app/user-preferences.json
  • +
  • run on startup: run notion on boot/startup. (default: true)
  • +
  • hide on open: hide the launch of notion to the tray. (default: false)
  • +
  • open maximised: maximise the app on open. (default: false)
  • +
  • close to tray: app will close to the tray when the button is pressed rather than closing outright. (default: false)
  • +
+ +
+
+

+ styling +

+

due to customiser.py setting up a direct link to resources/user.css, +changes will be applied instantly on notion reload +(no need to re-run customiser.py every time you want to change some styles).

+

these should also work for the web version, if copied into your css customiser.

+

css below will work for every instance of the element, but if you wish to hide only a specific element +(e.g. the '+ new' table row) it is recommended that you prepend each selector with [data-block-id='ID'] (video tutorial on fetching IDs).

+ +
+
+

+ wider page view +

+
.notion-peek-renderer > div:nth-child(2) {
+  max-width: 85vw !important;
+}
+ +
+
+

+ thinner cover image +

+
[style^='position: relative; width: 100%; display: flex; flex-direction: column; align-items: center; height: 30vh;'] {
+  height: 12vh !important;
+}
+[style^='position: relative; width: 100%; display: flex; flex-direction: column; align-items: center; height: 30vh;']
+  img {
+  height: 20vh !important;
+}
+ +
+
+

+ table columns below 100px +

+

not recommended! this is unreliable and will cause bugs. +coincidentally, this is also what the youtube video linked above shows how to do. +as it is a per-table-column style, unlike all others here, it must be prepended with the block ID.

+
[data-block-id^='ID']
+  > [style^='display: flex; position: absolute; background: rgb(47, 52, 55); z-index: 82; height: 33px; color: rgba(255, 255, 255, 0.6);']
+  > div:nth-child(1)
+  > div:nth-child(10)
+  > div:nth-child(1),
+[data-block-id^='ID']
+  > [style^='position: relative; min-width: calc(100% - 192px);']
+  > [data-block-id]
+  > div:nth-child(10),
+[data-block-id^='ID'] > div:nth-child(5) > div:nth-child(10) {
+  width: 45px !important;
+}
+[data-block-id^='ID']
+  [style^='position: absolute; top: 0px; left: 0px; pointer-events: none;']:not(.notion-presence-container) {
+  display: none;
+}
+ +
+
+

+ hide '+ new' table row +

+
.notion-table-view-add-row {
+  display: none !important;
+}
+ +
+
+

+ hide calculations table row +

+
[] .notion-table-view-add-row + div {
+  display: none !important;
+}
+ +
+
+

+ hide '+ new' board row +

+
.notion-board-group
+  [style='user-select: none; transition: background 120ms ease-in 0s; cursor: pointer; display: inline-flex; align-items: center; flex-shrink: 0; white-space: nowrap; height: 32px; border-radius: 3px; font-size: 14px; line-height: 1.2; min-width: 0px; padding-left: 6px; padding-right: 8px; color: rgba(255, 255, 255, 0.4); width: 100%;'] {
+  display: none !important;
+}
+ +
+
+

+ hide board view hidden columns +

+
.notion-board-view > [data-block-id] > div:nth-last-child(2),
+.notion-board-view > [data-block-id] > div:first-child > div:nth-last-child(2) {
+  display: none !important;
+}
+ +
+
+

+ hide board view 'add a group' +

+
.notion-board-view > [data-block-id] > div:last-child,
+.notion-board-view > [data-block-id] > div:first-child > div:last-child {
+  display: none !important;
+}
+ +
+
+

+ centre-align table column headers +

+
.notion-table-view-header-cell > div > div {
+  margin: 0px auto;
+}
+ +
+
+

+ smaller table column header icons +

+
[style^='display: flex; position: absolute; background: rgb(47, 52, 55); z-index: 82; height: 33px; color: rgba(255, 255, 255, 0.6);']
+  div:nth-child(1)
+  svg {
+  height: 10px !important;
+  width: 10px !important;
+  margin-right: -4px;
+}
+ +
+
+

+ remove icons from table column headers +

+
.notion-table-view-header-cell [style^='margin-right: 6px;'] {
+  display: none !important;
+}
+ +
+
+

+ removing/decreasing side padding for tables +

+
[style^='flex-shrink: 0; flex-grow: 1; width: 100%; max-width: 100%; display: flex; align-items: center; flex-direction: column; font-size: 16px; color: rgba(255, 255, 255, 0.9); padding: 0px 96px 30vh;']
+  .notion-table-view,
+[class='notion-scroller'] > .notion-table-view {
+  padding-left: 35px !important;
+  padding-right: 15px !important;
+  min-width: 0% !important;
+}
+[style^='flex-shrink: 0; flex-grow: 1; width: 100%; max-width: 100%; display: flex; align-items: center; flex-direction: column; font-size: 16px; color: rgba(255, 255, 255, 0.9); padding: 0px 96px 30vh;']
+  .notion-selectable
+  .notion-scroller.horizontal::-webkit-scrollbar-track {
+  margin-left: 10px;
+  margin-right: 10px;
+}
+ +
+
+

+ removing/decreasing side padding for boards +

+
.notion-board-view {
+  padding-left: 10px !important;
+  padding-right: 10px !important;
+}
+ +
\ No newline at end of file diff --git a/Notion Customization/resources/icon.ico b/docs/resources/notion.ico similarity index 100% rename from Notion Customization/resources/icon.ico rename to docs/resources/notion.ico diff --git a/resources/hotkey.js b/resources/hotkey.js new file mode 100644 index 0000000..c7ba941 --- /dev/null +++ b/resources/hotkey.js @@ -0,0 +1,109 @@ +/* === INJECTION MARKER === */ + +/* + * Notion Enhancer + * (c) 2020 dragonwocky + * (c) 2020 TarasokUA + * (https://dragonwocky.me/) under the MIT license + */ + +// adds: tray support (inc. context menu with settings), window toggle hotkey + +// DO NOT REMOVE THE INJECTION MARKER ABOVE. +// DO NOT CHANGE THE NAME OF THE 'enhancements()' FUNCTION. + +let tray; + +function enhancements() { + const { Tray, Menu } = require('electron'), + path = require('path'), + store = new (require(path.join(__dirname, '..', 'store.js')))({ + config: 'user-preferences', + defaults: { + openhidden: false, + maximised: false, + tray: false + } + }), + states = { + startup: electron_1.app.getLoginItemSettings().openAtLogin, + openhidden: store.get('openhidden'), + maximised: store.get('maximised'), + tray: store.get('tray') + }; + + tray = new Tray(path.join(__dirname, './notion.ico')); + const contextMenu = Menu.buildFromTemplate([ + { + id: 'startup', + label: 'run on startup', + type: 'checkbox', + checked: states.startup, + click: () => + contextMenu.getMenuItemById('startup').checked + ? electron_1.app.setLoginItemSettings({ openAtLogin: true }) + : electron_1.app.setLoginItemSettings({ openAtLogin: false }) + }, + { + id: 'openhidden', + label: 'hide on open', + type: 'checkbox', + checked: states.openhidden, + click: () => + contextMenu.getMenuItemById('openhidden').checked + ? store.set('openhidden', true) + : store.set('openhidden', false) + }, + { + id: 'maximised', + label: 'open maximised', + type: 'checkbox', + checked: states.maximised, + click: () => + contextMenu.getMenuItemById('maximised').checked + ? store.set('maximised', true) + : store.set('maximised', false) + }, + { + id: 'tray', + label: 'close to tray', + type: 'checkbox', + checked: states.tray, + click: () => + contextMenu.getMenuItemById('tray').checked + ? store.set('tray', true) + : store.set('tray', false) + }, + { + type: 'separator' + }, + { + label: '(x) quit', + role: 'quit' + } + ]); + tray.setContextMenu(contextMenu); + + tray.on('click', function () { + const win = electron_1.BrowserWindow.getAllWindows()[0]; + if (win.isVisible()) { + if (win.isMinimized()) { + win.show(); + } else win.hide(); + } else { + if (contextMenu.getMenuItemById('maximised').checked) { + win.maximize(); + } else win.show(); + } + }); + + const hotkey = '$$$hotkey$$$'; // will be set by python script + electron_1.globalShortcut.register(hotkey, () => { + const windows = electron_1.BrowserWindow.getAllWindows(); + if (windows.some(win => !win.isVisible())) { + if (contextMenu.getMenuItemById('maximised').checked) { + windows.forEach(win => win.maximize()); + } else windows.forEach(win => win.show()); + } else windows.forEach(win => win.hide()); + }); +} diff --git a/resources/notion.ico b/resources/notion.ico new file mode 100644 index 0000000..b46f68c Binary files /dev/null and b/resources/notion.ico differ diff --git a/resources/preload.js b/resources/preload.js new file mode 100644 index 0000000..4ca1503 --- /dev/null +++ b/resources/preload.js @@ -0,0 +1,86 @@ +/* === INJECTION MARKER === */ + +/* + * Notion Enhancer + * (c) 2020 dragonwocky + * (c) 2020 TarasokUA + * (https://dragonwocky.me/) under the MIT license + */ + +// adds: custom styles, nicer window control buttons + +// DO NOT REMOVE THE INJECTION MARKER ABOVE + +require('electron').remote.getGlobal('setTimeout')(() => { + /* style injection */ + const fs = require('fs'), + css = fs.readFileSync('$$$user.css$$$'), // will be set by python script + style = document.createElement('style'), + head = document.getElementsByTagName('head')[0]; + if (!head) return; + style.type = 'text/css'; + style.innerHTML = css; + head.appendChild(style); + + /* window control buttons */ + const intervalID = setInterval(insertbuttons, 100); + function insertbuttons() { + if (document.querySelector('div.notion-topbar > div') == undefined) return; + + const appwindow = require('electron').remote.getCurrentWindow(); + let node = document.querySelector('div.notion-topbar > div'), + element = document.createElement('div'); + element.id = 'window-buttons-area'; + node.appendChild(element); + node = document.querySelector('#window-buttons-area'); + + // always-on-top + element = document.createElement('button'); + element.classList.add('window-buttons'); + element.innerHTML = '🠛'; + element.onclick = function () { + const state = appwindow.isAlwaysOnTop(); + appwindow.setAlwaysOnTop(!state); + this.innerHTML = state ? '🠛' : '🠙'; + }; + node.appendChild(element); + + // minimise + element = document.createElement('button'); + element.classList.add('window-buttons'); + element.innerHTML = '⚊'; + element.onclick = () => appwindow.minimize(); + node.appendChild(element); + + // maximise + element = document.createElement('button'); + element.classList.add('window-buttons'); + element.innerHTML = '▢'; + element.onclick = () => + appwindow.isMaximized() ? appwindow.unmaximize() : appwindow.maximize(); + node.appendChild(element); + + // close + const path = require('path'); + element = document.createElement('button'); + element.classList.add('window-buttons'); + element.innerHTML = '⨉'; + element.onclick = () => { + const store = new (require(path.join(__dirname, '..', 'store.js')))({ + config: 'user-preferences', + defaults: { + tray: false + } + }); + if ( + store.get('tray') && + require('electron').remote.BrowserWindow.getAllWindows().length === 1 + ) { + appwindow.hide(); + } else appwindow.close(); + }; + node.appendChild(element); + + clearInterval(intervalID); + } +}, 100); diff --git a/resources/store.js b/resources/store.js new file mode 100644 index 0000000..b2238c4 --- /dev/null +++ b/resources/store.js @@ -0,0 +1,35 @@ +/* + * Notion Enhancer + * (c) 2020 dragonwocky + * (c) 2020 TarasokUA + * (https://dragonwocky.me/) under the MIT license + */ + +// a wrapper for accessing data stored in a JSON file + +const path = require('path'), + fs = require('fs'); + +class Store { + constructor(opts) { + this.path = path.join(__dirname, opts.config + '.json'); + this.data = parseDataFile(this.path, opts.defaults); + } + get(key) { + return this.data[key]; + } + set(key, val) { + this.data[key] = val; + fs.writeFileSync(this.path, JSON.stringify(this.data)); + } +} + +function parseDataFile(path, defaults) { + try { + return JSON.parse(fs.readFileSync(path)); + } catch (error) { + return defaults; + } +} + +module.exports = Store; diff --git a/resources/user.css b/resources/user.css new file mode 100644 index 0000000..770c458 --- /dev/null +++ b/resources/user.css @@ -0,0 +1,87 @@ +/* + * Notion Enhancer + * (c) 2020 dragonwocky + * (c) 2020 TarasokUA + * (https://dragonwocky.me/) under the MIT license + */ + +/* window control buttons: block */ +#window-buttons-area { + padding-left: 14px; +} +/* window control buttons: light theme */ +.notion-light-theme .window-buttons { + background: rgb(255, 255, 255); + color: black; + border: 0; + margin: 0px 0px 0px 9px; + width: 32px; + line-height: 26px; + border-radius: 4px; + font-size: 16px; + transition-duration: 0.2s; + font-weight: bold; +} +.notion-light-theme .window-buttons:hover { + background: rgb(239, 239, 239); +} +/* window control buttons: dark theme */ +.notion-dark-theme .window-buttons { + background: rgb(47, 52, 55); + border: 0; + margin: 0px 0px 0px 9px; + width: 32px; + line-height: 26px; + border-radius: 4px; + font-size: 16px; + transition-duration: 0.2s; +} +.notion-dark-theme .window-buttons:hover { + background: rgb(71, 76, 80); +} + +/* scrollbar: pointer */ +.notion-scroller { + cursor: auto; +} +/* scrollbar: size */ +::-webkit-scrollbar { + width: 8px; /* for vertical */ + height: 8px; /* for horizontal */ +} +/* scrollbar: light theme */ +.notion-light-theme ::-webkit-scrollbar-corner { + background-color: #afafaf; /* for overlap */ +} +.notion-light-theme ::-webkit-scrollbar-thumb { + background-color: #afafaf; +} +.notion-light-theme ::-webkit-scrollbar-track { + background-color: #e4e4e4; +} +.notion-light-theme ::-webkit-scrollbar-thumb:hover { + background: #969696; +} +/* scrollbar: dark theme */ +.notion-dark-theme ::-webkit-scrollbar-corner { + background-color: #3d3d42; /* for overlap */ +} +.notion-dark-theme ::-webkit-scrollbar-track { + background-color: #3d3d42; +} +.notion-dark-theme ::-webkit-scrollbar-thumb { + border-radius: 5px; + background-color: #5d5d5d; +} +.notion-dark-theme ::-webkit-scrollbar-thumb:hover { + background: #868686; +} +/* rounded borders */ +.notion-dark-theme ::-webkit-scrollbar-track:vertical, +.notion-light-theme ::-webkit-scrollbar-track:vertical { + border-radius: 5px 5px 0 0; +} +.notion-dark-theme ::-webkit-scrollbar-track:horizontal, +.notion-light-theme ::-webkit-scrollbar-track:horizontal { + border-radius: 5px 0 0 5px; +}