v0.11.0
							
								
								
									
										20
									
								
								extension/.github/workflows/submodules.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,20 @@ | ||||
| name: 'update submodules' | ||||
| 
 | ||||
| on: | ||||
|   workflow_dispatch: | ||||
| 
 | ||||
| jobs: | ||||
|   sync: | ||||
|     name: 'update submodules' | ||||
|     runs-on: ubuntu-latest | ||||
|     steps: | ||||
|       - name: checkout repo | ||||
|         uses: actions/checkout@v2 | ||||
|         with: | ||||
|           submodules: true | ||||
|       - name: pull updates | ||||
|         run: | | ||||
|           git pull --recurse-submodules | ||||
|           git submodule update --remote --recursive | ||||
|       - name: commit changes | ||||
|         uses: stefanzweifel/git-auto-commit-action@v4 | ||||
							
								
								
									
										13
									
								
								extension/.gitmodules
									
									
									
									
										vendored
									
									
								
							
							
						
						| @ -1,3 +1,16 @@ | ||||
| [submodule "api"] | ||||
| 	path = api | ||||
| 	url = git@github.com:notion-enhancer/api.git | ||||
| 	branch = dev | ||||
| [submodule "repo"] | ||||
| 	path = repo | ||||
| 	url = git@github.com:notion-enhancer/repo.git | ||||
| 	branch = dev | ||||
| [submodule "media"] | ||||
| 	path = media | ||||
| 	url = git@github.com:notion-enhancer/media.git | ||||
| 	branch = main | ||||
| [submodule "dep"] | ||||
| 	path = dep | ||||
| 	url = git@github.com:notion-enhancer/dep.git | ||||
| 	branch = main | ||||
|  | ||||
| @ -1,361 +0,0 @@ | ||||
| # changelog | ||||
| 
 | ||||
| ### v0.11.0 (dev) | ||||
| 
 | ||||
| a complete rework of the enhancer including a port to the browser as a chrome extension. | ||||
| 
 | ||||
| - new: cross-environment and properly documented api to replace helpers. | ||||
| - new: cross-environment mod loader structure. | ||||
| - new: notifications sourced from an online endpoint for sending global user alerts. | ||||
| - new: simplify user installs by depending on the chrome web store and [notion-repackaged](https://github.com/notion-enhancer/notion-repackaged). | ||||
| - new: separate menu profiles for mod configurations. | ||||
| - new: a hotkey option type that allows typing in/pressing a hotkey to enter it, instead of typing. | ||||
| - improved: split the core mod into separate mods for specific features. | ||||
| - improved: theming variables that are more specific, less laggy, and less complicated. | ||||
| - improved: merged bracketed-links into tweaks. | ||||
| - improved: a redesigned menu with nicer ui, separate categories for mods and a sidebar for configuration. | ||||
| - removed: integrated scrollbar tweak (notion now includes by default). | ||||
| - removed: js insert. css insert moved to tweaks mod. | ||||
| - removed: majority of layout and font size variables - better to leave former to notion and use `ctrl +` for latter. | ||||
| - bugfix: bypass csp restrictions. | ||||
| 
 | ||||
| **below this point the enhancer was desktop-only. in v0.11.0 it was been ported to also** | ||||
| **run as a chrome extension. changes made to both are indicated above.** | ||||
| 
 | ||||
| ### v0.10.2 (2020-12-05) | ||||
| 
 | ||||
| again, an emergency release for bugfixes. | ||||
| not properly documented and new features have not yet been fully reviewed/edited. | ||||
| 
 | ||||
| - new: side panel - adds an extra sidebar on the right for use by other mods, | ||||
|   toggleable with `ctrl+shift+backslash`. | ||||
| - improved: notion icons uses spritesheets for faster loading of icons. | ||||
| - improved: icon sets can be hidden/toggled. | ||||
| - improved: toggles in the enhancer menu follow the same style as notion's toggles. | ||||
| - improved: separate quote font variable & option in the font chooser mod (`--theme_[dark|light]--font_quote`). | ||||
| - improved: option to hide the "page details" text for the word counter extension. | ||||
| - bugfix: notion icons tab is now visible in fullpage databases. | ||||
| - bugfix: code line numbers handles wrapped code blocks. | ||||
| - bugfix: file explorer no longer opens when enhancer menu is opened. | ||||
| - bugfix: enable the remote module in webviews (windows/tabs) for compatibility with the | ||||
|   updated version of electron used by new notion builds (>= 2.0.10). | ||||
| - bugfix: add support for enhancing an `app` folder if there is no `app.asar` file present. | ||||
| - extension: "outliner" = table of contents in right sidebar. | ||||
| - extension: "panel sites" = embed sites on the site panel. | ||||
| - extension: "indentation lines" = adds vertical relationship lines to make list trees easier to follow. | ||||
| - extension: "truncated table titles" = see the full text of the truncated table titles on hover over. | ||||
| 
 | ||||
| > 📥 `npm i -g notion-enhancer@0.10.2` | ||||
| 
 | ||||
| ### v0.10.1 (2020-11-18) | ||||
| 
 | ||||
| essentially a prerelease for v0.11.0: pushed out for urgent bugfixes during | ||||
| exam/study weeks when there's no time to code a full release. | ||||
| 
 | ||||
| note that this means new features have not yet been fully documented and | ||||
| may not be fully ready for ideal use yet. however, things overall will | ||||
| work more reliably than v0.10.0. | ||||
| 
 | ||||
| - new: different css entrypoints for different components (tabs, menu, app). | ||||
| - improved: use an svg for the scroll-to-top button. | ||||
| - improved: use a better-matching icon and add transitions to the property layout toggle. | ||||
| - improved: themes are directly applied to tabs and menu rather than sync-ed between (infinite loading). | ||||
| - improved: error message "is notion running?" --> clearer "make sure notion isn't running!" | ||||
| - improved: auto-shrink system for tabs (max of 15 open in a window). | ||||
| - bugfix: disable fadein of selected block halo with snappy transitions. | ||||
| - bugfix: increase contrast of `--theme_dark--interactive_hover` in dark+ and dracula. | ||||
| - bugfix: tabs are focused properly for input. | ||||
| - bugfix: keyboard shortcut listeners are stricter so they don't conflict. | ||||
| - bugfix: dots indicating draggability are no longer next to the tabs mod in the menu. | ||||
| - bugfix: prevent empty hotkeys from triggering every keypress. | ||||
| - bugfix: don't try loading an empty default page url (infinite loading). | ||||
| - bugfix: remove `* { z-index: 1}` rule so format dropdowns in table view can be opened. | ||||
| - extension: "topbar icons" = replaces the topbar buttons with icons. | ||||
| - extension: "code line numbers" = adds line numbers to code blocks. | ||||
| - extension: "notion icons" = use custom icon sets directly in notion. | ||||
| - tweak: vertical indentation/relationship lines for lists. | ||||
| - tweak: scroll database toolbars horizontally if partially hidden. | ||||
| - tweak: condense bullet points (decrease line spacing). | ||||
| 
 | ||||
| > 📥 `npm i -g notion-enhancer@0.10.1` | ||||
| 
 | ||||
| ### v0.10.0 (2020-11-02) | ||||
| 
 | ||||
| a flexibility update. | ||||
| 
 | ||||
| - new: mods can be reordered in the menu to control what order styling/scripts are added/executed in. | ||||
|   higher up on the list = higher priority of application = loaded last in order to override others. | ||||
|   (excluding the core, which though pinned to the top of the list is always loaded first so theming | ||||
|   variables can be modified.) | ||||
| - new: relaunch button in tray menu. | ||||
| - new: a core mod option for a default page id/url (all new windows will load it instead of the | ||||
|   normal "most recent" page). | ||||
| - new: css variables for increasing line spacing/paragraph margins. | ||||
| - new: patch the notion:// url scheme/protocol to work on linux. | ||||
| - new: menu shows theme conflicts + a core mod option to auto-resolve theme conflicts. | ||||
| - new: a `-n` cli option. | ||||
| - improved: menu will now respect integrated titlebar setting. | ||||
| - improved: use keyup listeners instead of a globalShortcut for the enhancements menu toggle. | ||||
| - improved: overwrite `app.asar.bak` if already exists (e.g. for app updates). | ||||
| - improved: additional menu option descriptions on hover. | ||||
| - improved: listen to prefers-color-scheme to better change theme in night shift. | ||||
| - improved: platform-specific option overrides for features not required on macOS. | ||||
| - improved: made extra padding at the bottom with the "focus mode" extension toggleable. | ||||
| - bugfix: removed messenger emoji set as the provider no longer supports it. | ||||
| - bugfix: remove shadow around light mode board headers. | ||||
| - bugfix: properly detect/respond to `EACCES`/`EBUSY` errors. | ||||
| - bugfix: night shift checks every interaction, | ||||
|   will respond to system changes without any manual changes. | ||||
| - bugfix: toc blocks can have text colours. | ||||
| - bugfix: bypass preview extension works with the back/forward keyboard shortcuts. | ||||
| - bugfix: (maybe) fix csp issues under proxy. | ||||
| - bugfix: remove focus mode footer from neutral theme + better contrast in calendar views. | ||||
| - bugfix: improvements to the colour theming, particularly to make real- and fake-light/dark | ||||
|   modes (as applied by the night shift extension) look consistent. | ||||
|   relevant variables (assuming all are prefixed by `--theme_[dark|light]--`): | ||||
|   `box-shadow`, `box-shadow_strong`, `select_input`, and `ui-border` | ||||
| - bugfix: font sizing applied to overlays/previews. | ||||
| - bugfix: removed typo in variable name for brown text. | ||||
| - bugfix: primary-colour text (mainly in "add a \_" popups) is now properly themed. | ||||
| - bugfix: right-to-left extension applies to text in columns. | ||||
| - bugfix: block text colour applies to text with backgrounds. | ||||
| - bugfix: font applied to wrong mode with littlepig dark. | ||||
| - bugfix: keep "empty" top bar visible in the menu. | ||||
| - bugfix: set NSRequiresAquaSystemAppearance to false in /Applications/Notion.app/Contents/Info.plist | ||||
|   so system dark/light mode can be properly detected. | ||||
| - bugfix: make ctrl+f popover shadow less extreme. | ||||
| - bugfix: "weekly" calendar view name made case insensitive. | ||||
| - bugfix: re-show hidden windows when clicking on the dock. | ||||
| - tweak: sticky table/list rows. | ||||
| - theme: "material ocean" = an oceanic colour palette. | ||||
| - theme: "cherry cola" = a delightfully plummy, cherry cola flavored theme. | ||||
| - theme: "dracula" = a theme based on the popular dracula color palette | ||||
|   originally by zeno rocha and friends. | ||||
| - extension: "tabs" = have multiple notion pages open in a single window. tabs can be controlled | ||||
|   with keyboard shortcuts and dragged/reordered within/between windows. | ||||
| - extension: "scroll to top" = add an arrow above the help button to scroll back to the top of a page. | ||||
| - extension: "tweaks" = common style/layout changes. includes: | ||||
|   - new: make transitions snappy/0s. | ||||
|   - new: in-page columns are disabled/wrapped and pages are wider when | ||||
|     the window is narrower than 600px for improved responsiveness. | ||||
|   - new: thicker bold text for better visibility. | ||||
|   - new: more readable line spacing. | ||||
|   - moved: smooth scrollbars. | ||||
|   - moved: change dragarea height. | ||||
|   - moved: hide help. | ||||
| 
 | ||||
| a fork of notion-deb-builder that does generate an app.asar has been created and is once again supported. | ||||
| 
 | ||||
| > 📥 `npm i -g notion-enhancer@0.10.0` | ||||
| 
 | ||||
| ### v0.9.1 (2020-09-26) | ||||
| 
 | ||||
| - bugfix: font chooser will continue iterating through fonts after encountering a blank option. | ||||
| - bugfix: block indents are no longer overriden. | ||||
| - bugfix: neutral does not force full width pages. | ||||
| - bugfix: bypass preview extension works with the back/forward arrows. | ||||
| - bugfix: check all views on a page for a weekly calendar. | ||||
| - bugfix: emoji sets no longer modifies the user agent = doesn't break hotkeys. | ||||
| 
 | ||||
| > 📥 `npm i -g notion-enhancer@0.9.1` | ||||
| 
 | ||||
| ### v0.9.0 (2020-09-20) | ||||
| 
 | ||||
| a feature and cleanup update. | ||||
| 
 | ||||
| - improved: halved the number of css rules used -> much better performance. | ||||
| - improved: font imports must be define in the `mod.js` so that they can also be used in | ||||
|   the enhancements menu. | ||||
| - improved: tiling window-manager support (can hide titlebars entirely without dragarea/buttons). | ||||
| - improved: extensions menu search is now case insensitive and includes options, inputs and versions. | ||||
|   the search box can also for focused with `CMD/CTRL+F`. | ||||
| - improved: extensions menu filters shown either a ✓ or × to help understand the current state. | ||||
| - improved: added individual text-colour rules for different background colours. | ||||
| - improved: added variables for callout colouring. | ||||
| - improved: replaced with `helpers.getNotion()` with the constant `helpers.__notion` to reduce | ||||
|   repeated function calls. | ||||
| - improved: added variables for page width. | ||||
| - improved/bugfix: emoji sets extension should now work on macOS and will change user agent to use | ||||
|   real emojis instead of downloading images when system default is selected. | ||||
| - bugfix: enhancer settings should no longer reset on update (though this will not have | ||||
|   effect until the release after this one). | ||||
| - bugfix: blue select tags are no longer purple. | ||||
| - bugfix: page titles now respond to small-text mode. | ||||
| - bugfix: weekly calendar view height is now sized correctly according to its contents. | ||||
| - bugfix: made the open enhancements menu hotkey configurable and changed the default to `ALT+E`. | ||||
|   to remove conflict with the inline code highlight shortcut. | ||||
| - bugfix: update property-layout to match notion changes again. | ||||
| - bugfix: updated some of the tweak styling to match notion changes. | ||||
| - bugfix: block-level text colours are now changed properly. | ||||
| - bugfix: do not require data folder during installation, to prevent `sudo` attempting to | ||||
|   create it in `/var/root/`. | ||||
| - bugfix: bullet points/checkboxes will now align properly in the right-to-left extension. | ||||
| - themes: "littlepig" (light + dark) = monospaced themes using emojis and colourful text. | ||||
| - extension: "font chooser" = customize fonts. for each option, type in the name of the font you would like to use, | ||||
|   or leave it blank to not change anything. | ||||
| - extension: "always on top" = add an arrow/button to show the notion window on top of other windows | ||||
|   even if it's not focused. | ||||
| - extension: "calendar scroll" = add a button to scroll down to the current week in fullpage/infinite-scroll calendars. | ||||
| - extension: "hide help button" = hide the help button if you don't need it. | ||||
| - extension: "bypass preview" = go straight to the normal full view when opening a page. | ||||
| - extension: "word counter" = add page details: word/character/sentence/block count & speaking/reading times. | ||||
| 
 | ||||
| notion-deb-builder has been discovered to not generate an app.asar and so is no longer supported. | ||||
| 
 | ||||
| > 📥 `npm i -g notion-enhancer@0.9.0` | ||||
| 
 | ||||
| ### v0.8.5 (2020-08-29) | ||||
| 
 | ||||
| - bugfix: separate text highlight and select tag variables. | ||||
| - bugfix: bypass CSP for the `enhancement://` protocol - was failing on some platforms? | ||||
| 
 | ||||
| > 📥 `npm i -g notion-enhancer@0.8.5` | ||||
| 
 | ||||
| ### v0.8.4 (2020-08-29) | ||||
| 
 | ||||
| - bugfix: property-layout now works consistently with or without a banner. | ||||
| 
 | ||||
| > 📥 `npm i -g notion-enhancer@0.8.4` | ||||
| 
 | ||||
| ### v0.8.3 (2020-08-29) | ||||
| 
 | ||||
| previous release was a mistake: it did as intended on linux, but broke windows. | ||||
| this should achieve the same thing in a more compatible way. | ||||
| 
 | ||||
| > 📥 `npm i -g notion-enhancer@0.8.3` | ||||
| 
 | ||||
| ### v0.8.2 (2020-08-28) | ||||
| 
 | ||||
| some things you just can't test until production... fixed the auto-installer | ||||
| to use `./bin.js` instead of `notion-enhancer` | ||||
| 
 | ||||
| > 📥 `npm i -g notion-enhancer@0.8.2` | ||||
| 
 | ||||
| ### v0.8.1 (2020-08-28) | ||||
| 
 | ||||
| a clarity and stability update. | ||||
| 
 | ||||
| - improved: more informative cli error messages (original ones can be accessed with the `-d/--dev` flag). | ||||
| - bugfix: gallery variable didn't apply on fullpage. | ||||
| - bugfix: date picker hid current date number. | ||||
| - bugfix: small-text pages should now work as expected. | ||||
| - bugfix: padding issues in page previews. | ||||
| - bugfix: property-layout extension had been broken by internal notion changes. | ||||
| - bugfix: linux installer path typo. | ||||
| - bugfix: caret-color was being mistaken for color and block-level text colouring was broken. | ||||
| - improved: auto-application on install. | ||||
| 
 | ||||
| > 📥 `npm i -g notion-enhancer@0.8.1` | ||||
| 
 | ||||
| ### v0.8.0 (2020-08-27) | ||||
| 
 | ||||
| complete rewrite with node.js. | ||||
| 
 | ||||
| - new: simpler cli installation system (inc. commands: `apply`, `remove`, and `check`). | ||||
| - new: mod loading system (easier to create new mods, adds to notion rather than overwriting). | ||||
| - new: mod configuration menu. | ||||
| - improved: more theming variable coverage - inc. light theme and sizing/spacing. | ||||
| - bugfix: non-reproducable errors with python. | ||||
| - bugfix: better launcher patching on linux. | ||||
| - bugfix: fix frameless window issue introduced by notion desktop 2.0.9. | ||||
| - extension: "custom inserts" = link files for small client-side tweaks. | ||||
| - extension: "bracketed links" = render links surrounded with \[\[brackets]] instead of underlined. | ||||
| - extension: "focus mode" = hide the titlebar/menubar if the sidebar is closed (will be shown on hover). | ||||
| - theme: "dark+" = a vivid-colour near-black theme. | ||||
| - theme: "neutral" = smoother colours and fonts, designed to be more pleasing to the eye. | ||||
| - theme: "gameish" = a purple, "gamer-styled" theme with a blocky-font. | ||||
| - theme: "pastel dark" = a smooth-transition true dark theme with a hint of pastel. | ||||
| - extension: "emoji sets" = pick from a variety of emoji styles to use. | ||||
| - extension: "night shift" = sync dark/light theme with the system (overrides normal theme setting). | ||||
| - extension: "right-to-left" = enables auto rtl/ltr text direction detection. (ported from [github.com/obahareth/notion-rtl](https://github.com/obahareth/notion-rtl).) | ||||
| - extension: "weekly view" = calendar views named "weekly" will show only the 7 days of this week. (ported from [github.com/adihd/notionweeklyview](https://github.com/adihd/notionweeklyview).)] | ||||
| - extension: "property layout" = auto-collapse page properties that usually push down page content. (ported from [github.com/alexander-kazakov/notion-layout-extension](https://github.com/alexander-kazakov/notion-layout-extension).) | ||||
| 
 | ||||
| > 📥 `npm i -g notion-enhancer@0.8.0` | ||||
| 
 | ||||
| ### v0.7.0 (2020-07-09) | ||||
| 
 | ||||
| - new: tray option to use system default emojis (instead of twitter's emojiset). | ||||
| - new: mac support (identical functionality to other platforms with the | ||||
|   exception of the native minimise/maximise/close buttons being kept, as they integrate | ||||
|   better with the OS while not being out-of-place in notion). | ||||
| - new: notion-deb-builder support for linux. | ||||
| - new: an alert will be shown if there is an update available for the enhancer. | ||||
| - improved: replaced button symbols with svgs for multi-platform support. | ||||
| - improved: window close button is now red on hover (thanks to [@torchatlas](https://github.com/torchatlas)). | ||||
| - bugfix: `cleaner.py` patched for linux. | ||||
| - bugfix: tray now operates as expected on linux. | ||||
| - bugfix: odd mix of `\\` and `/` being used for windows filepaths. | ||||
| - bugfix: app no longer crashes when sidebar is toggled. | ||||
| 
 | ||||
| > 📥 [notion-enhancer.v0.7.0.zip](https://github.com/notion-enhancer/notion-enhancer/archive/v0.7.0.zip) | ||||
| 
 | ||||
| ### v0.6.0 (2020-06-30) | ||||
| 
 | ||||
| - style: custom fonts. | ||||
| - style: font resizing. | ||||
| - style: hide discussions (thanks to [u/Roosmaryn](https://www.reddit.com/user/Roosmaryn/)). | ||||
| - new: custom colour theming, demonstrated via the dark+ theme. | ||||
| - new: linux support (thanks to [@Blacksuan19](https://github.com/Blacksuan19)). | ||||
| - improved: if hotkey is pressed while notion is unfocused, it will bring it to the front rather than hiding it. | ||||
| - improved: stop window buttons breaking at smaller widths. | ||||
| - improved: more obviously visible drag area. | ||||
| - bugfix: specify UTF-8 encoding to prevent multibyte/gbk codec errors (thanks to [@etnperlong](https://github.com/etnperlong)). | ||||
| 
 | ||||
| > 📥 [notion-enhancer.v0.6.0.zip](https://github.com/notion-enhancer/notion-enhancer/archive/v0.6.0.zip) | ||||
| 
 | ||||
| ### v0.5.0 (2020-05-23) | ||||
| 
 | ||||
| - new: running from the wsl. | ||||
| - new: reload window with f5. | ||||
| - improved: code has been refactored and cleaned up, | ||||
|   inc. file renaming and a `customiser.py` that doesn't require | ||||
|   a run of `cleaner.py` to build modifications. | ||||
|   improved: scrollbar colours that fit better with notion's theming. | ||||
| - bugfix: un-break having multiple notion windows open. | ||||
| 
 | ||||
| > 📥 [notion-enhancer.v0.5.0.zip](https://github.com/notion-enhancer/notion-enhancer/archive/v0.5.0.zip) | ||||
| 
 | ||||
| **development here taken over by [@dragonwocky](https://github.com/dragonwocky).** | ||||
| 
 | ||||
| **the ~~crossed out~~ features below are no longer features included by default,** | ||||
| **but can still easily be added as [custom tweaks](https://github.com/notion-enhancer/tweaks).** | ||||
| 
 | ||||
| ### 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. | ||||
| @ -1,13 +1,5 @@ | ||||
| # extension | ||||
| # notion-enhancer/extension | ||||
| 
 | ||||
| bringing all your favourite notion-enhancer features to the browser (wip) | ||||
| an enhancer/customiser for the all-in-one productivity workspace notion.so (browser) | ||||
| 
 | ||||
| > considering that using notion in the browser is more lightweight & probably preferred for some people since it means they have less apps open, i've always planned to eventually port the enhancer to be a chrome extension. | ||||
| > | ||||
| > since the enhancer focuses on being able to manipulate notion's app files, it's not going to be a very friendly transfer to web - a few features will have to be lost (e.g. frameless mode & tabs, of course) and the extensions system will need to be completely rebuilt. | ||||
| > | ||||
| > to make this work better, i'm splitting things up: an mod repository, a chrome extension, and an app loader. | ||||
| > | ||||
| > i'll be building the enhancer chrome-first from now on, since it'll be more limited there (and it's easier to add extra features when porting than to take features out), and then releasing the app loader a little afterwards. both the app loader and the chrome extension will source the same themes & extensions from the mod repository, and updates & releases of individual mods won't require waiting for the enhancer version anymore. | ||||
| 
 | ||||
| -- from the #announcements channel of the [notion-enhancer discord](https://discord.gg/sFWPXtA) | ||||
| [read the docs online](https://notion-enhancer.github.io/) | ||||
|  | ||||
| @ -1 +1 @@ | ||||
| Subproject commit 5030fe2b0fd71b397796b055934ec50f7e909a5c | ||||
| Subproject commit 9815d73b9277e96864654a8d8dd48762039c9845 | ||||
							
								
								
									
										1
									
								
								extension/dep
									
									
									
									
									
										Submodule
									
								
							
							
								
								
								
								
								
								
							
						
						| @ -0,0 +1 @@ | ||||
| Subproject commit 1a4762550fe185706be26678f734b0475066c3e4 | ||||
| Before Width: | Height: | Size: 59 KiB | 
							
								
								
									
										6
									
								
								extension/dep/jscolor.min.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
							
								
								
									
										12
									
								
								extension/dep/markdown-it.min.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
							
								
								
									
										13
									
								
								extension/dep/prism.min.js
									
									
									
									
										vendored
									
									
								
							
							
						
						| @ -1,58 +0,0 @@ | ||||
| /** | ||||
|  * style-vendorizer v2.0.0 | ||||
|  * @license MIT | ||||
|  * @source https://unpkg.com/style-vendorizer@^2.0.0?module
 | ||||
|  */ | ||||
| 
 | ||||
| var i = new Map([ | ||||
|   ['align-self', '-ms-grid-row-align'], | ||||
|   ['color-adjust', '-webkit-print-color-adjust'], | ||||
|   ['column-gap', 'grid-column-gap'], | ||||
|   ['gap', 'grid-gap'], | ||||
|   ['grid-template-columns', '-ms-grid-columns'], | ||||
|   ['grid-template-rows', '-ms-grid-rows'], | ||||
|   ['justify-self', '-ms-grid-column-align'], | ||||
|   ['margin-inline-end', '-webkit-margin-end'], | ||||
|   ['margin-inline-start', '-webkit-margin-start'], | ||||
|   ['overflow-wrap', 'word-wrap'], | ||||
|   ['padding-inline-end', '-webkit-padding-end'], | ||||
|   ['padding-inline-start', '-webkit-padding-start'], | ||||
|   ['row-gap', 'grid-row-gap'], | ||||
|   ['scroll-margin-bottom', 'scroll-snap-margin-bottom'], | ||||
|   ['scroll-margin-left', 'scroll-snap-margin-left'], | ||||
|   ['scroll-margin-right', 'scroll-snap-margin-right'], | ||||
|   ['scroll-margin-top', 'scroll-snap-margin-top'], | ||||
|   ['scroll-margin', 'scroll-snap-margin'], | ||||
|   ['text-combine-upright', '-ms-text-combine-horizontal'], | ||||
| ]); | ||||
| function r(r) { | ||||
|   return i.get(r); | ||||
| } | ||||
| function n(i) { | ||||
|   var r = | ||||
|     /^(?:(text-(?:decoration$|e|or|si)|back(?:ground-cl|d|f)|box-d|(?:mask(?:$|-[ispro]|-cl)))|(tab-|column(?!-s)|text-align-l)|(ap)|(u|hy))/i.exec( | ||||
|       i | ||||
|     ); | ||||
|   return r ? (r[1] ? 1 : r[2] ? 2 : r[3] ? 3 : 5) : 0; | ||||
| } | ||||
| function t(i, r) { | ||||
|   var n = /^(?:(pos)|(background-i)|((?:max-|min-)?(?:block-s|inl|he|widt))|(dis))/i.exec(i); | ||||
|   return n | ||||
|     ? n[1] | ||||
|       ? /^sti/i.test(r) | ||||
|         ? 1 | ||||
|         : 0 | ||||
|       : n[2] | ||||
|       ? /^image-/i.test(r) | ||||
|         ? 1 | ||||
|         : 0 | ||||
|       : n[3] | ||||
|       ? '-' === r[3] | ||||
|         ? 2 | ||||
|         : 0 | ||||
|       : /^(inline-)?grid$/i.test(r) | ||||
|       ? 4 | ||||
|       : 0 | ||||
|     : 0; | ||||
| } | ||||
| export { r as cssPropertyAlias, n as cssPropertyPrefixFlags, t as cssValuePrefixFlags }; | ||||
| @ -1,43 +0,0 @@ | ||||
| /** | ||||
|  * Twind v0.16.16 | ||||
|  * @license MIT | ||||
|  * @source https://unpkg.com/@twind/content@0.1.0/content.js?module
 | ||||
|  */ | ||||
| 
 | ||||
| import { directive as o } from './twind.mjs'; | ||||
| var c = new Set([ | ||||
|     'open-quote', | ||||
|     'close-quote', | ||||
|     'no-open-quote', | ||||
|     'no-close-quote', | ||||
|     'normal', | ||||
|     'none', | ||||
|     'inherit', | ||||
|     'initial', | ||||
|     'unset', | ||||
|   ]), | ||||
|   n = (t) => t.join('-'), | ||||
|   s = (t) => { | ||||
|     switch (t[0]) { | ||||
|       case 'data': | ||||
|         return `attr(${n(t)})`; | ||||
|       case 'attr': | ||||
|       case 'counter': | ||||
|         return `${t[0]}(${n(t.slice(1))})`; | ||||
|       case 'var': | ||||
|         return `var(--${n(t)})`; | ||||
|       case void 0: | ||||
|         return 'attr(data-content)'; | ||||
|       default: | ||||
|         return JSON.stringify(n(t)); | ||||
|     } | ||||
|   }, | ||||
|   i = (t, { theme: r }) => { | ||||
|     let e = Array.isArray(t) ? n(t) : t; | ||||
|     return { | ||||
|       content: | ||||
|         (e && r('content', [e], '')) || (c.has(e) && e) || (Array.isArray(t) ? s(t) : e), | ||||
|     }; | ||||
|   }, | ||||
|   u = (t, r) => (Array.isArray(t) ? i(t, r) : o(i, t)); | ||||
| export { u as content }; | ||||
| @ -1,134 +0,0 @@ | ||||
| /** | ||||
|  * Twind v0.16.16 | ||||
|  * @license MIT | ||||
|  * @source https://unpkg.com/twind@0.16.16/css/css.js?module
 | ||||
|  */ | ||||
| 
 | ||||
| // src/css/index.ts
 | ||||
| import { apply, hash, directive } from "./twind.mjs"; | ||||
| 
 | ||||
| // src/internal/util.ts
 | ||||
| var includes = (value, search) => !!~value.indexOf(search); | ||||
| var join = (parts, separator = "-") => parts.join(separator); | ||||
| var hyphenate = value => value.replace(/[A-Z]/g, "-$&").toLowerCase(); | ||||
| var evalThunk = (value, context) => { | ||||
|   while (typeof value == "function") { | ||||
|     value = value(context); | ||||
|   } | ||||
|   return value; | ||||
| }; | ||||
| var isCSSProperty = (key, value) => !includes("@:&", key[0]) && (includes("rg", (typeof value)[5]) || Array.isArray(value)); | ||||
| var merge = (target, source, context) => source ? Object.keys(source).reduce((target2, key) => { | ||||
|   const value = evalThunk(source[key], context); | ||||
|   if (isCSSProperty(key, value)) { | ||||
|     target2[hyphenate(key)] = value; | ||||
|   } else { | ||||
|     target2[key] = key[0] == "@" && includes("figa", key[1]) ? (target2[key] || []).concat(value) : merge(target2[key] || {}, value, context); | ||||
|   } | ||||
|   return target2; | ||||
| }, target) : target; | ||||
| var escape = typeof CSS !== "undefined" && CSS.escape || (className => className.replace(/[!"'`*+.,;:\\/<=>?@#$%&^|~()[\]{}]/g, "\\$&").replace(/^\d/, "\\3$& ")); | ||||
| var buildMediaQuery = screen2 => { | ||||
|   if (!Array.isArray(screen2)) { | ||||
|     screen2 = [screen2]; | ||||
|   } | ||||
|   return "@media " + join(screen2.map(screen3 => { | ||||
|     if (typeof screen3 == "string") { | ||||
|       screen3 = { min: screen3 }; | ||||
|     } | ||||
|     return screen3.raw || join(Object.keys(screen3).map(feature => `(${feature}-width:${screen3[feature]})`), " and "); | ||||
|   }), ","); | ||||
| }; | ||||
| 
 | ||||
| // src/css/index.ts
 | ||||
| var translate = (tokens, context) => { | ||||
|   const collect = (target, token) => Array.isArray(token) ? token.reduce(collect, target) : merge(target, evalThunk(token, context), context); | ||||
|   return tokens.reduce(collect, {}); | ||||
| }; | ||||
| var newRule = /\s*(?:([\w-%@]+)\s*:?\s*([^{;]+?)\s*(?:;|$|})|([^;}{]*?)\s*{)|(})/gi; | ||||
| var ruleClean = /\/\*[\s\S]*?\*\/|\s+|\n/gm; | ||||
| var decorate = (selectors, currentBlock) => selectors.reduceRight((rules, selector) => ({ [selector]: rules }), currentBlock); | ||||
| var saveBlock = (rules, selectors, currentBlock) => { | ||||
|   if (currentBlock) { | ||||
|     rules.push(decorate(selectors, currentBlock)); | ||||
|   } | ||||
| }; | ||||
| var interleave = (strings, interpolations, context) => { | ||||
|   let buffer = strings[0]; | ||||
|   const result = []; | ||||
|   for (let index = 0; index < interpolations.length;) { | ||||
|     const interpolation = evalThunk(interpolations[index], context); | ||||
|     if (interpolation && typeof interpolation == "object") { | ||||
|       result.push(buffer, interpolation); | ||||
|       buffer = strings[++index]; | ||||
|     } else { | ||||
|       buffer += (interpolation || "") + strings[++index]; | ||||
|     } | ||||
|   } | ||||
|   result.push(buffer); | ||||
|   return result; | ||||
| }; | ||||
| var astish = (values, context) => { | ||||
|   const selectors = []; | ||||
|   const rules = []; | ||||
|   let currentBlock; | ||||
|   let match; | ||||
|   for (let index = 0; index < values.length; index++) { | ||||
|     const value = values[index]; | ||||
|     if (typeof value == "string") { | ||||
|       while (match = newRule.exec(value.replace(ruleClean, " "))) { | ||||
|         if (!match[0]) | ||||
|         continue; | ||||
|         if (match[4]) { | ||||
|           currentBlock = saveBlock(rules, selectors, currentBlock); | ||||
|           selectors.pop(); | ||||
|         } | ||||
|         if (match[3]) { | ||||
|           currentBlock = saveBlock(rules, selectors, currentBlock); | ||||
|           selectors.push(match[3]); | ||||
|         } else if (!match[4]) { | ||||
|           if (!currentBlock) | ||||
|           currentBlock = {}; | ||||
|           const value2 = match[2] && /\S/.test(match[2]) ? match[2] : values[++index]; | ||||
|           if (value2) { | ||||
|             if (match[1] == "@apply") { | ||||
|               merge(currentBlock, evalThunk(apply(value2), context), context); | ||||
|             } else { | ||||
|               currentBlock[match[1]] = value2; | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|     } else { | ||||
|       currentBlock = saveBlock(rules, selectors, currentBlock); | ||||
|       rules.push(decorate(selectors, value)); | ||||
|     } | ||||
|   } | ||||
|   saveBlock(rules, selectors, currentBlock); | ||||
|   return rules; | ||||
| }; | ||||
| var cssFactory = (tokens, context) => translate(Array.isArray(tokens[0]) && Array.isArray(tokens[0].raw) ? astish(interleave(tokens[0], tokens.slice(1), context), context) : tokens, context); | ||||
| var css = (...tokens) => directive(cssFactory, tokens); | ||||
| var keyframesFactory = (tokens, context) => { | ||||
|   const waypoints = cssFactory(tokens, context); | ||||
|   const id = hash(JSON.stringify(waypoints)); | ||||
|   context.tw(() => ({ [`@keyframes ${id}`]: waypoints })); | ||||
|   return id; | ||||
| }; | ||||
| var keyframes = (...tokens) => directive(keyframesFactory, tokens); | ||||
| var animation = (value, waypoints) => waypoints === void 0 ? (...args) => animation(value, keyframes(...args)) : css({ | ||||
|   ...(value && typeof value == "object" ? value : { animation: value }), | ||||
|   animationName: typeof waypoints == "function" ? waypoints : keyframes(waypoints) }); | ||||
| 
 | ||||
| var screenFactory = ({ size, rules }, context) => { | ||||
|   const media = buildMediaQuery(context.theme("screens", size)); | ||||
|   return rules === void 0 ? media : { | ||||
|     [media]: typeof rules == "function" ? evalThunk(rules, context) : cssFactory([rules], context) }; | ||||
| 
 | ||||
| }; | ||||
| var screen = (size, rules) => directive(screenFactory, { size, rules }); | ||||
| export { | ||||
| animation, | ||||
| css, | ||||
| keyframes, | ||||
| screen }; | ||||
							
								
								
									
										5
									
								
								extension/env/env.mjs
									
									
									
									
										vendored
									
									
								
							
							
						
						| @ -6,10 +6,7 @@ | ||||
| 
 | ||||
| 'use strict'; | ||||
| 
 | ||||
| /** | ||||
|  * environment-specific methods and constants | ||||
|  * @module notion-enhancer/api/env | ||||
|  */ | ||||
| /** environment-specific methods and constants */ | ||||
| 
 | ||||
| /** | ||||
|  * the environment/platform name code is currently being executed in | ||||
|  | ||||
							
								
								
									
										9
									
								
								extension/env/fs.mjs
									
									
									
									
										vendored
									
									
								
							
							
						
						| @ -6,10 +6,7 @@ | ||||
| 
 | ||||
| 'use strict'; | ||||
| 
 | ||||
| /** | ||||
|  * environment-specific filesystem reading | ||||
|  * @module notion-enhancer/api/fs | ||||
|  */ | ||||
| /** environment-specific file reading */ | ||||
| 
 | ||||
| /** | ||||
|  * transform a path relative to the enhancer root directory into an absolute path | ||||
| @ -21,7 +18,7 @@ export const localPath = chrome.runtime.getURL; | ||||
| /** | ||||
|  * fetch and parse a json file's contents | ||||
|  * @param {string} path - a url or within-the-enhancer filepath | ||||
|  * @param {object} [opts] - the second argument of a fetch() request | ||||
|  * @param {object=} opts - the second argument of a fetch() request | ||||
|  * @returns {object} the json value of the requested file as a js object | ||||
|  */ | ||||
| export const getJSON = (path, opts = {}) => | ||||
| @ -30,7 +27,7 @@ export const getJSON = (path, opts = {}) => | ||||
| /** | ||||
|  * fetch a text file's contents | ||||
|  * @param {string} path - a url or within-the-enhancer filepath | ||||
|  * @param {object} [opts] - the second argument of a fetch() request | ||||
|  * @param {object=} opts - the second argument of a fetch() request | ||||
|  * @returns {string} the text content of the requested file | ||||
|  */ | ||||
| export const getText = (path, opts = {}) => | ||||
|  | ||||
							
								
								
									
										30
									
								
								extension/env/storage.mjs
									
									
									
									
										vendored
									
									
								
							
							
						
						| @ -6,18 +6,15 @@ | ||||
| 
 | ||||
| 'use strict'; | ||||
| 
 | ||||
| /** | ||||
|  * environment-specific data persistence | ||||
|  * @module notion-enhancer/api/storage | ||||
|  */ | ||||
| /** environment-specific data persistence */ | ||||
| 
 | ||||
| const _queue = [], | ||||
|   _onChangeListeners = []; | ||||
| 
 | ||||
| /** | ||||
|  * get persisted data | ||||
|  * @param {array<string>} path - the path of keys to the value being fetched | ||||
|  * @param {*} [fallback] - a default value if the path is not matched | ||||
|  * @param {string[]} path - the path of keys to the value being fetched | ||||
|  * @param {unknown=} fallback - a default value if the path is not matched | ||||
|  * @returns {Promise} value ?? fallback | ||||
|  */ | ||||
| export const get = (path, fallback = undefined) => { | ||||
| @ -39,8 +36,8 @@ export const get = (path, fallback = undefined) => { | ||||
| 
 | ||||
| /** | ||||
|  * persist data | ||||
|  * @param {array<string>} path - the path of keys to the value being set | ||||
|  * @param {*} value - the data to save | ||||
|  * @param {string[]} path - the path of keys to the value being set | ||||
|  * @param {unknown} value - the data to save | ||||
|  * @returns {Promise} resolves when data has been saved | ||||
|  */ | ||||
| export const set = (path, value) => { | ||||
| @ -68,7 +65,7 @@ export const set = (path, value) => { | ||||
|         } | ||||
|         chrome.storage.local.set({ [namespace]: values[namespace] }, () => { | ||||
|           _onChangeListeners.forEach((listener) => | ||||
|             listener({ type: 'set', path: pathClone, new: value, old }) | ||||
|             listener({ path: pathClone, new: value, old }) | ||||
|           ); | ||||
|           res(value); | ||||
|         }); | ||||
| @ -80,12 +77,13 @@ export const set = (path, value) => { | ||||
| 
 | ||||
| /** | ||||
|  * create a wrapper for accessing a partition of the storage | ||||
|  * @param {array<string>} namespace - the path of keys to prefix all storage requests with | ||||
|  * @param {function} [get] - the storage get function to be wrapped | ||||
|  * @param {function} [set] - the storage set function to be wrapped | ||||
|  * @param {string[]} namespace - the path of keys to prefix all storage requests with | ||||
|  * @param {function=} get - the storage get function to be wrapped | ||||
|  * @param {function=} set - the storage set function to be wrapped | ||||
|  * @returns {object} an object with the wrapped get/set functions | ||||
|  */ | ||||
| export const db = (namespace, getFunc = get, setFunc = set) => { | ||||
|   if (typeof namespace === 'string') namespace = [namespace]; | ||||
|   return { | ||||
|     get: (path = [], fallback = undefined) => getFunc([...namespace, ...path], fallback), | ||||
|     set: (path, value) => setFunc([...namespace, ...path], value), | ||||
| @ -112,9 +110,7 @@ export const removeChangeListener = (callback) => { | ||||
| /** | ||||
|  * @callback onStorageChangeCallback | ||||
|  * @param {object} event | ||||
|  * @param {string} event.type - 'set' or 'reset' | ||||
|  * @param {string} event.namespace- the name of the store, e.g. a mod id | ||||
|  * @param {string} [event.key] - the key associated with the changed value | ||||
|  * @param {string} [event.new] - the new value being persisted to the store | ||||
|  * @param {string} [event.old] - the previous value associated with the key | ||||
|  * @param {string} event.path- the path of keys to the changed value | ||||
|  * @param {string=} event.new - the new value being persisted to the store | ||||
|  * @param {string=} event.old - the previous value associated with the key | ||||
|  */ | ||||
|  | ||||
| Before Width: | Height: | Size: 55 KiB | 
| Before Width: | Height: | Size: 5.1 KiB | 
| Before Width: | Height: | Size: 623 B | 
| Before Width: | Height: | Size: 10 KiB | 
| Before Width: | Height: | Size: 1.3 KiB | 
| Before Width: | Height: | Size: 2.0 KiB | 
| Before Width: | Height: | Size: 21 KiB | 
| Before Width: | Height: | Size: 18 KiB | 
| Before Width: | Height: | Size: 5.3 KiB | 
| Before Width: | Height: | Size: 634 B | 
| Before Width: | Height: | Size: 11 KiB | 
| Before Width: | Height: | Size: 1.3 KiB | 
| Before Width: | Height: | Size: 2.0 KiB | 
| Before Width: | Height: | Size: 24 KiB | 
| Before Width: | Height: | Size: 24 KiB | 
							
								
								
									
										35
									
								
								extension/init.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,35 @@ | ||||
| /* | ||||
|  * notion-enhancer | ||||
|  * (c) 2021 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
 | ||||
|  * (https://notion-enhancer.github.io/) under the MIT license
 | ||||
|  */ | ||||
| 
 | ||||
| 'use strict'; | ||||
| 
 | ||||
| (async () => { | ||||
|   const site = location.host.endsWith('.notion.site'), | ||||
|     page = location.pathname.split(/[/-]/g).reverse()[0].length === 32, | ||||
|     whitelisted = ['/', '/onboarding'].includes(location.pathname), | ||||
|     signedIn = localStorage['LRU:KeyValueStore2:current-user-id']; | ||||
| 
 | ||||
|   if (site || page || (whitelisted && signedIn)) { | ||||
|     const api = await import(chrome.runtime.getURL('api/index.mjs')), | ||||
|       { fs, registry, web } = api; | ||||
| 
 | ||||
|     for (const mod of await registry.list((mod) => registry.enabled(mod.id))) { | ||||
|       for (const sheet of mod.css?.client || []) { | ||||
|         web.loadStylesheet(`repo/${mod._dir}/${sheet}`); | ||||
|       } | ||||
|       for (let script of mod.js?.client || []) { | ||||
|         script = await import(fs.localPath(`repo/${mod._dir}/${script}`)); | ||||
|         script.default(api, await registry.db(mod.id)); | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     const errors = await registry.errors(); | ||||
|     if (errors.length) { | ||||
|       console.log('[notion-enhancer] registry errors:'); | ||||
|       console.table(errors); | ||||
|     } | ||||
|   } | ||||
| })(); | ||||
| @ -1,34 +0,0 @@ | ||||
| /* | ||||
|  * notion-enhancer | ||||
|  * (c) 2021 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
 | ||||
|  * (https://notion-enhancer.github.io/) under the MIT license
 | ||||
|  */ | ||||
| 
 | ||||
| 'use strict'; | ||||
| 
 | ||||
| (async () => { | ||||
|   if (location.pathname === '/') await new Promise((res, rej) => setTimeout(res, 500)); | ||||
| 
 | ||||
|   const site = location.host.endsWith('.notion.site'), | ||||
|     page = location.pathname.split(/[/-]/g).reverse()[0].length === 32; | ||||
| 
 | ||||
|   if (site || page) { | ||||
|     import(chrome.runtime.getURL('api/_.mjs')).then(async (api) => { | ||||
|       const { fs, registry, web } = api; | ||||
|       for (const mod of await registry.list((mod) => registry.enabled(mod.id))) { | ||||
|         for (const sheet of mod.css?.client || []) { | ||||
|           web.loadStylesheet(`repo/${mod._dir}/${sheet}`); | ||||
|         } | ||||
|         for (let script of mod.js?.client || []) { | ||||
|           script = await import(fs.localPath(`repo/${mod._dir}/${script}`)); | ||||
|           script.default(api, await registry.db(mod.id)); | ||||
|         } | ||||
|       } | ||||
|       const errors = await registry.errors(); | ||||
|       if (errors.length) { | ||||
|         console.log('[notion-enhancer] registry errors:'); | ||||
|         console.table(errors); | ||||
|       } | ||||
|     }); | ||||
|   } | ||||
| })(); | ||||
| @ -6,32 +6,25 @@ | ||||
|   "description": "an enhancer/customiser for the all-in-one productivity workspace notion.so", | ||||
|   "homepage_url": "https://notion-enhancer.github.io", | ||||
|   "icons": { | ||||
|     "16": "icon/colour-x16.png", | ||||
|     "32": "icon/colour-x32.png", | ||||
|     "48": "icon/colour-x48.png", | ||||
|     "128": "icon/colour-x128.png", | ||||
|     "256": "icon/colour-x256.png", | ||||
|     "512": "icon/colour-x512.png" | ||||
|     "16": "media/colour-x16.png", | ||||
|     "32": "media/colour-x32.png", | ||||
|     "48": "media/colour-x48.png", | ||||
|     "128": "media/colour-x128.png", | ||||
|     "256": "media/colour-x256.png", | ||||
|     "512": "media/colour-x512.png" | ||||
|   }, | ||||
|   "browser_action": {}, | ||||
|   "background": { "scripts": ["worker.js"] }, | ||||
|   "options_ui": { | ||||
|     "page": "repo/menu@a6621988-551d-495a-97d8-3c568bca2e9e/menu.html", | ||||
|     "page": "repo/menu/menu.html", | ||||
|     "open_in_tab": true | ||||
|   }, | ||||
|   "web_accessible_resources": ["env/*", "api/*", "dep/*", "icon/*", "repo/*"], | ||||
|   "web_accessible_resources": ["env/*", "api/*", "dep/*", "media/*", "repo/*"], | ||||
|   "content_scripts": [ | ||||
|     { | ||||
|       "matches": ["https://*.notion.so/*", "https://*.notion.site/*"], | ||||
|       "js": ["launcher.js"] | ||||
|       "js": ["init.js"] | ||||
|     } | ||||
|   ], | ||||
|   "permissions": [ | ||||
|     "tabs", | ||||
|     "storage", | ||||
|     "unlimitedStorage", | ||||
|     "https://*.notion.so/*", | ||||
|     "https://*.notion.site/*", | ||||
|     "<all_urls>" | ||||
|   ] | ||||
|   "permissions": ["tabs", "storage", "clipboardRead", "clipboardWrite", "unlimitedStorage"] | ||||
| } | ||||
|  | ||||
							
								
								
									
										1
									
								
								extension/media
									
									
									
									
									
										Submodule
									
								
							
							
								
								
								
								
								
								
							
						
						| @ -0,0 +1 @@ | ||||
| Subproject commit 2a0a17998385f1d86148b9213451b3a5deff6bae | ||||
							
								
								
									
										1
									
								
								extension/repo
									
									
									
									
									
										Submodule
									
								
							
							
								
								
								
								
								
								
							
						
						| @ -0,0 +1 @@ | ||||
| Subproject commit 76e36ab47ca2ffd354280b5c907f9ebb827c30c4 | ||||
| @ -1,9 +0,0 @@ | ||||
| /* | ||||
|  * notion-enhancer: bypass-preview | ||||
|  * (c) 2021 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/) | ||||
|  * (https://notion-enhancer.github.io/) under the MIT license | ||||
|  */ | ||||
| 
 | ||||
| .notion-peek-renderer { | ||||
|   display: none; | ||||
| } | ||||
| @ -1,33 +0,0 @@ | ||||
| /* | ||||
|  * notion-enhancer: bypass-preview | ||||
|  * (c) 2021 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
 | ||||
|  * (https://notion-enhancer.github.io/) under the MIT license
 | ||||
|  */ | ||||
| 
 | ||||
| 'use strict'; | ||||
| 
 | ||||
| export default async function (api, db) { | ||||
|   const { web, components } = api; | ||||
|   await web.whenReady(); | ||||
| 
 | ||||
|   let _lastPage = {}; | ||||
|   function getCurrentPage() { | ||||
|     if (web.queryParams().get('p')) return { type: 'preview', id: web.queryParams().get('p') }; | ||||
|     return { type: 'page', id: location.pathname.split(/(-|\/)/g).reverse()[0] }; | ||||
|   } | ||||
| 
 | ||||
|   web.addDocumentObserver((event) => { | ||||
|     const currentPage = getCurrentPage(); | ||||
|     if (currentPage.id !== _lastPage.id || currentPage.type !== _lastPage.type) { | ||||
|       const openAsPage = document.querySelector( | ||||
|         '.notion-peek-renderer [style*="height: 45px;"] a' | ||||
|       ); | ||||
|       if (openAsPage) { | ||||
|         if (currentPage.id === _lastPage.id && currentPage.type === 'preview') { | ||||
|           history.back(); | ||||
|         } else openAsPage.click(); | ||||
|       } | ||||
|       _lastPage = getCurrentPage(); | ||||
|     } | ||||
|   }); | ||||
| } | ||||
| @ -1,22 +0,0 @@ | ||||
| { | ||||
|   "name": "bypass-preview", | ||||
|   "id": "cb6fd684-f113-4a7a-9423-8f0f0cff069f", | ||||
|   "version": "0.2.0", | ||||
|   "description": "go straight to the normal full view when opening a page.", | ||||
|   "tags": ["extension", "automation"], | ||||
|   "authors": [ | ||||
|     { | ||||
|       "name": "dragonwocky", | ||||
|       "email": "thedragonring.bod@gmail.com", | ||||
|       "homepage": "https://dragonwocky.me/", | ||||
|       "avatar": "https://dragonwocky.me/avatar.jpg" | ||||
|     } | ||||
|   ], | ||||
|   "js": { | ||||
|     "client": ["client.mjs"] | ||||
|   }, | ||||
|   "css": { | ||||
|     "client": ["client.css"] | ||||
|   }, | ||||
|   "options": [] | ||||
| } | ||||
| @ -1,21 +0,0 @@ | ||||
| /* | ||||
|  * notion-enhancer: calendar-scroll | ||||
|  * (c) 2021 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/) | ||||
|  * (https://notion-enhancer.github.io/) under the MIT license | ||||
|  */ | ||||
| 
 | ||||
| #calendar-scroll-to-week { | ||||
|   background: var(--theme--button_hover); | ||||
|   border: 1px solid transparent; | ||||
|   font-size: var(--theme--font_label-size); | ||||
|   color: var(--theme--text); | ||||
|   height: 24px; | ||||
|   border-radius: 3px; | ||||
|   line-height: 1.2; | ||||
|   padding: 0 0.5em; | ||||
|   margin-right: 5px; | ||||
| } | ||||
| #calendar-scroll-to-week:hover { | ||||
|   background: transparent; | ||||
|   border: 1px solid var(--theme--button_hover); | ||||
| } | ||||
| @ -1,64 +0,0 @@ | ||||
| /* | ||||
|  * notion-enhancer core: bypass-preview | ||||
|  * (c) 2021 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
 | ||||
|  * (https://notion-enhancer.github.io/) under the MIT license
 | ||||
|  */ | ||||
| 
 | ||||
| 'use strict'; | ||||
| 
 | ||||
| import { web } from '../../api/_.mjs'; | ||||
| 
 | ||||
| const $button = web.createElement( | ||||
|   web.html`<button id="calendar-scroll-to-week">Scroll</button>` | ||||
| ); | ||||
| $button.addEventListener('click', async (event) => { | ||||
|   let $day = document.querySelector('.notion-calendar-view-day[style*="background:"]'); | ||||
|   while (!$day) { | ||||
|     const $toolbar = document.querySelector( | ||||
|         '.notion-calendar-view > :first-child > :first-child > :first-child' | ||||
|       ), | ||||
|       year = +$toolbar.children[0].innerText.split(' ')[1], | ||||
|       month = { | ||||
|         'January': 0, | ||||
|         'February': 1, | ||||
|         'March': 2, | ||||
|         'April': 3, | ||||
|         'May': 4, | ||||
|         'June': 5, | ||||
|         'July': 6, | ||||
|         'August': 7, | ||||
|         'September': 8, | ||||
|         'October': 9, | ||||
|         'November': 10, | ||||
|         'December': 11, | ||||
|       }[$toolbar.children[0].innerText.split(' ')[0]], | ||||
|       now = new Date(); | ||||
|     switch (true) { | ||||
|       case now.getFullYear() < year: | ||||
|       case now.getFullYear() === year && now.getMonth() < month: | ||||
|         $toolbar.children[3].click(); | ||||
|         break; | ||||
|       case now.getFullYear() > year: | ||||
|       case now.getFullYear() === year && now.getMonth() > month: | ||||
|         $toolbar.children[5].click(); | ||||
|         break; | ||||
|       default: | ||||
|         await new Promise((res, rej) => requestAnimationFrame(res)); | ||||
|         $day = document.querySelector('.notion-calendar-view-day[style*="background:"]'); | ||||
|     } | ||||
|     await new Promise((res, rej) => requestAnimationFrame(res)); | ||||
|   } | ||||
|   const $scroller = document.querySelector('.notion-frame .notion-scroller'); | ||||
|   $scroller.scroll({ | ||||
|     top: $day.offsetParent.offsetParent.offsetTop + 70, | ||||
|     behavior: 'auto', | ||||
|   }); | ||||
| }); | ||||
| 
 | ||||
| web.addDocumentObserver((event) => { | ||||
|   if (document.contains($button)) return; | ||||
|   const toolbar = document.querySelector( | ||||
|     '.notion-calendar-view > :first-child > :first-child > :first-child' | ||||
|   ); | ||||
|   if (toolbar) toolbar.insertBefore($button, toolbar.children[2]); | ||||
| }); | ||||
| @ -1,22 +0,0 @@ | ||||
| { | ||||
|   "name": "calendar-scroll", | ||||
|   "id": "b1c7db33-dfee-489a-a76c-0dd66f7ed29a", | ||||
|   "version": "0.2.0", | ||||
|   "description": "add a button to jump down to the current week in fullpage/infinite-scroll calendars.", | ||||
|   "tags": ["extension", "shortcut"], | ||||
|   "authors": [ | ||||
|     { | ||||
|       "name": "dragonwocky", | ||||
|       "email": "thedragonring.bod@gmail.com", | ||||
|       "homepage": "https://dragonwocky.me/", | ||||
|       "avatar": "https://dragonwocky.me/avatar.jpg" | ||||
|     } | ||||
|   ], | ||||
|   "js": { | ||||
|     "client": ["client.mjs"] | ||||
|   }, | ||||
|   "css": { | ||||
|     "client": ["client.css"] | ||||
|   }, | ||||
|   "options": [] | ||||
| } | ||||
| @ -1,33 +0,0 @@ | ||||
| { | ||||
|   "__comment": "pseudo-mod to allow configuration of API-provided components", | ||||
|   "name": "components", | ||||
|   "id": "36a2ffc9-27ff-480e-84a7-c7700a7d232d", | ||||
|   "version": "0.2.0", | ||||
|   "description": "shared notion-style elements.", | ||||
|   "tags": ["core"], | ||||
|   "authors": [ | ||||
|     { | ||||
|       "name": "dragonwocky", | ||||
|       "email": "thedragonring.bod@gmail.com", | ||||
|       "homepage": "https://dragonwocky.me/", | ||||
|       "avatar": "https://dragonwocky.me/avatar.jpg" | ||||
|     }, | ||||
|     { | ||||
|       "name": "CloudHill", | ||||
|       "email": "rh.cloudhill@gmail.com", | ||||
|       "homepage": "https://github.com/CloudHill", | ||||
|       "avatar": "https://avatars.githubusercontent.com/u/54142180" | ||||
|     } | ||||
|   ], | ||||
|   "js": {}, | ||||
|   "css": {}, | ||||
|   "options": [ | ||||
|     { | ||||
|       "type": "hotkey", | ||||
|       "key": "panel.hotkey", | ||||
|       "label": "toggle panel hotkey", | ||||
|       "value": "Ctrl+Alt+\\", | ||||
|       "tooltip": "opens/closes the side panel in notion - will only work if a mod is making use of it." | ||||
|     } | ||||
|   ] | ||||
| } | ||||
| @ -1,237 +0,0 @@ | ||||
| /* | ||||
|  * notion-enhancer core: menu | ||||
|  * (c) 2021 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
 | ||||
|  * (https://notion-enhancer.github.io/) under the MIT license
 | ||||
|  */ | ||||
| 
 | ||||
| 'use strict'; | ||||
| 
 | ||||
| import { fmt, web, registry, components } from '../../api/_.mjs'; | ||||
| import { notifications } from './notifications.mjs'; | ||||
| const profileDB = await registry.profileDB(); | ||||
| 
 | ||||
| export const blocks = { | ||||
|   preview: (url) => web.html`<img
 | ||||
|     class="mod-preview" | ||||
|     src="${web.escape(url)}" | ||||
|     alt="" | ||||
|   >`,
 | ||||
|   title: (title) => web.html`<h4 class="mod-title"><span>${web.escape(title)}</span></h4>`, | ||||
|   version: (version) => web.html`<span class="mod-version">v${web.escape(version)}</span>`, | ||||
|   tags: (tags) => { | ||||
|     if (!tags.length) return ''; | ||||
|     return web.render( | ||||
|       web.html`<p class="mod-tags"></p>`, | ||||
|       tags.map((tag) => `#${web.escape(tag)}`).join(' ') | ||||
|     ); | ||||
|   }, | ||||
|   description: (description) => web.html`<p class="mod-description markdown-inline">
 | ||||
|     ${fmt.md.renderInline(description)} | ||||
|   </p>`, | ||||
|   authors: (authors) => { | ||||
|     const author = (author) => web.html`<a
 | ||||
|       class="mod-author" | ||||
|       href="${web.escape(author.homepage)}" | ||||
|       target="_blank" | ||||
|     > | ||||
|       <img class="mod-author-avatar" | ||||
|         src="${web.escape(author.avatar)}" alt="${web.escape(author.name)}'s avatar" | ||||
|       > <span>${web.escape(author.name)}</span> | ||||
|     </a>`; | ||||
|     return web.render(web.html`<p class="mod-authors-container"></p>`, ...authors.map(author)); | ||||
|   }, | ||||
|   toggle: (label, checked) => { | ||||
|     const $label = web.html`<label tabindex="0" class="toggle-label">
 | ||||
|       <span>${web.escape(label)}</span> | ||||
|     </label>`, | ||||
|       $input = web.html`<input tabindex="-1" type="checkbox" class="toggle-check"
 | ||||
|         ${checked ? 'checked' : ''}>`,
 | ||||
|       $feature = web.html`<span class="toggle-box toggle-feature"></span>`; | ||||
|     $label.addEventListener('keyup', (event) => { | ||||
|       if (['Enter', ' '].includes(event.key)) $input.checked = !$input.checked; | ||||
|     }); | ||||
|     return web.render($label, $input, $feature); | ||||
|   }, | ||||
| }; | ||||
| 
 | ||||
| export const options = { | ||||
|   toggle: async (mod, opt) => { | ||||
|     const checked = await profileDB.get([mod.id, opt.key], opt.value), | ||||
|       $toggle = blocks.toggle(opt.label, checked), | ||||
|       $tooltip = web.html`${await components.feather('info', { class: 'input-tooltip' })}`, | ||||
|       $label = $toggle.children[0], | ||||
|       $input = $toggle.children[1]; | ||||
|     if (opt.tooltip) { | ||||
|       $label.prepend($tooltip); | ||||
|       components.tooltip($tooltip, opt.tooltip); | ||||
|     } | ||||
|     $input.addEventListener('change', async (event) => { | ||||
|       await profileDB.set([mod.id, opt.key], $input.checked); | ||||
|       notifications.onChange(); | ||||
|     }); | ||||
|     return $toggle; | ||||
|   }, | ||||
|   select: async (mod, opt) => { | ||||
|     const value = await profileDB.get([mod.id, opt.key], opt.values[0]), | ||||
|       $tooltip = web.html`${await components.feather('info', { class: 'input-tooltip' })}`, | ||||
|       $label = web.render( | ||||
|         web.html`<label class="input-label"></label>`, | ||||
|         web.render(web.html`<p></p>`, opt.tooltip ? $tooltip : '', opt.label) | ||||
|       ), | ||||
|       $options = opt.values.map( | ||||
|         (option) => web.raw`<option
 | ||||
|           class="select-option" | ||||
|           value="${web.escape(option)}" | ||||
|           ${option === value ? 'selected' : ''} | ||||
|         >${web.escape(option)}</option>` | ||||
|       ), | ||||
|       $select = web.html`<select class="input">
 | ||||
|         ${$options.join('')} | ||||
|       </select>`, | ||||
|       $icon = web.html`${await components.feather('chevron-down', { class: 'input-icon' })}`; | ||||
|     if (opt.tooltip) components.tooltip($tooltip, opt.tooltip); | ||||
|     $select.addEventListener('change', async (event) => { | ||||
|       await profileDB.set([mod.id, opt.key], $select.value); | ||||
|       notifications.onChange(); | ||||
|     }); | ||||
|     return web.render($label, $select, $icon); | ||||
|   }, | ||||
|   text: async (mod, opt) => { | ||||
|     const value = await profileDB.get([mod.id, opt.key], opt.value), | ||||
|       $tooltip = web.html`${await components.feather('info', { class: 'input-tooltip' })}`, | ||||
|       $label = web.render( | ||||
|         web.html`<label class="input-label"></label>`, | ||||
|         web.render(web.html`<p></p>`, opt.tooltip ? $tooltip : '', opt.label) | ||||
|       ), | ||||
|       $input = web.html`<input type="text" class="input" value="${web.escape(value)}">`, | ||||
|       $icon = web.html`${await components.feather('type', { class: 'input-icon' })}`; | ||||
|     if (opt.tooltip) components.tooltip($tooltip, opt.tooltip); | ||||
|     $input.addEventListener('change', async (event) => { | ||||
|       await profileDB.set([mod.id, opt.key], $input.value); | ||||
|       notifications.onChange(); | ||||
|     }); | ||||
|     return web.render($label, $input, $icon); | ||||
|   }, | ||||
|   number: async (mod, opt) => { | ||||
|     const value = await profileDB.get([mod.id, opt.key], opt.value), | ||||
|       $tooltip = web.html`${await components.feather('info', { class: 'input-tooltip' })}`, | ||||
|       $label = web.render( | ||||
|         web.html`<label class="input-label"></label>`, | ||||
|         web.render(web.html`<p></p>`, opt.tooltip ? $tooltip : '', opt.label) | ||||
|       ), | ||||
|       $input = web.html`<input type="number" class="input" value="${value}">`, | ||||
|       $icon = web.html`${await components.feather('hash', { class: 'input-icon' })}`; | ||||
|     if (opt.tooltip) components.tooltip($tooltip, opt.tooltip); | ||||
|     $input.addEventListener('change', async (event) => { | ||||
|       await profileDB.set([mod.id, opt.key], $input.value); | ||||
|       notifications.onChange(); | ||||
|     }); | ||||
|     return web.render($label, $input, $icon); | ||||
|   }, | ||||
|   color: async (mod, opt) => { | ||||
|     const value = await profileDB.get([mod.id, opt.key], opt.value), | ||||
|       $tooltip = web.html`${await components.feather('info', { class: 'input-tooltip' })}`, | ||||
|       $label = web.render( | ||||
|         web.html`<label class="input-label"></label>`, | ||||
|         web.render(web.html`<p></p>`, opt.tooltip ? $tooltip : '', opt.label) | ||||
|       ), | ||||
|       $input = web.html`<input type="text" class="input">`, | ||||
|       $icon = web.html`${await components.feather('droplet', { class: 'input-icon' })}`, | ||||
|       paint = () => { | ||||
|         $input.style.background = $picker.toBackground(); | ||||
|         $input.style.color = $picker.isLight() ? '#000' : '#fff'; | ||||
|         $input.style.padding = ''; | ||||
|       }, | ||||
|       $picker = new web.jscolor($input, { | ||||
|         value, | ||||
|         format: 'rgba', | ||||
|         previewSize: 0, | ||||
|         borderRadius: 3, | ||||
|         borderColor: 'var(--theme--ui_divider)', | ||||
|         controlBorderColor: 'var(--theme--ui_divider)', | ||||
|         backgroundColor: 'var(--theme--bg)', | ||||
|         onInput: paint, | ||||
|         onChange: paint, | ||||
|       }); | ||||
|     if (opt.tooltip) components.tooltip($tooltip, opt.tooltip); | ||||
|     $input.addEventListener('change', async (event) => { | ||||
|       await profileDB.set([mod.id, opt.key], $input.value); | ||||
|       notifications.onChange(); | ||||
|     }); | ||||
|     paint(); | ||||
|     return web.render($label, $input, $icon); | ||||
|   }, | ||||
|   file: async (mod, opt) => { | ||||
|     const { filename } = (await profileDB.get([mod.id, opt.key], {})) || {}, | ||||
|       $tooltip = web.html`${await components.feather('info', { class: 'input-tooltip' })}`, | ||||
|       $label = web.render( | ||||
|         web.html`<label class="input-label"></label>`, | ||||
|         web.render(web.html`<p></p>`, opt.tooltip ? $tooltip : '', opt.label) | ||||
|       ), | ||||
|       $pseudo = web.html`<span class="input"><span class="input-placeholder">Upload file...</span></span>`, | ||||
|       $input = web.html`<input type="file" class="hidden" accept=${web.escape( | ||||
|         opt.extensions.join(',') | ||||
|       )}>`,
 | ||||
|       $icon = web.html`${await components.feather('file', { class: 'input-icon' })}`, | ||||
|       $filename = web.html`<span>${web.escape(filename || 'none')}</span>`, | ||||
|       $latest = web.render(web.html`<button class="file-latest">Latest: </button>`, $filename); | ||||
|     if (opt.tooltip) components.tooltip($tooltip, opt.tooltip); | ||||
|     $input.addEventListener('change', (event) => { | ||||
|       const file = event.target.files[0], | ||||
|         reader = new FileReader(); | ||||
|       reader.onload = async (progress) => { | ||||
|         $filename.innerText = file.name; | ||||
|         await profileDB.set([mod.id, opt.key], { | ||||
|           filename: file.name, | ||||
|           content: progress.currentTarget.result, | ||||
|         }); | ||||
|         notifications.onChange(); | ||||
|       }; | ||||
|       reader.readAsText(file); | ||||
|     }); | ||||
|     $latest.addEventListener('click', (event) => { | ||||
|       $filename.innerText = 'none'; | ||||
|       profileDB.set([mod.id, opt.key], {}); | ||||
|     }); | ||||
|     return web.render( | ||||
|       web.html`<div></div>`, | ||||
|       web.render($label, $input, $pseudo, $icon), | ||||
|       $latest | ||||
|     ); | ||||
|   }, | ||||
|   hotkey: async (mod, opt) => { | ||||
|     const value = await profileDB.get([mod.id, opt.key], opt.value), | ||||
|       $tooltip = web.html`${await components.feather('info', { class: 'input-tooltip' })}`, | ||||
|       $label = web.render( | ||||
|         web.html`<label class="input-label"></label>`, | ||||
|         web.render(web.html`<p></p>`, opt.tooltip ? $tooltip : '', opt.label) | ||||
|       ), | ||||
|       $input = web.html`<input type="text" class="input" value="${web.escape(value)}">`, | ||||
|       $icon = web.html`${await components.feather('command', { class: 'input-icon' })}`; | ||||
|     if (opt.tooltip) components.tooltip($tooltip, opt.tooltip); | ||||
|     $input.addEventListener('keydown', async (event) => { | ||||
|       event.preventDefault(); | ||||
|       const pressed = [], | ||||
|         modifiers = { | ||||
|           metaKey: 'Meta', | ||||
|           ctrlKey: 'Control', | ||||
|           altKey: 'Alt', | ||||
|           shiftKey: 'Shift', | ||||
|         }; | ||||
|       for (const modifier in modifiers) { | ||||
|         if (event[modifier]) pressed.push(modifiers[modifier]); | ||||
|       } | ||||
|       const empty = ['Backspace', 'Delete'].includes(event.key) && !pressed.length; | ||||
|       if (!empty && !pressed.includes(event.key)) { | ||||
|         let key = event.key; | ||||
|         if (key === ' ') key = 'Space'; | ||||
|         if (key.length === 1) key = event.key.toUpperCase(); | ||||
|         pressed.push(key); | ||||
|       } | ||||
|       $input.value = pressed.join('+'); | ||||
|       await profileDB.set([mod.id, opt.key], $input.value); | ||||
|       notifications.onChange(); | ||||
|     }); | ||||
|     return web.render($label, $input, $icon); | ||||
|   }, | ||||
| }; | ||||
| @ -1,69 +0,0 @@ | ||||
| /* | ||||
|  * notion-enhancer core: menu | ||||
|  * (c) 2021 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/) | ||||
|  * (https://notion-enhancer.github.io/) under the MIT license | ||||
|  */ | ||||
| 
 | ||||
| .enhancer--sidebarMenuLink { | ||||
|   user-select: none; | ||||
|   -webkit-user-select: none; | ||||
|   transition: background 20ms ease-in 0s; | ||||
|   cursor: pointer; | ||||
|   color: var(--theme--text_secondary); | ||||
| } | ||||
| .enhancer--sidebarMenuLink:hover { | ||||
|   background: var(--theme--ui_interactive-hover); | ||||
| } | ||||
| .enhancer--sidebarMenuLink svg { | ||||
|   width: 16px; | ||||
|   height: 16px; | ||||
|   margin-left: 2px; | ||||
| } | ||||
| .enhancer--sidebarMenuLink > div { | ||||
|   display: flex; | ||||
|   align-items: center; | ||||
|   min-height: 27px; | ||||
|   font-size: 14px; | ||||
|   padding: 2px 14px; | ||||
|   width: 100%; | ||||
| } | ||||
| .enhancer--sidebarMenuLink > div > :first-child { | ||||
|   flex-shrink: 0; | ||||
|   flex-grow: 0; | ||||
|   border-radius: 3px; | ||||
|   width: 22px; | ||||
|   height: 22px; | ||||
|   display: flex; | ||||
|   align-items: center; | ||||
|   justify-content: center; | ||||
|   margin-right: 8px; | ||||
| } | ||||
| .enhancer--sidebarMenuLink > div > :nth-child(2) { | ||||
|   flex: 1 1 auto; | ||||
|   white-space: nowrap; | ||||
|   overflow: hidden; | ||||
|   text-overflow: ellipsis; | ||||
| } | ||||
| 
 | ||||
| .enhancer--sidebarMenuLink[data-has-notifications] { | ||||
|   color: var(--theme--text); | ||||
| } | ||||
| .enhancer--sidebarMenuLink > div > .enhancer--notificationBubble { | ||||
|   display: flex; | ||||
| } | ||||
| .enhancer--sidebarMenuLink > div > .enhancer--notificationBubble > div { | ||||
|   display: inline-flex; | ||||
|   align-items: center; | ||||
|   justify-content: center; | ||||
|   width: 16px; | ||||
|   height: 16px; | ||||
|   font-size: 10px; | ||||
|   font-weight: 600; | ||||
|   border-radius: 3px; | ||||
|   color: var(--theme--accent_red-text); | ||||
|   background: var(--theme--accent_red); | ||||
| } | ||||
| .enhancer--sidebarMenuLink > div > .enhancer--notificationBubble > div > span { | ||||
|   margin-bottom: 1px; | ||||
|   margin-left: -0.5px; | ||||
| } | ||||
| @ -1,59 +0,0 @@ | ||||
| /* | ||||
|  * notion-enhancer core: menu | ||||
|  * (c) 2021 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
 | ||||
|  * (https://notion-enhancer.github.io/) under the MIT license
 | ||||
|  */ | ||||
| 
 | ||||
| 'use strict'; | ||||
| 
 | ||||
| export default async function (api, db) { | ||||
|   const { env, fs, storage, registry, web } = api; | ||||
| 
 | ||||
|   web.addHotkeyListener(await db.get(['hotkey']), env.focusMenu); | ||||
| 
 | ||||
|   const updateTheme = () => | ||||
|     storage.set(['theme'], document.querySelector('.notion-dark-theme') ? 'dark' : 'light'); | ||||
|   web.addDocumentObserver((mutation) => { | ||||
|     if (mutation.target === document.body && document.hasFocus()) updateTheme(); | ||||
|   }); | ||||
|   if (document.hasFocus()) updateTheme(); | ||||
|   document.addEventListener('visibilitychange', updateTheme); | ||||
| 
 | ||||
|   const sidebarSelector = '.notion-sidebar-container .notion-sidebar > div:nth-child(4)'; | ||||
|   await web.whenReady([sidebarSelector]); | ||||
| 
 | ||||
|   const $sidebarLink = web.html`<div class="enhancer--sidebarMenuLink" role="button" tabindex="0">
 | ||||
|       <div> | ||||
|         <div>${await fs.getText('icon/colour.svg')}</div> | ||||
|         <div><div>notion-enhancer</div></div> | ||||
|       </div> | ||||
|     </div>`; | ||||
|   $sidebarLink.addEventListener('click', env.focusMenu); | ||||
| 
 | ||||
|   const notifications = { | ||||
|     cache: await storage.get(['notifications'], []), | ||||
|     provider: [ | ||||
|       registry.welcomeNotification, | ||||
|       ...(await fs.getJSON('https://notion-enhancer.github.io/notifications.json')), | ||||
|     ], | ||||
|     count: (await registry.errors()).length, | ||||
|   }; | ||||
|   for (const notification of notifications.provider) { | ||||
|     if ( | ||||
|       !notifications.cache.includes(notification.id) && | ||||
|       notification.version === env.version && | ||||
|       (!notification.environments || notification.environments.includes(env.name)) | ||||
|     ) { | ||||
|       notifications.count++; | ||||
|     } | ||||
|   } | ||||
|   if (notifications.count) { | ||||
|     $sidebarLink.dataset.hasNotifications = true; | ||||
|     web.render( | ||||
|       $sidebarLink.children[0], | ||||
|       web.html`<div class="enhancer--notificationBubble"><div><span>${notifications.count}</span></div></div>` | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   web.render(document.querySelector(sidebarSelector), $sidebarLink); | ||||
| } | ||||
| @ -1,165 +0,0 @@ | ||||
| /* | ||||
|  * notion-enhancer core: menu | ||||
|  * (c) 2021 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/) | ||||
|  * (https://notion-enhancer.github.io/) under the MIT license | ||||
|  */ | ||||
| 
 | ||||
| .markdown table { | ||||
|   border-spacing: 0; | ||||
|   border: 1px solid var(--theme--ui_divider); | ||||
| } | ||||
| .markdown table th { | ||||
|   text-align: left; | ||||
| } | ||||
| .markdown table th, | ||||
| .markdown table td { | ||||
|   padding: 5px 8px 6px; | ||||
|   border: 1px solid var(--theme--ui_divider); | ||||
| } | ||||
| .markdown h1 { | ||||
|   font-size: 1.875rem; | ||||
|   margin: 1rem 0 0.5rem 0; | ||||
| } | ||||
| .markdown h2 { | ||||
|   font-size: 1.5rem; | ||||
|   margin: 1rem 0 0.5rem 0; | ||||
| } | ||||
| .markdown h3 { | ||||
|   font-size: 1.25rem; | ||||
|   margin: 1rem 0 0.5rem 0; | ||||
| } | ||||
| .markdown ul, | ||||
| .markdown ol { | ||||
|   padding-left: 1.25rem; | ||||
| } | ||||
| .markdown li { | ||||
|   margin: 0.4rem 0; | ||||
| } | ||||
| .markdown ol li { | ||||
|   padding-left: 0.25rem; | ||||
| } | ||||
| .markdown blockquote { | ||||
|   border-left: 2px solid currentColor; | ||||
|   padding-left: 0.75rem; | ||||
|   margin: 0.5rem 0; | ||||
| } | ||||
| .markdown hr { | ||||
|   border: 0.5px solid var(--theme--ui_divider); | ||||
| } | ||||
| .markdown.markdown-inline a { | ||||
|   opacity: 0.7; | ||||
|   text-decoration: none; | ||||
|   border-bottom: 0.05em solid var(--theme--text_secondary); | ||||
| } | ||||
| .markdown.markdown-inline a:hover { | ||||
|   opacity: 0.9; | ||||
| } | ||||
| 
 | ||||
| .markdown :not(pre) > code, | ||||
| .markdown-inline code { | ||||
|   padding: 0.2em 0.4em; | ||||
|   border-radius: 3px; | ||||
|   background: var(--theme--code_inline); | ||||
|   color: var(--theme--code_inline-text); | ||||
| } | ||||
| .markdown pre { | ||||
|   padding: 2em 1.25em; | ||||
|   border-radius: 3px; | ||||
|   tab-size: 2; | ||||
|   white-space: pre; | ||||
|   overflow-x: auto; | ||||
|   background: var(--theme--code); | ||||
|   color: var(--theme--code_plain); | ||||
| } | ||||
| .markdown pre, | ||||
| .markdown.markdown-inline code { | ||||
|   font-family: var(--theme--font_code); | ||||
|   font-size: 0.796875rem; | ||||
|   text-align: left; | ||||
|   word-spacing: normal; | ||||
|   word-break: normal; | ||||
|   word-wrap: normal; | ||||
|   hyphens: none; | ||||
|   line-height: 1.5; | ||||
| } | ||||
| 
 | ||||
| /* | ||||
|  * https://prismjs.com/plugins/inline-color/ | ||||
|  */ | ||||
| .markdown .inline-color-wrapper { | ||||
|   /* | ||||
| 	 * base64 svg (https://stackoverflow.com/a/21626701/7595472 - prevents visual glitches) | ||||
| 	 * <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 2 2"> | ||||
| 	 *   <path fill="gray" d="M0 0h2v2H0z"/> | ||||
| 	 *   <path fill="white" d="M0 0h1v1H0zM1 1h1v1H1z"/> | ||||
| 	 * </svg> | ||||
| 	 */ | ||||
|   background: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyIDIiPjxwYXRoIGZpbGw9ImdyYXkiIGQ9Ik0wIDBoMnYySDB6Ii8+PHBhdGggZmlsbD0id2hpdGUiIGQ9Ik0wIDBoMXYxSDB6TTEgMWgxdjFIMXoiLz48L3N2Zz4='); | ||||
|   background-position: center; | ||||
|   background-size: 110%; | ||||
|   display: inline-block; | ||||
|   height: 1.333ch; | ||||
|   width: 1.333ch; | ||||
|   margin: 0 0.333ch; | ||||
|   box-sizing: border-box; | ||||
|   border: 0.5px solid var(--theme--code_plain); | ||||
|   overflow: hidden; | ||||
| } | ||||
| .markdown .inline-color { | ||||
|   display: block; | ||||
|   height: 120%; | ||||
|   width: 120%; | ||||
| } | ||||
| 
 | ||||
| /* | ||||
|  * https://prismjs.com/plugins/match-braces/ | ||||
|  */ | ||||
| .markdown .token.punctuation.brace-hover, | ||||
| .markdown .token.punctuation.brace-selected { | ||||
|   outline: solid 1px; | ||||
| } | ||||
| 
 | ||||
| /* | ||||
|  * https://prismjs.com/plugins/show-language/ | ||||
|  * https://prismjs.com/plugins/copy-to-clipboard/ | ||||
|  */ | ||||
| .markdown .code-toolbar { | ||||
|   position: relative; | ||||
| } | ||||
| .markdown .code-toolbar .toolbar-item { | ||||
|   position: absolute; | ||||
|   top: 0.35rem; | ||||
|   display: inline-block; | ||||
|   transition: opacity 200ms ease-in-out; | ||||
|   opacity: 0; | ||||
| } | ||||
| .markdown .code-toolbar .toolbar-item:first-child { | ||||
|   left: 0.8rem; | ||||
| } | ||||
| .markdown .code-toolbar .toolbar-item:last-child { | ||||
|   right: 0.8rem; | ||||
| } | ||||
| .markdown .code-toolbar:hover .toolbar-item, | ||||
| .markdown .code-toolbar:focus-within .toolbar-item { | ||||
|   opacity: 1; | ||||
| } | ||||
| .markdown .code-toolbar .toolbar-item > * { | ||||
|   padding: 0.25rem 0.35rem; | ||||
|   color: var(--theme--text_secondary); | ||||
|   font-size: 11px; | ||||
|   font-family: inherit; | ||||
| } | ||||
| .markdown .code-toolbar .toolbar-item .copy-to-clipboard-button { | ||||
|   border: none; | ||||
|   background: none; | ||||
|   cursor: pointer; | ||||
|   border-radius: 3px; | ||||
|   transition: background 100ms ease-in-out; | ||||
| } | ||||
| .markdown .code-toolbar .toolbar-item .copy-to-clipboard-button:hover { | ||||
|   background: var(--theme--button-hover); | ||||
| } | ||||
| .markdown .code-toolbar .toolbar-item .copy-to-clipboard-button svg { | ||||
|   width: 1em; | ||||
|   margin-right: 0.5em; | ||||
| } | ||||
| @ -1,25 +0,0 @@ | ||||
| /* | ||||
|  * notion-enhancer core: menu | ||||
|  * (c) 2021 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/) | ||||
|  * (https://notion-enhancer.github.io/) under the MIT license | ||||
|  */ | ||||
| 
 | ||||
| ::selection { | ||||
|   background: var(--theme--accent_blue-selection); | ||||
| } | ||||
| 
 | ||||
| ::-webkit-scrollbar { | ||||
|   width: 10px; | ||||
|   height: 10px; | ||||
|   background: transparent; | ||||
| } | ||||
| ::-webkit-scrollbar-track, | ||||
| ::-webkit-scrollbar-corner { | ||||
|   background: var(--theme--scrollbar_track) !important; | ||||
| } | ||||
| ::-webkit-scrollbar-thumb { | ||||
|   background: var(--theme--scrollbar_thumb) !important; | ||||
| } | ||||
| ::-webkit-scrollbar-thumb:hover { | ||||
|   background: var(--theme--scrollbar_thumb-hover) !important; | ||||
| } | ||||
| @ -1,11 +0,0 @@ | ||||
| <!DOCTYPE html> | ||||
| <html lang="en"> | ||||
|   <head> | ||||
|     <meta charset="UTF-8" /> | ||||
|     <meta name="viewport" content="width=device-width, initial-scale=1.0" /> | ||||
|     <title>notion-enhancer menu</title> | ||||
|   </head> | ||||
|   <body> | ||||
|     <script src="./menu.mjs" type="module"></script> | ||||
|   </body> | ||||
| </html> | ||||
| @ -1,370 +0,0 @@ | ||||
| /* | ||||
|  * notion-enhancer core: menu | ||||
|  * (c) 2021 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
 | ||||
|  * (https://notion-enhancer.github.io/) under the MIT license
 | ||||
|  */ | ||||
| 
 | ||||
| 'use strict'; | ||||
| 
 | ||||
| import { env, fs, storage, registry, web, components } from '../../api/_.mjs'; | ||||
| import { notifications } from './notifications.mjs'; | ||||
| import { blocks, options } from './blocks.mjs'; | ||||
| import './styles.mjs'; | ||||
| 
 | ||||
| const db = await registry.db('a6621988-551d-495a-97d8-3c568bca2e9e'), | ||||
|   profileName = await registry.profileName(), | ||||
|   profileDB = await registry.profileDB(); | ||||
| 
 | ||||
| for (const mod of await registry.list((mod) => registry.enabled(mod.id))) { | ||||
|   for (const sheet of mod.css?.menu || []) { | ||||
|     web.loadStylesheet(`repo/${mod._dir}/${sheet}`); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| web.addHotkeyListener(await db.get(['hotkey']), env.focusNotion); | ||||
| 
 | ||||
| const loadTheme = async () => { | ||||
|   document.documentElement.className = | ||||
|     (await storage.get(['theme'], 'light')) === 'dark' ? 'dark' : ''; | ||||
| }; | ||||
| document.addEventListener('visibilitychange', loadTheme); | ||||
| loadTheme(); | ||||
| 
 | ||||
| window.addEventListener('beforeunload', (event) => { | ||||
|   // trigger input save
 | ||||
|   document.activeElement.blur(); | ||||
| }); | ||||
| 
 | ||||
| const $main = web.html`<main class="main"></main>`, | ||||
|   $sidebar = web.html`<article class="sidebar"></article>`, | ||||
|   $options = web.html`<div class="options-container">
 | ||||
|     <p class="options-placeholder">Select a mod to view and configure its options.</p> | ||||
|   </div>`, | ||||
|   $profile = web.html`<button class="profile-trigger">
 | ||||
|     Profile: ${web.escape(profileName)} | ||||
|   </button>`; | ||||
| 
 | ||||
| let _$profileConfig; | ||||
| $profile.addEventListener('click', async (event) => { | ||||
|   for (const $selected of document.querySelectorAll('.mod-selected')) { | ||||
|     $selected.className = 'mod'; | ||||
|   } | ||||
|   if (!_$profileConfig) { | ||||
|     const profileNames = [ | ||||
|         ...new Set([ | ||||
|           ...Object.keys(await storage.get(['profiles'], { default: {} })), | ||||
|           profileName, | ||||
|         ]), | ||||
|       ], | ||||
|       $options = profileNames.map( | ||||
|         (profile) => web.raw`<option
 | ||||
|           class="select-option" | ||||
|           value="${web.escape(profile)}" | ||||
|           ${profile === profileName ? 'selected' : ''} | ||||
|         >${web.escape(profile)}</option>` | ||||
|       ), | ||||
|       $select = web.html`<select class="input">
 | ||||
|         <option class="select-option" value="--">-- new --</option> | ||||
|         ${$options.join('')} | ||||
|       </select>`, | ||||
|       $edit = web.html`<input
 | ||||
|         type="text" | ||||
|         class="input" | ||||
|         value="${web.escape(profileName)}" | ||||
|         pattern="/^[A-Za-z0-9_-]+$/" | ||||
|       >`,
 | ||||
|       $export = web.html`<button class="profile-export">
 | ||||
|         ${await components.feather('download', { class: 'profile-icon-action' })} | ||||
|       </button>`, | ||||
|       $import = web.html`<label class="profile-import">
 | ||||
|         <input type="file" class="hidden" accept="application/json"> | ||||
|         ${await components.feather('upload', { class: 'profile-icon-action' })} | ||||
|       </label>`, | ||||
|       $save = web.html`<button class="profile-save">
 | ||||
|         ${await components.feather('save', { class: 'profile-icon-text' })} Save | ||||
|       </button>`, | ||||
|       $delete = web.html`<button class="profile-delete">
 | ||||
|         ${await components.feather('trash-2', { class: 'profile-icon-text' })} Delete | ||||
|       </button>`, | ||||
|       $error = web.html`<p class="profile-error"></p>`; | ||||
|     $export.addEventListener('click', async (event) => { | ||||
|       const now = new Date(), | ||||
|         $a = web.html`<a
 | ||||
|           class="hidden" | ||||
|           download="notion-enhancer_${web.escape($select.value)}_${now.getFullYear()}-${ | ||||
|           now.getMonth() + 1 | ||||
|         }-${now.getDate()}.json" | ||||
|           href="data:text/plain;charset=utf-8,${encodeURIComponent( | ||||
|             JSON.stringify(await storage.get(['profiles', $select.value], {}), null, 2) | ||||
|           )}" | ||||
|         ></a>`; | ||||
|       web.render(document.body, $a); | ||||
|       $a.click(); | ||||
|       $a.remove(); | ||||
|     }); | ||||
|     $import.addEventListener('change', (event) => { | ||||
|       const file = event.target.files[0], | ||||
|         reader = new FileReader(); | ||||
|       reader.onload = async (progress) => { | ||||
|         try { | ||||
|           const profileUpload = JSON.parse(progress.currentTarget.result); | ||||
|           if (!profileUpload) throw Error; | ||||
|           await storage.set(['profiles', $select.value], profileUpload); | ||||
|           env.reload(); | ||||
|         } catch { | ||||
|           web.render(web.empty($error), 'Invalid JSON uploaded.'); | ||||
|         } | ||||
|       }; | ||||
|       reader.readAsText(file); | ||||
|     }); | ||||
|     $select.addEventListener('change', async (event) => { | ||||
|       if ($select.value === '--') { | ||||
|         $edit.value = ''; | ||||
|       } else $edit.value = $select.value; | ||||
|     }); | ||||
|     $save.addEventListener('click', async (event) => { | ||||
|       if (profileNames.includes($edit.value) && $select.value !== $edit.value) { | ||||
|         web.render( | ||||
|           web.empty($error), | ||||
|           `The profile "${web.escape($edit.value)}" already exists.` | ||||
|         ); | ||||
|         return false; | ||||
|       } | ||||
|       if (!$edit.value) { | ||||
|         web.render(web.empty($error), 'Profile names cannot be empty.'); | ||||
|         return false; | ||||
|       } | ||||
|       if (!$edit.value.match(/^[A-Za-z0-9_-]+$/)) { | ||||
|         web.render( | ||||
|           web.empty($error), | ||||
|           'Profile names can only contain letters, numbers, dashes and underscores.' | ||||
|         ); | ||||
|         return false; | ||||
|       } | ||||
|       await storage.set(['currentprofile'], $edit.value); | ||||
|       if ($select.value === '--') { | ||||
|         await storage.set(['profiles', $edit.value], {}); | ||||
|       } else if ($select.value !== $edit.value) { | ||||
|         await storage.set( | ||||
|           ['profiles', $edit.value], | ||||
|           await storage.get(['profiles', $select.value], {}) | ||||
|         ); | ||||
|         await storage.set(['profiles', $select.value], undefined); | ||||
|       } | ||||
|       env.reload(); | ||||
|     }); | ||||
|     $delete.addEventListener('click', async (event) => { | ||||
|       await storage.set(['profiles', $select.value], undefined); | ||||
|       await storage.set( | ||||
|         ['currentprofile'], | ||||
|         profileNames.find((profile) => profile !== $select.value) || 'default' | ||||
|       ); | ||||
|       env.reload(); | ||||
|     }); | ||||
| 
 | ||||
|     _$profileConfig = web.render( | ||||
|       web.html`<div></div>`, | ||||
|       web.html`<p class="options-placeholder">
 | ||||
|         Profiles are used to switch entire configurations. | ||||
|         Here they can be selected, renamed or deleted. | ||||
|         Profile names can only contain letters, numbers, | ||||
|         dashes and underscores. <br> | ||||
|         Be careful - deleting a profile deletes all configuration | ||||
|         related to it.  | ||||
|       </p>`, | ||||
|       web.render( | ||||
|         web.html`<label class="input-label"></label>`, | ||||
|         $select, | ||||
|         web.html`${await components.feather('chevron-down', { class: 'input-icon' })}` | ||||
|       ), | ||||
|       web.render( | ||||
|         web.html`<label class="input-label"></label>`, | ||||
|         $edit, | ||||
|         web.html`${await components.feather('type', { class: 'input-icon' })}` | ||||
|       ), | ||||
|       web.render(web.html`<p class="profile-actions"></p>`, $export, $import, $save, $delete), | ||||
|       $error | ||||
|     ); | ||||
|   } | ||||
|   web.render(web.empty($options), _$profileConfig); | ||||
| }); | ||||
| 
 | ||||
| const _$modListCache = {}, | ||||
|   generators = { | ||||
|     options: async (mod) => { | ||||
|       const $fragment = document.createDocumentFragment(); | ||||
|       for (const opt of mod.options) { | ||||
|         web.render($fragment, await options[opt.type](mod, opt)); | ||||
|       } | ||||
|       if (!mod.options.length) { | ||||
|         web.render($fragment, web.html`<p class="options-placeholder">No options.</p>`); | ||||
|       } | ||||
|       return $fragment; | ||||
|     }, | ||||
|     mod: async (mod) => { | ||||
|       const $mod = web.html`<div class="mod" data-id="${web.escape(mod.id)}"></div>`, | ||||
|         $toggle = blocks.toggle('', await registry.enabled(mod.id)); | ||||
|       $toggle.addEventListener('change', async (event) => { | ||||
|         if (event.target.checked && mod.tags.includes('theme')) { | ||||
|           const mode = mod.tags.includes('light') ? 'light' : 'dark', | ||||
|             id = mod.id, | ||||
|             mods = await registry.list( | ||||
|               (mod) => | ||||
|                 mod.environments.includes(env.name) && | ||||
|                 mod.tags.includes('theme') && | ||||
|                 mod.tags.includes(mode) && | ||||
|                 mod.id !== id | ||||
|             ); | ||||
|           for (const mod of mods) { | ||||
|             profileDB.set(['_mods', mod.id], false); | ||||
|             document.querySelector( | ||||
|               `[data-id="${web.escape(mod.id)}"] .toggle-check` | ||||
|             ).checked = false; | ||||
|           } | ||||
|         } | ||||
|         profileDB.set(['_mods', mod.id], event.target.checked); | ||||
|         notifications.onChange(); | ||||
|       }); | ||||
|       $mod.addEventListener('click', async (event) => { | ||||
|         if ($mod.className === 'mod-selected') return; | ||||
|         for (const $selected of document.querySelectorAll('.mod-selected')) { | ||||
|           $selected.className = 'mod'; | ||||
|         } | ||||
|         $mod.className = 'mod-selected'; | ||||
|         const fragment = [ | ||||
|           web.render(blocks.title(mod.name), blocks.version(mod.version)), | ||||
|           blocks.tags(mod.tags), | ||||
|           await generators.options(mod), | ||||
|         ]; | ||||
|         web.render(web.empty($options), ...fragment); | ||||
|       }); | ||||
|       return web.render( | ||||
|         web.html`<article class="mod-container"></article>`, | ||||
|         web.render( | ||||
|           $mod, | ||||
|           mod.preview | ||||
|             ? blocks.preview( | ||||
|                 mod.preview.startsWith('http') | ||||
|                   ? mod.preview | ||||
|                   : fs.localPath(`repo/${mod._dir}/${mod.preview}`) | ||||
|               ) | ||||
|             : '', | ||||
|           web.render( | ||||
|             web.html`<div class="mod-body"></div>`, | ||||
|             web.render(blocks.title(mod.name), blocks.version(mod.version)), | ||||
|             blocks.tags(mod.tags), | ||||
|             blocks.description(mod.description), | ||||
|             blocks.authors(mod.authors), | ||||
|             mod.environments.includes(env.name) && !registry.core.includes(mod.id) | ||||
|               ? $toggle | ||||
|               : '' | ||||
|           ) | ||||
|         ) | ||||
|       ); | ||||
|     }, | ||||
|     modList: async (category, message = '') => { | ||||
|       if (!_$modListCache[category]) { | ||||
|         const $search = web.html`<input type="search" class="search"
 | ||||
|           placeholder="Search ('/' to focus)">`,
 | ||||
|           $list = web.html`<div class="mods-list"></div>`, | ||||
|           mods = await registry.list( | ||||
|             (mod) => mod.environments.includes(env.name) && mod.tags.includes(category) | ||||
|           ); | ||||
|         web.addHotkeyListener(['/'], () => $search.focus()); | ||||
|         $search.addEventListener('input', (event) => { | ||||
|           const query = $search.value.toLowerCase(); | ||||
|           for (const $mod of $list.children) { | ||||
|             const matches = !query || $mod.innerText.toLowerCase().includes(query); | ||||
|             $mod.classList[matches ? 'remove' : 'add']('hidden'); | ||||
|           } | ||||
|         }); | ||||
|         for (const mod of mods) { | ||||
|           mod.tags = mod.tags.filter((tag) => tag !== category); | ||||
|           web.render($list, await generators.mod(mod)); | ||||
|           mod.tags.unshift(category); | ||||
|         } | ||||
|         _$modListCache[category] = web.render( | ||||
|           web.html`<div></div>`, | ||||
|           web.render( | ||||
|             web.html`<label class="search-container"></label>`, | ||||
|             $search, | ||||
|             web.html`${await components.feather('search', { class: 'input-icon' })}` | ||||
|           ), | ||||
|           message ? web.html`<p class="main-message">${web.escape(message)}</p>` : '', | ||||
|           $list | ||||
|         ); | ||||
|       } | ||||
|       return _$modListCache[category]; | ||||
|     }, | ||||
|   }; | ||||
| 
 | ||||
| const $notionNavItem = web.html`<h1 class="nav-notion">
 | ||||
|     ${(await fs.getText('icon/colour.svg')).replace( | ||||
|       /width="\d+" height="\d+"/, | ||||
|       `class="nav-notion-icon"` | ||||
|     )} | ||||
|     <span>notion-enhancer</span> | ||||
|   </h1>`; | ||||
| $notionNavItem.addEventListener('click', env.focusNotion); | ||||
| 
 | ||||
| const $coreNavItem = web.html`<a href="?view=core" class="nav-item">core</a>`, | ||||
|   $extensionsNavItem = web.html`<a href="?view=extensions" class="nav-item">extensions</a>`, | ||||
|   $themesNavItem = web.html`<a href="?view=themes" class="nav-item">themes</a>`; | ||||
| 
 | ||||
| web.render( | ||||
|   document.body, | ||||
|   web.render( | ||||
|     web.html`<div class="body-container"></div>`, | ||||
|     web.render( | ||||
|       web.html`<div class="content-container"></div>`, | ||||
|       web.render( | ||||
|         web.html`<nav class="nav"></nav>`, | ||||
|         $notionNavItem, | ||||
|         $coreNavItem, | ||||
|         $extensionsNavItem, | ||||
|         $themesNavItem, | ||||
|         web.html`<a href="https://notion-enhancer.github.io" class="nav-item">docs</a>`, | ||||
|         web.html`<a href="https://discord.gg/sFWPXtA" class="nav-item">community</a>` | ||||
|       ), | ||||
|       $main | ||||
|     ), | ||||
|     web.render($sidebar, $profile, $options) | ||||
|   ) | ||||
| ); | ||||
| 
 | ||||
| function selectNavItem($item) { | ||||
|   for (const $selected of document.querySelectorAll('.nav-item-selected')) { | ||||
|     $selected.className = 'nav-item'; | ||||
|   } | ||||
|   $item.className = 'nav-item-selected'; | ||||
| } | ||||
| 
 | ||||
| import * as router from './router.mjs'; | ||||
| 
 | ||||
| router.addView('core', async () => { | ||||
|   web.empty($main); | ||||
|   selectNavItem($coreNavItem); | ||||
|   return web.render($main, await generators.modList('core')); | ||||
| }); | ||||
| 
 | ||||
| router.addView('extensions', async () => { | ||||
|   web.empty($main); | ||||
|   selectNavItem($extensionsNavItem); | ||||
|   return web.render($main, await generators.modList('extension')); | ||||
| }); | ||||
| 
 | ||||
| router.addView('themes', async () => { | ||||
|   web.empty($main); | ||||
|   selectNavItem($themesNavItem); | ||||
|   return web.render( | ||||
|     $main, | ||||
|     await generators.modList( | ||||
|       'theme', | ||||
|       `Dark themes will only work when Notion is in dark mode,
 | ||||
|       and light themes will only work when Notion is in light mode. | ||||
|       Only one theme of each mode can be enabled at a time.` | ||||
|     ) | ||||
|   ); | ||||
| }); | ||||
| 
 | ||||
| router.loadView('extensions', $main); | ||||
| @ -1,31 +0,0 @@ | ||||
| { | ||||
|   "name": "menu", | ||||
|   "id": "a6621988-551d-495a-97d8-3c568bca2e9e", | ||||
|   "version": "0.11.0", | ||||
|   "description": "the enhancer's graphical menu, related buttons and shortcuts.", | ||||
|   "tags": ["core"], | ||||
|   "authors": [ | ||||
|     { | ||||
|       "name": "dragonwocky", | ||||
|       "email": "thedragonring.bod@gmail.com", | ||||
|       "homepage": "https://dragonwocky.me/", | ||||
|       "avatar": "https://dragonwocky.me/avatar.jpg" | ||||
|     } | ||||
|   ], | ||||
|   "css": { | ||||
|     "client": ["client.css"], | ||||
|     "menu": ["menu.css", "markdown.css"] | ||||
|   }, | ||||
|   "js": { | ||||
|     "client": ["client.mjs"] | ||||
|   }, | ||||
|   "options": [ | ||||
|     { | ||||
|       "type": "hotkey", | ||||
|       "key": "hotkey", | ||||
|       "label": "toggle focus hotkey", | ||||
|       "value": "Ctrl+Alt+E", | ||||
|       "tooltip": "switches between notion & the enhancer menu" | ||||
|     } | ||||
|   ] | ||||
| } | ||||
| @ -1,86 +0,0 @@ | ||||
| /* | ||||
|  * notion-enhancer core: menu | ||||
|  * (c) 2021 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
 | ||||
|  * (https://notion-enhancer.github.io/) under the MIT license
 | ||||
|  */ | ||||
| 
 | ||||
| 'use strict'; | ||||
| 
 | ||||
| import { env, fs, storage, fmt, registry, web, components } from '../../api/_.mjs'; | ||||
| import { tw } from './styles.mjs'; | ||||
| 
 | ||||
| export const notifications = { | ||||
|   $container: web.html`<div class="notifications-container"></div>`, | ||||
|   cache: await storage.get(['notifications'], []), | ||||
|   provider: [ | ||||
|     registry.welcomeNotification, | ||||
|     ...(await fs.getJSON('https://notion-enhancer.github.io/notifications.json')), | ||||
|   ], | ||||
|   async add({ icon, message, id = undefined, color = undefined, link = undefined }) { | ||||
|     const $notification = link | ||||
|         ? web.html`<a
 | ||||
|           href="${web.escape(link)}" | ||||
|           class="${tw`notification-${color || 'default'}`}" | ||||
|           role="alert" | ||||
|           target="_blank" | ||||
|         ></a>` | ||||
|         : web.html`<p
 | ||||
|           class="${tw`notification-${color || 'default'}`}" | ||||
|           role="alert" | ||||
|           tabindex="0" | ||||
|         ></p>`, | ||||
|       resolve = async () => { | ||||
|         if (id !== undefined) { | ||||
|           notifications.cache.push(id); | ||||
|           await storage.set(['notifications'], notifications.cache); | ||||
|         } | ||||
|         $notification.remove(); | ||||
|       }; | ||||
|     $notification.addEventListener('click', resolve); | ||||
|     $notification.addEventListener('keyup', (event) => { | ||||
|       if (['Enter', ' '].includes(event.key)) resolve(); | ||||
|     }); | ||||
|     web.render( | ||||
|       notifications.$container, | ||||
|       web.render( | ||||
|         $notification, | ||||
|         web.html`<span class="notification-text markdown-inline">
 | ||||
|           ${fmt.md.renderInline(message)} | ||||
|         </span>`, | ||||
|         web.html`${await components.feather(icon, { class: 'notification-icon' })}` | ||||
|       ) | ||||
|     ); | ||||
|     return $notification; | ||||
|   }, | ||||
|   _onChange: false, | ||||
|   async onChange() { | ||||
|     if (this._onChange) return; | ||||
|     this._onChange = true; | ||||
|     const $notification = await this.add({ | ||||
|       icon: 'refresh-cw', | ||||
|       message: 'Reload to apply changes.', | ||||
|     }); | ||||
|     $notification.addEventListener('click', env.reload); | ||||
|   }, | ||||
| }; | ||||
| web.render(document.body, notifications.$container); | ||||
| for (const notification of notifications.provider) { | ||||
|   if ( | ||||
|     !notifications.cache.includes(notification.id) && | ||||
|     notification.version === env.version && | ||||
|     (!notification.environments || notification.environments.includes(env.name)) | ||||
|   ) { | ||||
|     notifications.add(notification); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| const errors = await registry.errors(); | ||||
| if (errors.length) { | ||||
|   console.log('[notion-enhancer] registry errors:'); | ||||
|   console.table(errors); | ||||
|   notifications.add({ | ||||
|     icon: 'alert-circle', | ||||
|     message: 'Failed to load mods (check console).', | ||||
|     color: 'red', | ||||
|   }); | ||||
| } | ||||
| @ -1,72 +0,0 @@ | ||||
| /* | ||||
|  * notion-enhancer core: menu | ||||
|  * (c) 2021 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
 | ||||
|  * (https://notion-enhancer.github.io/) under the MIT license
 | ||||
|  */ | ||||
| 
 | ||||
| 'use strict'; | ||||
| 
 | ||||
| import { web } from '../../api/_.mjs'; | ||||
| 
 | ||||
| let _defaultView = ''; | ||||
| const _views = new Map(); | ||||
| 
 | ||||
| export function addView(name, loadFunc) { | ||||
|   _views.set(name, loadFunc); | ||||
| } | ||||
| export function removeView(name) { | ||||
|   _views.delete(name); | ||||
| } | ||||
| 
 | ||||
| function router(event) { | ||||
|   event.preventDefault(); | ||||
|   const anchor = event.path | ||||
|     ? event.path.find((anchor) => anchor.nodeName === 'A') | ||||
|     : event.target; | ||||
|   if (location.search !== anchor.getAttribute('href')) { | ||||
|     window.history.pushState(null, null, anchor.href); | ||||
|     loadView(); | ||||
|   } | ||||
| } | ||||
| function navigator(event) { | ||||
|   event.preventDefault(); | ||||
|   const anchor = event.path | ||||
|       ? event.path.find((anchor) => anchor.nodeName === 'A') | ||||
|       : event.target, | ||||
|     hash = anchor.getAttribute('href').slice(1); | ||||
|   document.getElementById(hash).scrollIntoView(true); | ||||
|   document.documentElement.scrollTop = 0; | ||||
|   history.replaceState({ search: location.search, hash }, null, `#${hash}`); | ||||
| } | ||||
| 
 | ||||
| export async function loadView(defaultView = null) { | ||||
|   if (defaultView) _defaultView = defaultView; | ||||
|   if (!_defaultView) throw new Error('no view root set.'); | ||||
| 
 | ||||
|   const query = web.queryParams(), | ||||
|     fallbackView = () => { | ||||
|       window.history.replaceState(null, null, `?view=${_defaultView}`); | ||||
|       return loadView(); | ||||
|     }; | ||||
|   if (!query.get('view') || document.body.dataset.view !== query.get('view')) { | ||||
|     if (_views.get(query.get('view'))) { | ||||
|       await _views.get(query.get('view'))(); | ||||
|     } else return fallbackView(); | ||||
|   } else return fallbackView(); | ||||
| } | ||||
| 
 | ||||
| window.addEventListener('popstate', (event) => { | ||||
|   if (event.state) loadView(); | ||||
|   document.getElementById(location.hash.slice(1))?.scrollIntoView(true); | ||||
|   document.documentElement.scrollTop = 0; | ||||
| }); | ||||
| web.addDocumentObserver((mutation) => { | ||||
|   mutation.target.querySelectorAll('a[href^="?"]').forEach((a) => { | ||||
|     a.removeEventListener('click', router); | ||||
|     a.addEventListener('click', router); | ||||
|   }); | ||||
|   mutation.target.querySelectorAll('a[href^="#"]').forEach((a) => { | ||||
|     a.removeEventListener('click', navigator); | ||||
|     a.addEventListener('click', navigator); | ||||
|   }); | ||||
| }); | ||||
| @ -1,157 +0,0 @@ | ||||
| /* | ||||
|  * notion-enhancer core: menu | ||||
|  * (c) 2021 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
 | ||||
|  * (https://notion-enhancer.github.io/) under the MIT license
 | ||||
|  */ | ||||
| 
 | ||||
| 'use strict'; | ||||
| 
 | ||||
| // css-in-js for better component generation
 | ||||
| 
 | ||||
| import { tw, apply, setup } from '../../dep/twind.mjs'; | ||||
| import { content } from '../../dep/twind-content.mjs'; | ||||
| const pseudoContent = content('""'); | ||||
| 
 | ||||
| const mapColorVariables = (color) => ({ | ||||
|   'text': `var(--theme--text_${color})`, | ||||
|   'highlight': `var(--theme--highlight_${color})`, | ||||
|   'highlight-text': `var(--theme--highlight_${color}-text)`, | ||||
|   'block': `var(--theme--block_${color})`, | ||||
|   'block-text': `var(--theme--block_${color}-text)`, | ||||
|   'tag': `var(--theme--tag_${color})`, | ||||
|   'tag-text': `var(--theme--tag_${color}-text)`, | ||||
|   'callout': `var(--theme--callout_${color})`, | ||||
|   'callout-text': `var(--theme--callout_${color}-text)`, | ||||
| }); | ||||
| 
 | ||||
| const customClasses = { | ||||
|   'notifications-container': apply`absolute bottom-0 right-0 px-4 py-3 max-w-full w-96`, | ||||
|   'notification': ([color = 'default']) => | ||||
|     apply`p-2 ${ | ||||
|       color === 'default' | ||||
|         ? 'bg-tag text-tag-text hover:bg-interactive-hover border border-divider' | ||||
|         : `bg-${color}-tag text-${color}-tag-text border border-${color}-text hover:bg-${color}-text` | ||||
|     } flex items-center rounded-full mt-3 shadow-md cursor-pointer`,
 | ||||
|   'notification-text': apply`font-semibold text-xs mx-2 flex-auto`, | ||||
|   'notification-icon': apply`fill-current opacity-75 h-4 w-4 mx-2`, | ||||
|   'body-container': apply`flex w-full h-full overflow-hidden`, | ||||
|   'content-container': apply`h-full w-full-96`, | ||||
|   'nav': apply`px-4 py-3 flex flex-wrap items-center border-b border-divider h-64 sm:h-48 md:h-32 lg:h-16`, | ||||
|   'nav-notion': apply`flex items-center font-semibold text-xl cursor-pointer select-none mr-4
 | ||||
|       ml-4 sm:mb-4 md:w-full lg:(w-auto ml-0 mb-0)`,
 | ||||
|   'nav-notion-icon': apply`h-12 w-12 mr-5 sm:(h-6 w-6 mr-3)`, | ||||
|   'nav-item': apply`ml-4 px-3 py-2 rounded-md text-sm font-medium hover:bg-interactive-hover focus:bg-interactive-active`, | ||||
|   'nav-item-selected': apply`ml-4 px-3 py-2 rounded-md text-sm font-medium ring-1 ring-divider bg-notion-secondary`, | ||||
|   'main': apply`transition px-4 py-3 overflow-y-auto max-h-full-64 sm:max-h-full-48 md:max-h-full-32 lg:max-h-full-16`, | ||||
|   'main-message': apply`mx-2.5 my-2.5 px-px text-sm text-foreground-secondary text-justify`, | ||||
|   'mods-list': apply`flex flex-wrap`, | ||||
|   'mod-container': apply`w-full md:w-1/2 lg:w-1/3 xl:w-1/4 2xl:w-1/5 px-2.5 py-2.5 box-border`, | ||||
|   'mod': apply`relative h-full w-full flex flex-col overflow-hidden rounded-lg shadow-lg
 | ||||
|     bg-notion-secondary border border-divider cursor-pointer`,
 | ||||
|   'mod-selected': apply`mod ring ring-accent-blue-focus`, | ||||
|   'mod-body': apply`px-4 py-3 flex flex-col flex-auto children:cursor-pointer`, | ||||
|   'mod-preview': apply`object-cover w-full h-32`, | ||||
|   'mod-title': apply`mb-2 text-xl font-semibold tracking-tight flex items-center`, | ||||
|   'mod-version': apply`mt-px ml-3 p-1 font-normal text-xs leading-none bg-tag text-tag-text rounded`, | ||||
|   'mod-tags': apply`text-foreground-secondary mb-2 text-xs`, | ||||
|   'mod-description': apply`mb-2 text-sm`, | ||||
|   'mod-authors-container': apply`text-sm font-medium`, | ||||
|   'mod-author': apply`flex items-center mb-2`, | ||||
|   'mod-author-avatar': apply`inline object-cover w-5 h-5 rounded-full mr-2`, | ||||
|   'sidebar': apply`h-full w-96 px-4 pt-3 pb-32 flex flex-col bg-notion-secondary border-l border-divider`, | ||||
|   'profile-trigger': apply`block px-4 py-3 mb-2 rounded-md text-sm text-left font-semibold shadow-inner
 | ||||
|     bg-accent-red-hover border border-accent-red text-accent-red focus:(outline-none ring ring-inset ring-accent-red)`,
 | ||||
|   'profile-actions': apply`flex`, | ||||
|   'profile-save': apply`text-sm px-3 py-2 font-medium mt-2 bg-accent-blue text-accent-blue-text rounded-md flex-grow
 | ||||
|     hover:bg-accent-blue-hover focus:(bg-accent-blue-focus outline-none) text-center`,
 | ||||
|   'profile-delete': apply`text-sm px-3 py-2 font-medium ml-3 mt-2 bg-red-tag text-red-tag-text rounded-md flex-grow
 | ||||
|     border border-red-text hover:bg-red-text focus:(outline-none bg-red-text) text-center`,
 | ||||
|   'profile-export': apply`profile-save mr-2`, | ||||
|   'profile-import': apply`profile-save mr-2`, | ||||
|   'profile-error': apply`text-xs mt-2 text-red-text`, | ||||
|   'profile-icon-action': apply`w-4 h-4 -mt-1 inline-block`, | ||||
|   'profile-icon-text': apply`w-4 h-4 -mt-1 inline-block mr-1`, | ||||
|   'options-container': apply`px-4 py-3 shadow-inner rounded-lg bg-notion border border-divider space-y-3`, | ||||
|   'options-placeholder': apply`text-sm text-foreground-secondary`, | ||||
|   'toggle-box': apply`w-9 h-5 p-0.5 flex items-center bg-toggle-off rounded-full duration-300 ease-in-out cursor-pointer`, | ||||
|   'toggle-label': apply`relative text-sm flex w-full mt-auto`, | ||||
|   'toggle-check': apply`appearance-none ml-auto checked:sibling:(bg-toggle-on after::translate-x-4)`, | ||||
|   'toggle-feature': apply`after::(${pseudoContent} w-4 h-4 bg-toggle-feature rounded-full duration-300) cursor-pointer`, | ||||
|   'input-label': apply`block text-sm mt-2 relative`, | ||||
|   'input': apply`transition block w-full mt-2 pl-3 pr-14 py-2 text-sm rounded-md flex bg-input text-foreground
 | ||||
|     appearance-none placeholder-foreground-secondary ring-1 ring-divider focus:(outline-none ring ring-accent-blue-focus)`,
 | ||||
|   'input-tooltip': apply`h-4 w-4 -mt-1 inline-block mr-2`, | ||||
|   'input-icon': apply`absolute w-11 h-9 right-0 bottom-0 py-2 px-3 bg-notion-secondary rounded-r-md text-icon`, | ||||
|   'input-placeholder': apply`text-foreground-secondary`, | ||||
|   'select-option': apply`bg-notion-secondary`, | ||||
|   'file-latest': apply`block w-full text-left text-foreground-secondary text-xs mt-2 hover:line-through cursor-pointer`, | ||||
|   'search-container': apply`block mx-2.5 my-2.5 relative`, | ||||
|   'search': apply`input pr-12`, | ||||
| }; | ||||
| 
 | ||||
| setup({ | ||||
|   preflight: { | ||||
|     html: apply`w-full h-full`, | ||||
|     body: apply`w-full h-full bg-notion font-sans text-foreground`, | ||||
|   }, | ||||
|   theme: { | ||||
|     fontFamily: { | ||||
|       sans: ['var(--theme--font_sans)'], | ||||
|       mono: ['var(--theme--font_mono)'], | ||||
|     }, | ||||
|     colors: { | ||||
|       'notion': 'var(--theme--bg)', | ||||
|       'notion-secondary': 'var(--theme--bg_secondary)', | ||||
|       'notion-popup': 'var(--theme--bg_popup)', | ||||
|       'divider': 'var(--theme--ui_divider)', | ||||
|       'input': 'var(--theme--ui_input)', | ||||
|       'icon': 'var(--theme--icon)', | ||||
|       'icon-secondary': 'var(--theme--icon_secondary)', | ||||
|       'foreground': 'var(--theme--text)', | ||||
|       'foreground-secondary': 'var(--theme--text_secondary)', | ||||
|       'interactive-hover': 'var(--theme--ui_interactive-hover)', | ||||
|       'interactive-active': 'var(--theme--ui_interactive-active)', | ||||
|       'tag': 'var(--theme--tag_default)', | ||||
|       'tag-text': 'var(--theme--tag_default-text)', | ||||
|       'toggle': { | ||||
|         'on': 'var(--theme--ui_toggle-on)', | ||||
|         'off': 'var(--theme--ui_toggle-off)', | ||||
|         'feature': 'var(--theme--ui_toggle-feature)', | ||||
|       }, | ||||
|       'accent': { | ||||
|         'blue': 'var(--theme--accent_blue)', | ||||
|         'blue-hover': 'var(--theme--accent_blue-hover)', | ||||
|         'blue-focus': 'var(--theme--accent_blue-focus)', | ||||
|         'blue-text': 'var(--theme--accent_blue-text)', | ||||
|         'red': 'var(--theme--accent_red)', | ||||
|         'red-hover': 'var(--theme--accent_red-hover)', | ||||
|         'red-text': 'var(--theme--accent_red-text)', | ||||
|       }, | ||||
|       'grey': mapColorVariables('grey'), | ||||
|       'brown': mapColorVariables('brown'), | ||||
|       'orange': mapColorVariables('orange'), | ||||
|       'yellow': mapColorVariables('yellow'), | ||||
|       'green': mapColorVariables('green'), | ||||
|       'blue': mapColorVariables('blue'), | ||||
|       'purple': mapColorVariables('purple'), | ||||
|       'pink': mapColorVariables('pink'), | ||||
|       'red': mapColorVariables('red'), | ||||
|     }, | ||||
|     extend: { | ||||
|       width: { | ||||
|         'full-96': 'calc(100% - 24rem)', | ||||
|       }, | ||||
|       maxHeight: { | ||||
|         'full-16': 'calc(100% - 4rem)', | ||||
|         'full-32': 'calc(100% - 8rem)', | ||||
|         'full-48': 'calc(100% - 12rem)', | ||||
|         'full-64': 'calc(100% - 16rem)', | ||||
|       }, | ||||
|     }, | ||||
|   }, | ||||
|   plugins: customClasses, | ||||
| }); | ||||
| 
 | ||||
| tw`hidden ${Object.keys(customClasses).join(' ')}`; | ||||
| 
 | ||||
| export { tw }; | ||||
| @ -1,8 +0,0 @@ | ||||
| [ | ||||
|   "menu@a6621988-551d-495a-97d8-3c568bca2e9e", | ||||
|   "theming@0f0bf8b6-eae6-4273-b307-8fc43f2ee082", | ||||
|   "components@36a2ffc9-27ff-480e-84a7-c7700a7d232d", | ||||
|   "tweaks@5174a483-c88d-4bf8-a95f-35cd330b76e2", | ||||
|   "bypass-preview@cb6fd684-f113-4a7a-9423-8f0f0cff069f", | ||||
|   "calendar-scroll@b1c7db33-dfee-489a-a76c-0dd66f7ed29a" | ||||
| ] | ||||
| @ -1,20 +0,0 @@ | ||||
| /* | ||||
|  * notion-enhancer core: theming | ||||
|  * (c) 2021 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
 | ||||
|  * (https://notion-enhancer.github.io/) under the MIT license
 | ||||
|  */ | ||||
| 
 | ||||
| 'use strict'; | ||||
| 
 | ||||
| export default function (api, db) { | ||||
|   const { web } = api; | ||||
| 
 | ||||
|   const updateTheme = () => | ||||
|     document.documentElement.classList[ | ||||
|       document.body.classList.contains('dark') ? 'add' : 'remove' | ||||
|     ]('dark'); | ||||
|   updateTheme(); | ||||
|   web.addDocumentObserver((mutation) => { | ||||
|     if (mutation.target === document.body) updateTheme(); | ||||
|   }); | ||||
| } | ||||
| @ -1,24 +0,0 @@ | ||||
| { | ||||
|   "name": "theming", | ||||
|   "id": "0f0bf8b6-eae6-4273-b307-8fc43f2ee082", | ||||
|   "version": "0.11.0", | ||||
|   "description": "the default theme variables, required by other themes & extensions.", | ||||
|   "tags": ["core"], | ||||
|   "authors": [ | ||||
|     { | ||||
|       "name": "dragonwocky", | ||||
|       "email": "thedragonring.bod@gmail.com", | ||||
|       "homepage": "https://dragonwocky.me/", | ||||
|       "avatar": "https://dragonwocky.me/avatar.jpg" | ||||
|     } | ||||
|   ], | ||||
|   "css": { | ||||
|     "frame": ["variables.css"], | ||||
|     "client": ["variables.css", "prism.css", "theme.css"], | ||||
|     "menu": ["variables.css", "prism.css"] | ||||
|   }, | ||||
|   "js": { | ||||
|     "client": ["client.mjs"] | ||||
|   }, | ||||
|   "options": [] | ||||
| } | ||||
| @ -1,156 +0,0 @@ | ||||
| /* | ||||
|  * notion-enhancer core: theming | ||||
|  * (c) 2021 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/) | ||||
|  * (https://notion-enhancer.github.io/) under the MIT license | ||||
|  */ | ||||
| 
 | ||||
| .token.property { | ||||
|   color: var(--theme--code_property) !important; | ||||
| } | ||||
| .token.tag { | ||||
|   color: var(--theme--code_tag) !important; | ||||
| } | ||||
| .token.boolean { | ||||
|   color: var(--theme--code_boolean) !important; | ||||
| } | ||||
| .token.number { | ||||
|   color: var(--theme--code_number) !important; | ||||
| } | ||||
| .token.constant { | ||||
|   color: var(--theme--code_constant) !important; | ||||
| } | ||||
| .token.symbol { | ||||
|   color: var(--theme--code_symbol) !important; | ||||
| } | ||||
| .token.deleted { | ||||
|   color: var(--theme--code_deleted) !important; | ||||
| } | ||||
| .token.selector { | ||||
|   color: var(--theme--code_selector) !important; | ||||
| } | ||||
| .token.attr-name { | ||||
|   color: var(--theme--code_attr-name) !important; | ||||
| } | ||||
| .token.string { | ||||
|   color: var(--theme--code_string) !important; | ||||
| } | ||||
| .token.char { | ||||
|   color: var(--theme--code_char) !important; | ||||
| } | ||||
| .token.builtin { | ||||
|   color: var(--theme--code_builtin) !important; | ||||
| } | ||||
| .token.inserted { | ||||
|   color: var(--theme--code_inserted) !important; | ||||
| } | ||||
| .token.operator { | ||||
|   color: var(--theme--code_operator) !important; | ||||
| } | ||||
| .token.entity { | ||||
|   color: var(--theme--code_entity) !important; | ||||
| } | ||||
| .token.url { | ||||
|   color: var(--theme--code_url) !important; | ||||
| } | ||||
| .token.variable { | ||||
|   color: var(--theme--code_variable) !important; | ||||
| } | ||||
| .token.comment { | ||||
|   color: var(--theme--code_comment) !important; | ||||
| } | ||||
| .token.cdata { | ||||
|   color: var(--theme--code_cdata) !important; | ||||
| } | ||||
| .token.prolog { | ||||
|   color: var(--theme--code_prolog) !important; | ||||
| } | ||||
| .token.doctype { | ||||
|   color: var(--theme--code_doctype) !important; | ||||
| } | ||||
| .token.atrule { | ||||
|   color: var(--theme--code_atrule) !important; | ||||
| } | ||||
| .token.attr-value { | ||||
|   color: var(--theme--code_attr-value) !important; | ||||
| } | ||||
| .token.keyword { | ||||
|   color: var(--theme--code_keyword) !important; | ||||
| } | ||||
| .token.regex { | ||||
|   color: var(--theme--code_regex) !important; | ||||
| } | ||||
| .token.important { | ||||
|   color: var(--theme--code_important) !important; | ||||
| } | ||||
| .token.function { | ||||
|   color: var(--theme--code_function) !important; | ||||
| } | ||||
| .token.class-name { | ||||
|   color: var(--theme--code_class-name) !important; | ||||
| } | ||||
| .token.parameter { | ||||
|   color: var(--theme--code_parameter) !important; | ||||
| } | ||||
| .token.decorator { | ||||
|   color: var(--theme--code_decorator) !important; | ||||
| } | ||||
| .token.id { | ||||
|   color: var(--theme--code_id) !important; | ||||
| } | ||||
| .token.class { | ||||
|   color: var(--theme--code_class) !important; | ||||
| } | ||||
| .token.pseudo-element { | ||||
|   color: var(--theme--code_pseudo-element) !important; | ||||
| } | ||||
| .token.pseudo-class { | ||||
|   color: var(--theme--code_pseudo-class) !important; | ||||
| } | ||||
| .token.attribute { | ||||
|   color: var(--theme--code_attribute) !important; | ||||
| } | ||||
| .token.value { | ||||
|   color: var(--theme--code_value) !important; | ||||
| } | ||||
| .token.unit { | ||||
|   color: var(--theme--code_unit) !important; | ||||
| } | ||||
| .token.punctuation { | ||||
|   color: var(--theme--code_punctuation) !important; | ||||
|   opacity: 0.7 !important; | ||||
| } | ||||
| .token.annotation { | ||||
|   color: var(--theme--code_annotation) !important; | ||||
| } | ||||
| 
 | ||||
| .token.operator { | ||||
|   background: transparent !important; | ||||
| } | ||||
| .token.namespace { | ||||
|   opacity: 0.7 !important; | ||||
| } | ||||
| .token.important, | ||||
| .token.bold { | ||||
|   font-weight: bold !important; | ||||
| } | ||||
| .token.italic { | ||||
|   font-style: italic !important; | ||||
| } | ||||
| .token.entity { | ||||
|   cursor: help !important; | ||||
| } | ||||
| .token a { | ||||
|   color: inherit !important; | ||||
| } | ||||
| .token.punctuation.brace-hover, | ||||
| .token.punctuation.brace-selected { | ||||
|   outline: solid 1px !important; | ||||
| } | ||||
| 
 | ||||
| .token.operator, | ||||
| .token.entity, | ||||
| .token.url, | ||||
| .language-css .token.string, | ||||
| .style .token.string { | ||||
|   background: none !important; | ||||
| } | ||||
| @ -1,364 +0,0 @@ | ||||
| /* | ||||
|  * notion-enhancer core: theming | ||||
|  * (c) 2021 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/) | ||||
|  * (https://notion-enhancer.github.io/) under the MIT license | ||||
|  * | ||||
|  * (rgb|hsl|#)[^;]+; | ||||
|  */ | ||||
| 
 | ||||
| :root { | ||||
|   --theme--page-padding: calc(96px + env(safe-area-inset-left)); | ||||
|   --theme--page-width: 900px; | ||||
|   --theme--page-width_full: 100%; | ||||
|   --theme--page_banner-height: 30vh; | ||||
|   --theme--page_preview-padding: 8rem; | ||||
|   --theme--page_preview-width: 977px; | ||||
|   --theme--page_preview_banner-height: 20vh; | ||||
| 
 | ||||
|   --theme--font_sans: -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, | ||||
|     'Apple Color Emoji', Arial, sans-serif, 'Segoe UI Emoji', 'Segoe UI Symbol'; | ||||
|   --theme--font_serif: Lyon-Text, Georgia, YuMincho, 'Yu Mincho', 'Hiragino Mincho ProN', | ||||
|     'Hiragino Mincho Pro', 'Songti TC', 'Songti SC', SimSun, 'Nanum Myeongjo', NanumMyeongjo, | ||||
|     Batang, serif; | ||||
|   --theme--font_mono: iawriter-mono, Nitti, Menlo, Courier, monospace; | ||||
|   --theme--font_code: SFMono-Regular, Consolas, 'Liberation Mono', Menlo, Courier, monospace; | ||||
|   --theme--font_quote: var(--theme--font_sans); | ||||
|   --theme--font_headings: var(--theme--font_sans); | ||||
| 
 | ||||
|   --theme--accent_blue: rgb(46, 170, 220); | ||||
|   --theme--accent_blue-selection: rgb(46, 170, 220, 0.25); | ||||
|   --theme--accent_blue-hover: rgb(6, 156, 205); | ||||
|   --theme--accent_blue-focus: rgb(0, 141, 190); | ||||
|   --theme--accent_blue-text: #fff; | ||||
|   --theme--accent_red: #eb5757; | ||||
|   --theme--accent_red-hover: rgba(235, 87, 87, 0.1); | ||||
|   --theme--accent_red-text: #fff; | ||||
| } | ||||
| 
 | ||||
| :root { | ||||
|   --theme--bg: #fff; | ||||
|   --theme--bg_secondary: rgb(247, 246, 243); | ||||
|   --theme--bg_popup: #fff; | ||||
| 
 | ||||
|   --theme--scrollbar_track: #edece9; | ||||
|   --theme--scrollbar_thumb: #d3d1cb; | ||||
|   --theme--scrollbar_thumb-hover: #aeaca6; | ||||
| 
 | ||||
|   --theme--ui_shadow: rgba(15, 15, 15, 0.15); | ||||
|   --theme--ui_divider: rgb(237, 237, 236); | ||||
|   --theme--ui_interactive-hover: rgba(55, 53, 47, 0.08); | ||||
|   --theme--ui_interactive-active: rgba(55, 53, 47, 0.16); | ||||
|   --theme--ui_toggle-on: var(--theme--accent_blue); | ||||
|   --theme--ui_toggle-off: rgba(135, 131, 120, 0.3); | ||||
|   --theme--ui_toggle-feature: #fff; | ||||
|   --theme--ui_input: rgba(242, 241, 238, 0.6); | ||||
|   --theme--ui_tooltip: rgb(15, 15, 15); | ||||
|   --theme--ui_tooltip-title: rgba(255, 255, 255, 0.9); | ||||
|   --theme--ui_tooltip-description: rgba(206, 205, 202, 0.6); | ||||
| 
 | ||||
|   --theme--icon: rgba(55, 53, 47, 0.8); | ||||
|   --theme--icon_secondary: rgba(55, 53, 47, 0.4); | ||||
| 
 | ||||
|   --theme--text: rgb(55, 43, 47); | ||||
|   --theme--text_secondary: rgba(55, 43, 47, 0.6); | ||||
|   --theme--text_grey: rgb(155, 154, 151); | ||||
|   --theme--text_brown: rgb(100, 71, 58); | ||||
|   --theme--text_orange: rgb(217, 115, 13); | ||||
|   --theme--text_yellow: rgb(223, 171, 1); | ||||
|   --theme--text_green: rgb(15, 123, 108); | ||||
|   --theme--text_blue: rgb(11, 110, 153); | ||||
|   --theme--text_purple: rgb(105, 64, 165); | ||||
|   --theme--text_pink: rgb(173, 26, 114); | ||||
|   --theme--text_red: rgb(224, 62, 62); | ||||
| 
 | ||||
|   --theme--highlight-text: var(--theme--text); | ||||
|   --theme--highlight_grey: rgb(235, 236, 237); | ||||
|   --theme--highlight_grey-text: var(--theme--highlight-text); | ||||
|   --theme--highlight_brown: rgb(233, 229, 227); | ||||
|   --theme--highlight_brown-text: var(--theme--highlight-text); | ||||
|   --theme--highlight_orange: rgb(250, 235, 221); | ||||
|   --theme--highlight_orange-text: var(--theme--highlight-text); | ||||
|   --theme--highlight_yellow: rgb(251, 243, 219); | ||||
|   --theme--highlight_yellow-text: var(--theme--highlight-text); | ||||
|   --theme--highlight_green: rgb(221, 237, 234); | ||||
|   --theme--highlight_green-text: var(--theme--highlight-text); | ||||
|   --theme--highlight_blue: rgb(221, 235, 241); | ||||
|   --theme--highlight_blue-text: var(--theme--highlight-text); | ||||
|   --theme--highlight_purple: rgb(234, 228, 242); | ||||
|   --theme--highlight_purple-text: var(--theme--highlight-text); | ||||
|   --theme--highlight_pink: rgb(244, 223, 235); | ||||
|   --theme--highlight_pink-text: var(--theme--highlight-text); | ||||
|   --theme--highlight_red: rgb(251, 228, 228); | ||||
|   --theme--highlight_red-text: var(--theme--highlight-text); | ||||
| 
 | ||||
|   --theme--block-text: var(--theme--text); | ||||
|   --theme--block_grey: rgb(235, 236, 237); | ||||
|   --theme--block_grey-text: var(--theme--block-text); | ||||
|   --theme--block_brown: rgb(233, 229, 227); | ||||
|   --theme--block_brown-text: var(--theme--block-text); | ||||
|   --theme--block_orange: rgb(250, 235, 221); | ||||
|   --theme--block_orange-text: var(--theme--block-text); | ||||
|   --theme--block_yellow: rgb(251, 243, 219); | ||||
|   --theme--block_yellow-text: var(--theme--block-text); | ||||
|   --theme--block_green: rgb(221, 237, 234); | ||||
|   --theme--block_green-text: var(--theme--block-text); | ||||
|   --theme--block_blue: rgb(221, 235, 241); | ||||
|   --theme--block_blue-text: var(--theme--block-text); | ||||
|   --theme--block_purple: rgb(234, 228, 242); | ||||
|   --theme--block_purple-text: var(--theme--block-text); | ||||
|   --theme--block_pink: rgb(244, 223, 235); | ||||
|   --theme--block_pink-text: var(--theme--block-text); | ||||
|   --theme--block_red: rgb(251, 228, 228); | ||||
|   --theme--block_red-text: var(--theme--block-text); | ||||
| 
 | ||||
|   --theme--tag-text: var(--theme--text); | ||||
|   --theme--tag_default: rgba(206, 205, 202, 0.5); | ||||
|   --theme--tag_default-text: var(--theme--tag-text); | ||||
|   --theme--tag_grey: rgba(140, 46, 0, 0.2); | ||||
|   --theme--tag_grey-text: var(--theme--tag-text); | ||||
|   --theme--tag_brown: rgba(140, 46, 0, 0.2); | ||||
|   --theme--tag_brown-text: var(--theme--tag-text); | ||||
|   --theme--tag_orange: rgba(245, 93, 0, 0.2); | ||||
|   --theme--tag_orange-text: var(--theme--tag-text); | ||||
|   --theme--tag_yellow: rgba(233, 168, 0, 0.2); | ||||
|   --theme--tag_yellow-text: var(--theme--tag-text); | ||||
|   --theme--tag_green: rgba(0, 135, 107, 0.2); | ||||
|   --theme--tag_green-text: var(--theme--tag-text); | ||||
|   --theme--tag_blue: rgba(0, 120, 223, 0.2); | ||||
|   --theme--tag_blue-text: var(--theme--tag-text); | ||||
|   --theme--tag_purple: rgba(103, 36, 222, 0.2); | ||||
|   --theme--tag_purple-text: var(--theme--tag-text); | ||||
|   --theme--tag_pink: rgba(221, 0, 129, 0.2); | ||||
|   --theme--tag_pink-text: var(--theme--tag-text); | ||||
|   --theme--tag_red: rgba(255, 0, 26, 0.2); | ||||
|   --theme--tag_red-text: var(--theme--tag-text); | ||||
| 
 | ||||
|   --theme--callout-text: var(--theme--text); | ||||
|   --theme--callout_grey: rgba(235, 236, 237, 0.3); | ||||
|   --theme--callout_grey-text: var(--theme--callout-text); | ||||
|   --theme--callout_brown: rgba(233, 229, 227, 0.3); | ||||
|   --theme--callout_brown-text: var(--theme--callout-text); | ||||
|   --theme--callout_orange: rgba(250, 235, 221, 0.3); | ||||
|   --theme--callout_orange-text: var(--theme--callout-text); | ||||
|   --theme--callout_yellow: rgba(251, 243, 219, 0.3); | ||||
|   --theme--callout_yellow-text: var(--theme--callout-text); | ||||
|   --theme--callout_green: rgba(221, 237, 234, 0.3); | ||||
|   --theme--callout_green-text: var(--theme--callout-text); | ||||
|   --theme--callout_blue: rgba(221, 235, 241, 0.3); | ||||
|   --theme--callout_blue-text: var(--theme--callout-text); | ||||
|   --theme--callout_purple: rgba(234, 228, 242, 0.3); | ||||
|   --theme--callout_purple-text: var(--theme--callout-text); | ||||
|   --theme--callout_pink: rgba(244, 223, 235, 0.3); | ||||
|   --theme--callout_pink-text: var(--theme--callout-text); | ||||
|   --theme--callout_red: rgba(251, 228, 228, 0.3); | ||||
|   --theme--callout_red-text: var(--theme--callout-text); | ||||
| 
 | ||||
|   --theme--code_inline: rgba(135, 131, 120, 0.15); | ||||
|   --theme--code_inline-text: #eb5757; | ||||
| 
 | ||||
|   --theme--code: #f7f6f3; | ||||
|   --theme--code_plain: var(--theme--text); | ||||
|   --theme--code_property: #905; | ||||
|   --theme--code_tag: var(--theme--code_property); | ||||
|   --theme--code_boolean: var(--theme--code_property); | ||||
|   --theme--code_number: var(--theme--code_property); | ||||
|   --theme--code_constant: var(--theme--code_property); | ||||
|   --theme--code_symbol: var(--theme--code_property); | ||||
|   --theme--code_deleted: var(--theme--code_property); | ||||
|   --theme--code_selector: #690; | ||||
|   --theme--code_attr-name: var(--theme--code_selector); | ||||
|   --theme--code_string: var(--theme--code_selector); | ||||
|   --theme--code_char: var(--theme--code_selector); | ||||
|   --theme--code_builtin: var(--theme--code_selector); | ||||
|   --theme--code_inserted: var(--theme--code_selector); | ||||
|   --theme--code_operator: #9a6e3a; | ||||
|   --theme--code_entity: var(--theme--code_operator); | ||||
|   --theme--code_url: var(--theme--code_operator); | ||||
|   --theme--code_variable: var(--theme--code_regex); | ||||
|   --theme--code_comment: slategrey; | ||||
|   --theme--code_cdata: var(--theme--code_comment); | ||||
|   --theme--code_prolog: var(--theme--code_comment); | ||||
|   --theme--code_doctype: var(--theme--code_comment); | ||||
|   --theme--code_atrule: #07a; | ||||
|   --theme--code_attr-value: var(--theme--code_atrule); | ||||
|   --theme--code_keyword: var(--theme--code_atrule); | ||||
|   --theme--code_regex: #e90; | ||||
|   --theme--code_important: var(--theme--code_regex); | ||||
|   --theme--code_function: #dd4a68; | ||||
|   --theme--code_class-name: var(--theme--code_function); | ||||
|   --theme--code_parameter: var(--theme--code_plain); | ||||
|   --theme--code_decorator: var(--theme--code_plain); | ||||
|   --theme--code_id: var(--theme--code_plain); | ||||
|   --theme--code_class: var(--theme--code_plain); | ||||
|   --theme--code_pseudo-element: var(--theme--code_plain); | ||||
|   --theme--code_pseudo-class: var(--theme--code_plain); | ||||
|   --theme--code_attribute: var(--theme--code_plain); | ||||
|   --theme--code_value: var(--theme--code_plain); | ||||
|   --theme--code_unit: var(--theme--code_plain); | ||||
|   --theme--code_punctuation: #999; | ||||
|   --theme--code_annotation: var(--theme--code_plain); | ||||
| } | ||||
| 
 | ||||
| :root.dark { | ||||
|   --theme--bg: rgb(47, 52, 55); | ||||
|   --theme--bg_secondary: rgb(55, 60, 63); | ||||
|   --theme--bg_popup: rgb(63, 68, 71); | ||||
| 
 | ||||
|   --theme--scrollbar_track: rgba(202, 204, 206, 0.04); | ||||
|   --theme--scrollbar_thumb: #474c50; | ||||
|   --theme--scrollbar_thumb-hover: rgba(202, 204, 206, 0.3); | ||||
| 
 | ||||
|   --theme--ui_shadow: rgba(15, 15, 15, 0.15); | ||||
|   --theme--ui_divider: rgb(255, 255, 255, 0.07); | ||||
|   --theme--ui_interactive-hover: rgb(71, 76, 80); | ||||
|   --theme--ui_interactive-active: rgb(63, 68, 71); | ||||
|   --theme--ui_toggle-on: var(--theme--accent_blue); | ||||
|   --theme--ui_toggle-off: rgba(202, 204, 206, 0.3); | ||||
|   --theme--ui_toggle-feature: #fff; | ||||
|   --theme--ui_input: rgba(15, 15, 15, 0.3); | ||||
|   --theme--ui_tooltip: rgb(202, 204, 206); | ||||
|   --theme--ui_tooltip-title: rgb(15, 15, 15); | ||||
|   --theme--ui_tooltip-description: rgba(47, 52, 55, 0.6); | ||||
| 
 | ||||
|   --theme--icon: rgba(202, 204, 206); | ||||
|   --theme--icon_secondary: rgb(202, 204, 206, 0.6); | ||||
| 
 | ||||
|   --theme--text: rgba(255, 255, 255, 0.9); | ||||
|   --theme--text_secondary: rgba(255, 255, 255, 0.6); | ||||
|   --theme--text_grey: rgba(151, 154, 155, 0.95); | ||||
|   --theme--text_brown: rgb(147, 114, 100); | ||||
|   --theme--text_orange: rgb(255, 163, 68); | ||||
|   --theme--text_yellow: rgb(255, 220, 73); | ||||
|   --theme--text_green: rgb(77, 171, 154); | ||||
|   --theme--text_blue: rgb(82, 156, 202); | ||||
|   --theme--text_purple: rgb(154, 109, 215); | ||||
|   --theme--text_pink: rgb(226, 85, 161); | ||||
|   --theme--text_red: rgb(255, 115, 105); | ||||
| 
 | ||||
|   --theme--highlight-text: var(--theme--text); | ||||
|   --theme--highlight_grey: rgb(69, 75, 78); | ||||
|   --theme--highlight_grey-text: var(--theme--highlight-text); | ||||
|   --theme--highlight_brown: rgb(67, 64, 64); | ||||
|   --theme--highlight_brown-text: var(--theme--highlight-text); | ||||
|   --theme--highlight_orange: rgb(89, 74, 58); | ||||
|   --theme--highlight_orange-text: var(--theme--highlight-text); | ||||
|   --theme--highlight_yellow: rgb(89, 86, 59); | ||||
|   --theme--highlight_yellow-text: var(--theme--highlight-text); | ||||
|   --theme--highlight_green: rgb(53, 76, 75); | ||||
|   --theme--highlight_green-text: var(--theme--highlight-text); | ||||
|   --theme--highlight_blue: rgb(54, 73, 84); | ||||
|   --theme--highlight_blue-text: var(--theme--highlight-text); | ||||
|   --theme--highlight_purple: rgb(68, 63, 87); | ||||
|   --theme--highlight_purple-text: var(--theme--highlight-text); | ||||
|   --theme--highlight_pink: rgb(83, 59, 76); | ||||
|   --theme--highlight_pink-text: var(--theme--highlight-text); | ||||
|   --theme--highlight_red: rgb(89, 65, 65); | ||||
|   --theme--highlight_red-text: var(--theme--highlight-text); | ||||
| 
 | ||||
|   --theme--block-text: var(--theme--text); | ||||
|   --theme--block_grey: rgb(69, 75, 78); | ||||
|   --theme--block_grey-text: var(--theme--block-text); | ||||
|   --theme--block_brown: rgb(67, 64, 64); | ||||
|   --theme--block_brown-text: var(--theme--block-text); | ||||
|   --theme--block_orange: rgb(89, 74, 58); | ||||
|   --theme--block_orange-text: var(--theme--block-text); | ||||
|   --theme--block_yellow: rgb(89, 86, 59); | ||||
|   --theme--block_yellow-text: var(--theme--block-text); | ||||
|   --theme--block_green: rgb(53, 76, 75); | ||||
|   --theme--block_green-text: var(--theme--block-text); | ||||
|   --theme--block_blue: rgb(54, 73, 84); | ||||
|   --theme--block_blue-text: var(--theme--block-text); | ||||
|   --theme--block_purple: rgb(68, 63, 87); | ||||
|   --theme--block_purple-text: var(--theme--block-text); | ||||
|   --theme--block_pink: rgb(83, 59, 76); | ||||
|   --theme--block_pink-text: var(--theme--block-text); | ||||
|   --theme--block_red: rgb(89, 65, 65); | ||||
|   --theme--block_red-text: var(--theme--block-text); | ||||
| 
 | ||||
|   --theme--tag-text: var(--theme--text); | ||||
|   --theme--tag_default: rgb(80, 85, 88); | ||||
|   --theme--tag_default-text: var(--theme--tag-text); | ||||
|   --theme--tag_grey: rgba(151, 154, 155, 0.5); | ||||
|   --theme--tag_grey-text: var(--theme--tag-text); | ||||
|   --theme--tag_brown: rgba(147, 114, 100, 0.5); | ||||
|   --theme--tag_brown-text: var(--theme--tag-text); | ||||
|   --theme--tag_orange: rgba(255, 163, 68, 0.5); | ||||
|   --theme--tag_orange-text: var(--theme--tag-text); | ||||
|   --theme--tag_yellow: rgba(255, 220, 73, 0.5); | ||||
|   --theme--tag_yellow-text: var(--theme--tag-text); | ||||
|   --theme--tag_green: rgba(77, 171, 154, 0.5); | ||||
|   --theme--tag_green-text: var(--theme--tag-text); | ||||
|   --theme--tag_blue: rgba(82, 156, 202, 0.5); | ||||
|   --theme--tag_blue-text: var(--theme--tag-text); | ||||
|   --theme--tag_purple: rgba(154, 109, 215, 0.5); | ||||
|   --theme--tag_purple-text: var(--theme--tag-text); | ||||
|   --theme--tag_pink: rgba(226, 85, 161, 0.5); | ||||
|   --theme--tag_pink-text: var(--theme--tag-text); | ||||
|   --theme--tag_red: rgba(255, 115, 105, 0.5); | ||||
|   --theme--tag_red-text: var(--theme--tag-text); | ||||
| 
 | ||||
|   --theme--callout-text: var(--theme--text); | ||||
|   --theme--callout_grey: rgba(69, 75, 78, 0.3); | ||||
|   --theme--callout_grey-text: var(--theme--callout-text); | ||||
|   --theme--callout_brown: rgba(67, 64, 64, 0.3); | ||||
|   --theme--callout_brown-text: var(--theme--callout-text); | ||||
|   --theme--callout_orange: rgba(89, 74, 58, 0.3); | ||||
|   --theme--callout_orange-text: var(--theme--callout-text); | ||||
|   --theme--callout_yellow: rgba(89, 86, 59, 0.3); | ||||
|   --theme--callout_yellow-text: var(--theme--callout-text); | ||||
|   --theme--callout_green: rgba(53, 76, 75, 0.3); | ||||
|   --theme--callout_green-text: var(--theme--callout-text); | ||||
|   --theme--callout_blue: rgba(54, 73, 84, 0.3); | ||||
|   --theme--callout_blue-text: var(--theme--callout-text); | ||||
|   --theme--callout_purple: rgba(68, 63, 87, 0.3); | ||||
|   --theme--callout_purple-text: var(--theme--callout-text); | ||||
|   --theme--callout_pink: rgba(83, 59, 76, 0.3); | ||||
|   --theme--callout_pink-text: var(--theme--callout-text); | ||||
|   --theme--callout_red: rgba(89, 65, 65, 0.3); | ||||
|   --theme--callout_red-text: var(--theme--callout-text); | ||||
| 
 | ||||
|   --theme--code_inline: rgba(135, 131, 120, 0.15); | ||||
|   --theme--code_inline-text: #eb5757; | ||||
| 
 | ||||
|   --theme--code: rgb(63, 68, 71); | ||||
|   --theme--code_plain: var(--theme--text); | ||||
|   --theme--code_property: hsl(350, 40%, 70%); | ||||
|   --theme--code_tag: var(--theme--code_property); | ||||
|   --theme--code_boolean: var(--theme--code_property); | ||||
|   --theme--code_number: var(--theme--code_property); | ||||
|   --theme--code_constant: var(--theme--code_property); | ||||
|   --theme--code_symbol: var(--theme--code_property); | ||||
|   --theme--code_deleted: #f00; | ||||
|   --theme--code_selector: hsl(75, 70%, 60%); | ||||
|   --theme--code_attr-name: var(--theme--code_selector); | ||||
|   --theme--code_string: var(--theme--code_selector); | ||||
|   --theme--code_char: var(--theme--code_selector); | ||||
|   --theme--code_builtin: var(--theme--code_selector); | ||||
|   --theme--code_inserted: var(--theme--code_selector); | ||||
|   --theme--code_operator: hsl(40, 90%, 60%); | ||||
|   --theme--code_entity: var(--theme--code_operator); | ||||
|   --theme--code_url: var(--theme--code_operator); | ||||
|   --theme--code_variable: var(--theme--code_operator); | ||||
|   --theme--code_comment: hsl(30, 20%, 50%); | ||||
|   --theme--code_cdata: var(--theme--code_comment); | ||||
|   --theme--code_prolog: var(--theme--code_comment); | ||||
|   --theme--code_doctype: var(--theme--code_comment); | ||||
|   --theme--code_atrule: hsl(350, 40%, 70%); | ||||
|   --theme--code_attr-value: var(--theme--code_atrule); | ||||
|   --theme--code_keyword: var(--theme--code_atrule); | ||||
|   --theme--code_regex: #e90; | ||||
|   --theme--code_important: var(--theme--code_regex); | ||||
|   --theme--code_function: var(--theme--code_plain); | ||||
|   --theme--code_class-name: var(--theme--code_function); | ||||
|   --theme--code_parameter: var(--theme--code_plain); | ||||
|   --theme--code_decorator: var(--theme--code_plain); | ||||
|   --theme--code_id: var(--theme--code_plain); | ||||
|   --theme--code_class: var(--theme--code_plain); | ||||
|   --theme--code_pseudo-element: var(--theme--code_plain); | ||||
|   --theme--code_pseudo-class: var(--theme--code_plain); | ||||
|   --theme--code_attribute: var(--theme--code_plain); | ||||
|   --theme--code_value: var(--theme--code_plain); | ||||
|   --theme--code_unit: var(--theme--code_plain); | ||||
|   --theme--code_punctuation: var(--theme--code_plain); | ||||
|   --theme--code_annotation: var(--theme--code_plain); | ||||
| } | ||||
| @ -1,61 +0,0 @@ | ||||
| /* | ||||
|  * notion-enhancer: tweaks | ||||
|  * (c) 2021 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/) | ||||
|  * (c) 2020 arecsu | ||||
|  * (https://notion-enhancer.github.io/) under the MIT license | ||||
|  */ | ||||
| 
 | ||||
| .tweak--snappy_transitions * { | ||||
|   animation-duration: 0s !important; | ||||
|   transition-duration: 0s !important; | ||||
| } | ||||
| .tweak--snappy_transitions .notion-selectable-halo { | ||||
|   opacity: 1 !important; | ||||
| } | ||||
| 
 | ||||
| .tweak--hide_help .notion-help-button { | ||||
|   display: none !important; | ||||
| } | ||||
| 
 | ||||
| .tweak--thicker_bold .notion-page-content span[style*='font-weight:600'] { | ||||
|   font-weight: 700 !important; | ||||
| } | ||||
| 
 | ||||
| .tweak--spaced_lines .notion-page-content .notion-selectable.notion-text-block { | ||||
|   line-height: 1.65 !important; | ||||
|   margin-top: 0.75em !important; | ||||
| } | ||||
| 
 | ||||
| .tweak--condensed_bullets .notion-selectable.notion-bulleted_list-block { | ||||
|   margin-top: -1.5px !important; | ||||
|   margin-bottom: -1.5px !important; | ||||
| } | ||||
| 
 | ||||
| .tweak--responsive_breakpoint .notion-column_list-block [style='display: flex;'] > div { | ||||
|   width: 100% !important; | ||||
| } | ||||
| .tweak--responsive_breakpoint .notion-column_list-block [style='display: flex;'] { | ||||
|   flex-direction: column !important; | ||||
| } | ||||
| .tweak--responsive_breakpoint .notion-app-inner { | ||||
|   --theme--page-width: 100%; | ||||
|   --theme--page-padding: calc(48px + env(safe-area-inset-left)); | ||||
| } | ||||
| 
 | ||||
| .tweak--bracketed_links .notion-link-token span { | ||||
|   border-bottom: none !important; | ||||
| } | ||||
| .tweak--bracketed_links .notion-link-token:before { | ||||
|   content: '[['; | ||||
|   opacity: 0.7; | ||||
|   transition: opacity 100ms ease-in; | ||||
| } | ||||
| .tweak--bracketed_links .notion-link-token:after { | ||||
|   content: ']]'; | ||||
|   opacity: 0.7; | ||||
|   transition: opacity 100ms ease-in; | ||||
| } | ||||
| .tweak--bracketed_links .notion-link-token:hover::before, | ||||
| .tweak--bracketed_links .notion-link-token:hover::after { | ||||
|   opacity: 1; | ||||
| } | ||||
| @ -1,49 +0,0 @@ | ||||
| /* | ||||
|  * notion-enhancer: tweaks | ||||
|  * (c) 2021 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
 | ||||
|  * (https://notion-enhancer.github.io/) under the MIT license
 | ||||
|  */ | ||||
| 
 | ||||
| 'use strict'; | ||||
| 
 | ||||
| const _id = '5174a483-c88d-4bf8-a95f-35cd330b76e2'; | ||||
| import { env, storage, web } from '../../api/_.mjs'; | ||||
| 
 | ||||
| web.whenReady().then(async () => { | ||||
|   const cssInsert = await storage.get(_id, '_file.insert.css'); | ||||
|   if (cssInsert) { | ||||
|     document.body.append( | ||||
|       web.createElement( | ||||
|         web.html` | ||||
|         <style id="${await storage.get(_id, 'insert.css')}@${_id}">${cssInsert}</style>` | ||||
|       ) | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   if (['linux', 'win32'].includes(env.name)) { | ||||
|     // dragarea_height
 | ||||
|   } | ||||
| 
 | ||||
|   for (const tweak of [ | ||||
|     'snappy_transitions', | ||||
|     'thicker_bold', | ||||
|     'spaced_lines', | ||||
|     'hide_help', | ||||
|     'condensed_bullets', | ||||
|     'bracketed_links', | ||||
|   ]) { | ||||
|     if (await storage.get(_id, `tweak.${tweak}`)) { | ||||
|       document.body.classList.add(`tweak--${tweak}`); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   const responsiveBreakpoint = await storage.get(_id, 'tweak.responsive_breakpoint'), | ||||
|     addResponsiveBreakpoint = () => { | ||||
|       document.body.classList.remove('tweak--responsive_breakpoint'); | ||||
|       if (window.innerWidth <= responsiveBreakpoint) { | ||||
|         document.body.classList.add('tweak--responsive_breakpoint'); | ||||
|       } | ||||
|     }; | ||||
|   window.addEventListener('resize', addResponsiveBreakpoint); | ||||
|   addResponsiveBreakpoint(); | ||||
| }); | ||||
| @ -1,84 +0,0 @@ | ||||
| { | ||||
|   "name": "tweaks", | ||||
|   "id": "5174a483-c88d-4bf8-a95f-35cd330b76e2", | ||||
|   "version": "0.2.0", | ||||
|   "description": "common style/layout changes and custom CSS insertion.", | ||||
|   "tags": ["extension", "customisation"], | ||||
|   "authors": [ | ||||
|     { | ||||
|       "name": "dragonwocky", | ||||
|       "email": "thedragonring.bod@gmail.com", | ||||
|       "homepage": "https://dragonwocky.me/", | ||||
|       "avatar": "https://dragonwocky.me/avatar.jpg" | ||||
|     } | ||||
|   ], | ||||
|   "css": { | ||||
|     "client": ["client.css"] | ||||
|   }, | ||||
|   "js": { | ||||
|     "client": ["client.mjs"] | ||||
|   }, | ||||
|   "options": [ | ||||
|     { | ||||
|       "type": "file", | ||||
|       "key": "insert.css", | ||||
|       "label": "css insert", | ||||
|       "extensions": [".css"] | ||||
|     }, | ||||
|     { | ||||
|       "key": "tweak.dragarea_height", | ||||
|       "label": "height of frameless dragarea:", | ||||
|       "tooltip": "the rectangle added at the top of a window in 'integrated titlebar' mode, used to drag/move the window.", | ||||
|       "type": "number", | ||||
|       "value": 15, | ||||
|       "environments": ["linux", "win32"] | ||||
|     }, | ||||
|     { | ||||
|       "key": "tweak.responsive_breakpoint", | ||||
|       "label": "responsive columns breakpoint (px)", | ||||
|       "tooltip": "the width in pixels below which in-page columns are resized to appear full-width so content isn't squished.", | ||||
|       "type": "number", | ||||
|       "value": 600 | ||||
|     }, | ||||
|     { | ||||
|       "key": "tweak.snappy_transitions", | ||||
|       "label": "snappy transitions", | ||||
|       "tooltip": "set animation time for all css-driven animations to 0s. may not affect element motion e.g. the sidebar popping out.", | ||||
|       "type": "toggle", | ||||
|       "value": false | ||||
|     }, | ||||
|     { | ||||
|       "key": "tweak.hide_help", | ||||
|       "label": "hide help button", | ||||
|       "type": "toggle", | ||||
|       "value": false | ||||
|     }, | ||||
|     { | ||||
|       "key": "tweak.thicker_bold", | ||||
|       "label": "thicker bold text", | ||||
|       "type": "toggle", | ||||
|       "value": true | ||||
|     }, | ||||
|     { | ||||
|       "key": "tweak.spaced_lines", | ||||
|       "label": "readable line spacing", | ||||
|       "tooltip": "greater line spacing between text blocks.", | ||||
|       "type": "toggle", | ||||
|       "value": false | ||||
|     }, | ||||
|     { | ||||
|       "key": "tweak.condensed_bullets", | ||||
|       "label": "condense bullet points", | ||||
|       "tooltip": "tighter line spacing between bullet point blocks.", | ||||
|       "type": "toggle", | ||||
|       "value": false | ||||
|     }, | ||||
|     { | ||||
|       "key": "tweak.bracketed_links", | ||||
|       "label": "bracketed links", | ||||
|       "tooltip": "render links surrounded with [[brackets]] instead of __underlined__.", | ||||
|       "type": "toggle", | ||||
|       "value": false | ||||
|     } | ||||
|   ] | ||||
| } | ||||
| @ -6,11 +6,9 @@ | ||||
| 
 | ||||
| 'use strict'; | ||||
| 
 | ||||
| async function focusMenu() { | ||||
| function focusMenu() { | ||||
|   chrome.tabs.query({ windowId: chrome.windows.WINDOW_ID_CURRENT }, (tabs) => { | ||||
|     const url = chrome.runtime.getURL( | ||||
|         'repo/menu@a6621988-551d-495a-97d8-3c568bca2e9e/menu.html' | ||||
|       ), | ||||
|     const url = chrome.runtime.getURL('repo/menu/menu.html'), | ||||
|       menu = tabs.find((tab) => tab.url.startsWith(url)); | ||||
|     if (menu) { | ||||
|       chrome.tabs.highlight({ 'tabs': menu.index }); | ||||
| @ -19,7 +17,7 @@ async function focusMenu() { | ||||
| } | ||||
| chrome.browserAction.onClicked.addListener(focusMenu); | ||||
| 
 | ||||
| async function focusNotion() { | ||||
| function focusNotion() { | ||||
|   chrome.tabs.query({ windowId: chrome.windows.WINDOW_ID_CURRENT }, (tabs) => { | ||||
|     const notion = tabs.find((tab) => { | ||||
|       const url = new URL(tab.url), | ||||
| @ -32,11 +30,9 @@ async function focusNotion() { | ||||
|   }); | ||||
| } | ||||
| 
 | ||||
| async function reload() { | ||||
| function reload() { | ||||
|   chrome.tabs.query({ windowId: chrome.windows.WINDOW_ID_CURRENT }, (tabs) => { | ||||
|     const menu = chrome.runtime.getURL( | ||||
|       'repo/menu@a6621988-551d-495a-97d8-3c568bca2e9e/menu.html' | ||||
|     ); | ||||
|     const menu = chrome.runtime.getURL('repo/menu/menu.html'); | ||||
|     tabs.forEach((tab) => { | ||||
|       const url = new URL(tab.url), | ||||
|         matches = | ||||
|  | ||||