Compare commits

...

1351 Commits
v0.5.0 ... dev

Author SHA1 Message Date
dae9700b0b
chore: add directoryMode flag to enhanceApp for notion-repackaged 2024-11-19 21:48:56 +11:00
9f8d9030ae
feat(indent-guides): alternating indentation guide colours 2024-11-19 21:22:41 +11:00
e4d335919d
chore: move notion-enhancer btn to bottom of sidebar 2024-11-19 11:29:54 +11:00
42bb015384
feat: port across indent guides (formerly indentation lines) 2024-11-18 22:46:01 +11:00
8e9dcfac95
fix(outliner): correctly indent h1 > h3 > h2 heading sequences 2024-11-18 21:06:24 +11:00
7ad446ade8
fix(topbar): disable always on top in browser 2024-11-18 21:05:28 +11:00
d651b5ceea
feat(outliner): hide builtin floating toc when outliner is enabled 2024-11-15 15:03:41 +11:00
7dde3b8b3c
chore: notion v4.0.0 compat
use https://www.notion.so/api/__notion-enhancer for internal asset proxy
2024-11-15 14:41:02 +11:00
d5e0810915
fix: sidebar button appending
settings button has been moved into the workspace/acct dropdown
2024-07-19 21:11:27 +10:00
7fe9bb2543
fix: remove csp headers in notion desktop 2024-06-05 21:00:53 +10:00
d459455f3d
fix: adjust floating button position 2024-06-02 14:09:27 +10:00
d8bc86c943
chore: add prerelease message 2024-06-02 13:58:06 +10:00
9b36ca26b2
chore: disable telemetry, remove sponsor perks (temp) 2024-06-02 13:13:06 +10:00
c8613d0937
chore: update material ocean theme 2024-06-02 13:00:03 +10:00
af95a0bf49
style(tweaks): rounded & bubble tab styles 2024-05-30 16:24:52 +10:00
df63bef8bc
style(tweaks): default img alignment, responsive columns, disable sidebar resizing 2024-05-30 15:45:06 +10:00
43c3b1bbca
style(tweaks): update typography + page tweaks, add compact code blocks 2024-05-30 00:07:22 +10:00
daee8c70f5
chore: deprecate truncated-titles 2024-05-29 22:07:10 +10:00
2365b9043c
style(tweaks): hide slash menu, placeholders & default icons 2024-05-29 21:47:17 +10:00
a09e6abff7
fix: re-add topbar buttons after visiting home 2024-05-29 18:27:25 +10:00
72e5302c61
fix: position floating buttons correctly on load if help is hidden 2024-05-29 18:19:54 +10:00
35f243b032
style(tweaks): hide floating button 2024-05-29 12:45:45 +10:00
e25f2db188
fix(word-counter): #761 count words using chars outside latin alphabet 2024-05-28 23:01:07 +10:00
ec738cd03f
fix(right-to-left): preserve order of table cells with rtl
otherwise cells reverse on hover / do not sync between header and rows
2024-05-28 22:28:21 +10:00
fffc0c16d1
fix(right-to-left): #828 dir=auto per-element under toggle, not whole block 2024-05-28 22:02:06 +10:00
058f63c8e9
fix(right-to-left): #905 enable rtl in comments section 2024-05-28 21:39:30 +10:00
6ceca21be3
feat(right-to-left): update api compat, rtl + right-indent tocs 2024-05-28 21:30:57 +10:00
f2128fbf10
feat(line-numbers): border & bg decoration styles 2024-05-28 20:59:46 +10:00
75a864db8c
chore(line-numbers): granular dom updates 2024-05-28 00:36:23 +10:00
493ab5aa63
fix: handle mod errors individually, soft fail 2024-05-27 22:06:14 +10:00
71ed1ddb1f
feat(tweaks): normalised database scrolling 2024-05-27 22:05:46 +10:00
6dce77714e
feat(line-numbers): calc size + pos of numbers for wrapped code
- fix: #900, #897
- todo: optimise
- todo: re-implement styles/opts
2024-05-23 00:25:34 +10:00
6e80cf77b5
fix(scroller): handle repeated 'today' button clicks 2024-05-12 11:55:23 +10:00
bbc8c7a1e0
fix: replace .notion-help-button with .notion-ai-button 2024-05-12 11:25:02 +10:00
47cfece013
feat: update focus (focus-mode) extension 2024-04-28 00:01:12 +10:00
26cbdf7e2e
feat: update fonts extension, add option for math font
remove font vars from theme css, fonts can now only be controlled via font chooser
2024-04-25 23:59:56 +10:00
1576680a66
chore: remove outdated extension thumbnails 2024-04-23 16:59:07 +10:00
73665e9743
feat: update no-peeking extension (formerly bypass-preview) 2024-04-23 16:52:57 +10:00
5b22ca9ff9
chore: deprecate collapsible-properties, global-block-links, weekly-view 2024-04-23 15:33:16 +10:00
5f958726d9
feat(scroller): add calendar scroll functionality to 'Today' button 2024-04-23 15:13:05 +10:00
8ea6ef30ff
chore: update lucide to v0.372.0, unocss to v0.59.4, coloris to v0.24.0 2024-04-23 13:45:59 +10:00
f0d7d89653
fix: position floating buttons relative to help button without wrapping it
(changing help button dom parent makes notion error when inserting elements relative to it)
2024-04-23 13:36:56 +10:00
222d544f88
fix: add .js to electronScripts paths so they actually load in 2024-04-22 23:06:07 +10:00
71f9ecc32b
feat: custom userscripts in-app and in-browser!
firefox support will involve some tweaking, manifest v3 not fully supported yet
2024-04-22 22:58:50 +10:00
607fcee4f8
chore(tweaks): add tweaks + descriptions to updated mod.json 2024-04-21 19:08:38 +10:00
3d7efbef54
fix(cli): remove dup. message if already enhanced 2024-04-21 17:01:30 +10:00
9b03b1d9f7
chore: remove integrated-titlebar (default in notion desktop v3.4.0) 2024-04-21 16:56:09 +10:00
736292e1b8
chore: update sidebar button styles to match notion 2024-04-21 16:32:26 +10:00
a4b1e6e5f2
feat(cli): repack enhanced sources into app.asar 2024-04-21 15:45:29 +10:00
23ea8c1c55
chore: remove draggable tabs toggle (now enabled by default in vanilla) 2024-02-24 23:41:20 +11:00
b910f82acf
fix(panel): switch between multiple views correctly
update single select instance to avoid conflicting rerender listeners
2024-02-24 23:36:43 +11:00
951b199b54
fix(word-counter): handle singular vs. plural stats appropriately 2024-02-24 21:05:48 +11:00
44c480062b
chore: update to support notion 2.3.1 (regex-based replacement for updated webpack bundles)
removed use strict from mjs files, where strict mode is assumed/enforced regardless
2024-02-24 20:57:22 +11:00
ef09280f61
chore: ensure api is loaded before mod scripts execute 2024-02-19 23:08:09 +11:00
1f717b98ca
chore: simplify api imports 2024-02-17 00:27:35 +11:00
f0e2570448
chore: switch to unocss for styling
faster + more powerful + better documented + more active than twind. sticking with custom preset-icons to avoid fetching over cdn + to load custom icons
2024-02-16 18:16:58 +11:00
7fe993d356
feat: add scroll to bottom button #861 2024-02-13 21:41:35 +11:00
f32cf56a67
feat: show scroll progress in outliner #834, fix: jump to headings in columns 2024-02-09 15:06:34 +11:00
d156d810ba
feat: make peak panel on hover configurable 2024-02-09 13:46:10 +11:00
Tom
ad2c2cf199
feat: add "hide default page icons" tweak
merge pull request #819 from @1280px
2024-02-07 11:48:45 +11:00
9a7a814ab8
feat: port word counter extension 2024-02-05 15:43:06 +11:00
3601680c24
fix(topbar): custom topbar icons + window dragging in peek view
i have discovered that extra topbar icons e.g. window min/max/close are not accessible in peek mode. this is probably fine?
2024-02-05 12:59:58 +11:00
6bfe1d8b07
fix(outliner): render tex equations nicely 2024-02-05 12:42:23 +11:00
beb5873788
fix: set exact panel view height to prevent overflow 2024-01-31 23:38:44 +11:00
d8b6aef698
chore: update word counter mod.json w/ configurable speeds 2024-01-31 23:29:46 +11:00
b55e74a2c5
fix: more reliable scroll to top btn initial display + positioning 2024-01-31 23:16:43 +11:00
ba8c660442
feat: scroll to heading on click in outliner 2024-01-31 23:02:04 +11:00
6661c5559b
feat: render headings in outliner 2024-01-31 00:12:14 +11:00
ff1e5f7550
chore: update lucide 2024-01-30 22:37:30 +11:00
c4eacd201c
style: implement size- utility 2024-01-30 22:37:30 +11:00
Tom
4e1a28df3d
#814 standardise outliner indentation
merge pr from @achiyae
2024-01-30 22:28:24 +11:00
d420b2de4e
feat: conditional display of scroll to top btn 2024-01-30 17:57:03 +11:00
twelve eighty pix
848cd86b90
Add "hide default page icons" option 2024-01-26 23:08:41 +07:00
42b2fedacf
feat: add scroll to top button (wip) 2024-01-25 13:14:48 +11:00
aae4c7e663
chore: perf optimisations, overflow topbar to the left when comments & updates sidebars are open 2024-01-25 11:44:28 +11:00
1a709f5a84
feat: merge and update topbar-icons and always-on-top 2024-01-25 00:27:52 +11:00
4df00ca870
feat(titlebar): customisable window control button icons 2024-01-24 15:08:56 +11:00
6e9f0a420f
feat(titlebar): handle window button clicks 2024-01-24 14:46:52 +11:00
f085362774
fix(menu): hide titlebar extension if in browser 2024-01-24 13:21:09 +11:00
54594350bd
fix: no more menu skeleton fouc 2024-01-24 13:09:54 +11:00
ff002044aa
fix: use leading edge debounce to sync up transitions 2024-01-24 12:50:13 +11:00
26c9638013
fix(panel): dont move help button unless panel is pinned 2024-01-24 02:27:53 +11:00
653d7d9bc8
feat(titlebar): render window controls in tabbar if active, otherwise in topbar 2024-01-24 02:24:56 +11:00
346b61eff4
feat(titlebar): make frameless window draggable (w/out extra dragarea) 2024-01-23 23:14:08 +11:00
77322fc8ad
feat: implement frameless windows, fix: smooth out menu transitions 2024-01-23 14:30:47 +11:00
db0f3c5228
chore: remove tray and tabs extensions 2024-01-23 13:16:21 +11:00
bbb60ae76f
feat: toggle window hotkey, reorderable tabs, dev mode
(merged tray & tabs extensions into the core now that the official notion app includes tray & tabs implementations
2024-01-21 00:24:38 +11:00
23f8e3b560
feat(panel): add tooltips to panel toggles 2024-01-20 23:58:12 +11:00
68b73246fb
chore: minor refactoring 2024-01-20 19:48:07 +11:00
69eb2866d5
fix(panel): queue panel close after peek animation ends if no longer hovering 2024-01-18 16:20:18 +11:00
493db7aa2e
fix(panel): debounce peek trigger
e.g. do not show if mouse is moved quickly out of the window
2024-01-18 16:01:46 +11:00
c722c7f854
fix(panel): reliable/not-weird panel animations 2024-01-18 15:32:58 +11:00
a2efca4ca6
feat(panel): peek panel on hover near edge of screen
- regular toggle animation now slightly messed up
- todo make this configurable
2024-01-16 23:47:30 +11:00
Achiya Elyasaf
5ceec8eabf
Update client.mjs
Removed the depth variable that is no longer needed.
2023-11-26 20:38:26 +02:00
Achiya Elyasaf
f7fdb38c8c
Update client.mjs
Fix issue #813 with the outliner extension.
Instead of inc-/dec- rementing the indentation by 18 each iteration, the fix multiplies the header level by 18. This way, jumping from level 3 to level 1 results in the correct indentation,
2023-11-15 09:39:42 +02:00
a88c45cc80
chore: update to support notion 2.2.4 (bundled webpack build) 2023-10-29 20:16:56 +11:00
8ec6094caf
chore(panel): restore cloudhill credit 2023-09-04 22:29:11 +10:00
aed0081990
fix(panel): restore resizeability
css width var had been applied to panel and to panel parent separately, freezing the panel at an initial pos
2023-08-16 00:45:33 +10:00
a75a2d2608
chore: rm old panel srcs, move not-yet-ported srcs to archive folder 2023-08-16 00:30:16 +10:00
319db04403
feat(panel): disable panel if no views available 2023-08-16 00:22:20 +10:00
463607c6d2
feat(panel): register/render custom views to side panel
+ fix: debounce useState callback triggers to prevent over-handling conflicts
+ minor refactoring
2023-08-15 23:55:43 +10:00
608b8d42d4
fix: set min widths on panel content to prevent layout shift on toggle 2023-08-11 00:15:48 +10:00
9f998b5320
feat(menu, panel): improved keyboard navigation for selects + inputs 2023-08-10 23:38:32 +10:00
8745dc9313
feat(panel): render icons in switcher 2023-08-09 23:59:18 +10:00
681d5c13f6
feat(panel): open panel switcher as dropdown 2023-08-09 00:01:10 +10:00
0ad2b2ff54
feat(panel): add wip select to top of panel
- todo: display popup beneath select
- todo: dynamically register values
- todo: add icons
- todo: change view on select change
2023-08-07 12:50:37 +10:00
09b24147c5
fix: delay menu open until dom is ready to preserve transition 2023-08-07 11:48:29 +10:00
1d501dffa5
fix: close panel on handle click only if not dragged, inc. panel transition to 300ms 2023-08-07 00:09:47 +10:00
95483f4f25
fix: wait for api b4 hooking into state 2023-08-06 23:57:37 +10:00
45e0be7d62
refactor: move state.mjs into markup.mjs for use in notion interface 2023-08-06 23:45:56 +10:00
bb5853e866
feat(panel): add open/close gui buttons 2023-08-05 01:09:01 +10:00
94460375bf
feat: make side panel resizeable 2023-08-04 23:59:37 +10:00
8d679ae3c5
feat: add toggleable side panel w/ hotkey + tooltip 2023-08-04 22:46:53 +10:00
19e6d5451c
fix: only re-import dom modules in menu, don't use until post-re-import 2023-08-04 16:14:04 +10:00
4f07420c4a
feat(core): implement dev mode toggle in electron app
+ refactor: merge components/ into islands/ for consistency
2023-08-04 13:13:06 +10:00
e762e68f7f
chore: don't patch forge.config.js (may be useful for notion-repackaged?) 2023-08-04 13:11:32 +10:00
9a1b35afd9
fix(browser): handle port disconnection b/w client and worker 2023-08-03 22:34:18 +10:00
025bbca44c
fix: more resilient ipc for db operations, proper fallback handling
+ minor refactoring of files from api/ to shared/
+ adjusting wording of onboarding terms
+ inject menu button b/w the `settings & members` and the `new page` buttons
2023-08-03 18:26:16 +10:00
a88c74d1c3
chore: update deps 2023-07-31 23:08:14 +10:00
a65fb79878
fix: disable guardAgainstIFrameRequests to enable menu embed 2023-07-31 23:05:02 +10:00
705ba509cb
fix: do not inject to sqlite srcs, fixes infinite loading 2023-07-31 00:54:46 +10:00
f61d13ca91
fix(cli): make args["--json"] state available to print funcs 2023-07-31 00:53:30 +10:00
d3840c5922
???: ...and it doesn't work. i will look at this again later 2023-05-07 23:08:38 +10:00
bf07257ae8
wip: migrate db ops to use ipc (nodeintegration is disabled in latest app) 2023-05-07 22:37:56 +10:00
8e809d4233
fix(theme): stop bg-hover selector targeting checkboxes 2023-03-19 21:27:20 +11:00
2ac684056a
style(menu): round sidebar buttons, update heading styles 2023-03-19 19:50:16 +11:00
38b056429e
feat(menu): indicate upload status of file inputs with label change 2023-03-19 18:43:57 +11:00
5a91e58104
feat(telemetry): send weekly pings if enabled 2023-03-19 15:02:47 +11:00
3e379f44e8
fix: enable telemetry by default 2023-02-04 13:41:14 +11:00
0fff732fd9
chore(menu): remove option type demos, fix(menu): support mods without options, feat(menu): override heading autoremove 2023-02-04 13:34:49 +11:00
809b59ebb1
chore: update onboarding disclaimer, unify telemetry handling, detect firefox vs chromium 2023-02-04 13:10:12 +11:00
374efd3458
draft(menu): featured sponsors spot 2023-02-04 10:48:20 +11:00
6519f501d3
fix: move nord/neutral to themes folder, reword sponsor perks, dont trigger reload btn when agreeing to privacy policy 2023-02-03 21:10:50 +11:00
a6e9504b6f
chore(menu): detect db update by comparing against prev value
- syncs reload btn visibility b/w tabs
- doesn't trigger when switching profiles if identical
2023-02-03 13:04:12 +11:00
ba98ed6412
chore: refactor core/client.mjs, sync telemetry opt-in/out across profiles 2023-02-03 11:52:51 +11:00
3cd8ed7703
feat(menu): indicate updates with popup & notification pings 2023-02-03 01:01:11 +11:00
0daf0a38c2
style(menu): set landing tile bg to bg-secondary for consistency with mod tiles 2023-01-31 22:26:24 +11:00
5640b12139
chore: update vendored deps 2023-01-31 22:17:46 +11:00
f1332fffbd
feat(onboarding): require agreement to privacy policy + ts & cs, add landing tiles 2023-01-31 22:12:29 +11:00
567e678a6f
chore(menu): add telemetry toggle 2023-01-30 23:11:55 +11:00
f999969c13
chore!: store config in electron-defined userData folder instead of home folder
- cli can no longer detect where config will be, so config-related cli prompts have been removed
- should avoid file conflicts and enable portable builds
- users can export their data in the more readable .json format from the profiles section of the menu and no longer require direct access to the config file
2023-01-29 16:07:26 +11:00
f61ac3d8d9
feat(menu): add sponsorship buttons & perks to banner 2023-01-29 14:53:46 +11:00
71a0345768
style(menu): set min width w/ overflow-x: auto 2023-01-28 19:17:36 +11:00
38f59bab31
style(menu): add welcome banner 2023-01-28 18:34:26 +11:00
f943697ba7
fix(menu): preserve profile name on import, enabled repeat imports of same file 2023-01-24 09:23:09 +11:00
50a23f17c5
fix(menu): keyboard triggering of inputs and switches via space/enter, don't trap tab in hotkey input 2023-01-24 00:15:10 +11:00
c3317bd9ec
feat(menu): allow only one theme of each mode to be enabled at a time 2023-01-23 23:56:09 +11:00
7b6a244d72
feat(menu): display profile upload status as popup 2023-01-23 23:43:04 +11:00
e3f34dfc21
refactor(menu): menu.mjs render functions and components.mjs monolith -> islands/ and components/ 2023-01-23 21:58:17 +11:00
c19262a4ce
perf(load): import all deps at once + wait for load.mjs completion, fix(electron): use ipc for reloadApp 2023-01-20 00:33:05 +11:00
72332acc58
feat(menu): profile deletion via confirmation popup 2023-01-19 22:34:41 +11:00
23834475c0
feat(menu): hotswapped profiles w/out reload 2023-01-19 20:14:51 +11:00
106d776d85
feat(menu): render profile list, create and switch b/w profiles 2023-01-18 22:56:49 +11:00
7bafbedc67
feat(menu): add reload btn to footer, move return to category btn to footer 2023-01-16 19:29:53 +11:00
4456871f6d
feat(menu): loading skeleton 2023-01-16 16:47:27 +11:00
f57e5b7f9b
feat(menu): animate slide b/w mod lists <-> options, add enabled mods to sidebar 2023-01-16 13:34:41 +11:00
530be53e70
feat(menu): add searchbar + descriptions to mod lists, fix(menu): natural keyboard interactions w/ <Select> component 2023-01-15 22:54:57 +11:00
a8eb03ee67
refactor(menu): base input variants off dry <Input /> component 2023-01-15 17:48:21 +11:00
570a6f26ab
fix(menu): close select popups w/ esc, toggle menu w/ hotkeys while menu focused 2023-01-15 15:57:15 +11:00
e681d462d3
feat(menu): mimic notion select options popup
electron was positioning options distanced from their <select> for... some reason
2023-01-15 00:39:01 +11:00
d1f83884aa
chore(electron-compat): promisify db methods, import svgs as js instead of reading files 2023-01-14 22:17:28 +11:00
c95d96cd8e
feat(menu): option values update on view change/page focus, animate view changes 2023-01-14 21:38:51 +11:00
765e7b738c
chore(menu): ensure required mod.json fields exist before attempting render 2023-01-14 00:44:09 +11:00
c6d58f37ee
theme: classic dark, feat(menu): render card lists of available mods 2023-01-14 00:40:27 +11:00
d738801dc1
chore(menu): hide platform-specific options, hide empty/consecutive headings 2023-01-13 00:30:14 +11:00
d406f99d7e
feat(menu): show uploaded file name, clear prev uploads button 2023-01-13 00:05:32 +11:00
8d12d63349
feat(menu): save changes to options
file upload still todo
2023-01-12 23:19:06 +11:00
09268a538a
feat(menu): apply menuButtonIconStyle to icon in menu sidebar 2023-01-12 15:37:33 +11:00
8fe05b2888
feat(menu): text, number, hotkey & color input types
- replaced jscolor with coloris
2023-01-12 15:24:01 +11:00
f85b3c1359
feat(menu): clone notion file upload 2023-01-12 11:33:47 +11:00
b1e1405409
feat(menu): clone notion select/dropdown 2023-01-12 00:15:30 +11:00
5db238024a
chore(modloader): avoid repeat logging on menu load 2023-01-11 22:02:25 +11:00
881f69c47d
feat(menu): clone notion ui for core settings, add toggles
- reduce size of icons in sidebar
- keyboard focus outline
- improved distinguishing of attrs vs props
- register twind variants from preset-ext
2023-01-11 21:59:57 +11:00
ac5daf5b73
feat(menu): reactive view navigation 2023-01-11 17:29:57 +11:00
70cd128a46
feat(menu): notion-styled menu sidebar 2023-01-10 22:48:12 +11:00
bb7f044d3a
feat(theme): auto-load theme overrides or force to state 2023-01-09 22:38:16 +11:00
8c935ffb5d
fix(theme): consistent primary vs secondary bgs, mode-agnostic tooltip styling 2023-01-09 22:12:17 +11:00
523e1f0bb4
refactor(theme): pass style overrides and css variables through generator 2023-01-09 13:35:35 +11:00
0fbb85b138
chore: use utility classes for menu modal 2022-12-30 22:15:14 +11:00
b442e40446
feat: trigger menu from browser extension icon 2022-12-30 16:38:30 +11:00
c37877c6da
feat: load api/styles into menu, chore: separate mod types into folders 2022-12-30 15:19:47 +11:00
53362a4cee
feat: --theme--bg-overlay variable, style menu modal w/ variables 2022-12-28 00:41:24 +11:00
c1480c3840
fix: remove extra box-shadow from inline tables 2022-12-24 00:09:34 +11:00
2719751e2b
feat: theme css ~full coverage
- fonts
- interface bgs/borders
- dim bg variants for callouts and board groups
- manually-overriding code bg/fg
- datepicker
2022-12-23 23:57:25 +11:00
ee69d44796
feat: script generation of theme css
- foregrounds
- coloured backgrounds
- accents
- scrollbar
- inline code & code blocks

still todo:
- fonts
- base backgrounds
- borders and outlines
- tooltips
- cards
- buttons
- etc.
2022-12-23 00:26:40 +11:00
29c4fff909
feat: index/gen prism theming variables 2022-12-22 00:35:32 +11:00
5fb4405614
feat: create modal for menu to open within
- replace preact with direct htmlelement creation
2022-12-20 23:45:26 +11:00
40b150722e
feat: render lucide icons via twind 2022-12-20 02:14:31 +11:00
4965b28145
feat: update notion-enhancer icon 2022-12-17 18:10:08 +11:00
c7b384bc89
chore: add htm/preact to deps 2022-12-16 18:24:56 +11:00
a81a4dda6f
refactor: platform-agnostic modloading 2022-12-15 01:04:56 +11:00
d304f698a8
refactor: simplifications
- remove registry validation
- separate core from mods
- use __enhancerApi global for consistent api access
- use key-based namespacing instead of nested objects
2022-12-14 23:15:32 +11:00
44702af188
feat(desktop): use sqlite3 db
should fix the occasional db resets on concurrent read/write ops experienced with the json db
2022-12-13 13:34:24 +11:00
73e3c7c3a9
chore: update deps, add vendor script 2022-12-08 15:48:47 +11:00
Tom
c38c5006e0
merge git modules into monorepo 2022-12-07 20:05:37 +11:00
f8d647a1e6
chore: upload yarn.lock 2022-12-07 20:02:51 +11:00
dc0916ddda
chore: move extension manifest to src/, add build script 2022-12-07 19:56:31 +11:00
b1087a6187
feat(cli): port across apply & remove to updated cli 2022-12-07 19:04:21 +11:00
979616e032
feat(cli): safer patch process to avoid repeat-patching 2022-12-06 23:23:43 +11:00
ebf15dbfb9
feat(cli): ported remove cmd to new cli
+ more detailed error messages inc. alternative instructions for removal if not backup is found
2022-12-06 20:37:39 +11:00
a7861be39a
feat(cli): --quiet flag, split --dev into --json/--debug flags
+ added %PROGRAMW6432%/Notion/resources to potential resource paths (#779)
+ clearer separation of programmatic enhancement and cli interface
+ dep on vercel/arg for improved arg parsing
2022-12-06 15:37:21 +11:00
3e7b6dd6f8
chore: organise folders 2022-11-25 16:00:54 +11:00
92cc026904
rm submodule fetching workflow 2022-11-25 15:41:55 +11:00
7a065b1e69
rm .gitmodules 2022-11-25 15:40:17 +11:00
d704e08012
remove extension submodules 2022-11-25 15:38:49 +11:00
9c51542e50
Merge submodule contents for extension/dev 2022-11-25 15:37:32 +11:00
5c227381ea
Remove submodule extension 2022-11-25 15:36:51 +11:00
980e1c33aa
add extension submodule 2022-11-25 15:35:31 +11:00
89025f9fb6
Merge submodule contents for repo/dev 2022-11-25 15:22:40 +11:00
eaa4fb9d35
Remove submodule repo 2022-11-25 15:22:11 +11:00
016ed600b0
Merge submodule contents for dep/main 2022-11-25 15:22:06 +11:00
fd907a2ceb
Remove submodule dep 2022-11-25 15:21:46 +11:00
da30befa5c
Merge submodule contents for api/dev 2022-11-25 15:21:36 +11:00
22787bb44e
Remove submodule api 2022-11-25 15:21:15 +11:00
b574411edb
Merge submodule contents for media/main 2022-11-25 15:20:55 +11:00
1d43acf79e
Remove submodule media 2022-11-25 15:20:34 +11:00
31a2300913
mv submodules to root 2022-11-25 15:20:26 +11:00
dragonwocky
c1ba18b93e [repo] Merge pull request #100 from eldrickb/dev
Fix weekly-view for new Notion databases
2022-11-25 03:32:15 +00:00
dragonwocky
fd112e7b1a [repo] Merge pull request #100 from eldrickb/dev
Fix weekly-view for new Notion databases
2022-11-25 03:32:13 +00:00
Tom
d4a595a6ec Merge pull request #100 from eldrickb/dev
Fix weekly-view for new Notion databases
2022-11-25 14:31:48 +11:00
dragonwocky
3ca816fef5 [repo] Merge pull request #119 from DarkOnion0/dev
fix: make the notion-enhancer menu visible again
2022-11-25 03:31:29 +00:00
dragonwocky
0c5cc6321b [repo] Merge pull request #119 from DarkOnion0/dev
fix: make the notion-enhancer menu visible again
2022-11-25 03:31:20 +00:00
Tom
57b47b82aa Merge pull request #125 from amv146/bypass-preview-fix
Fixed bypass preview
2022-11-25 14:31:18 +11:00
Tom
7e9cd1b581 Merge pull request #119 from DarkOnion0/dev
fix: make the notion-enhancer menu visible again
2022-11-25 14:31:01 +11:00
Tom
e1ac1bfcd6
merge branches main and dev
merge branches main and dev
2022-11-25 14:29:47 +11:00
Tom
974f7ec63b
Merge pull request #763 from kidonng/patch-2
Add trailing semicolon when appending to preload script
2022-11-25 14:28:46 +11:00
Alex Vallone
2c89ced97e Fixed bypass preview 2022-09-01 21:45:06 -07:00
Alexo
70abec5630 fix: make the notion-enhancer menu visible again 2022-08-01 16:37:44 +00:00
Kid
f20e79cf3b
Add trailing semicolon when appending to preload script 2022-06-05 15:41:11 +08:00
pr3cast
fbc9709b66 Fix more code consistency in weekly-view 2022-03-21 15:18:25 -04:00
pr3cast
b921a2f86a Fix code consistentcy in weekly-view client 2022-03-21 15:06:40 -04:00
pr3cast
58d3030bf5 Clean up and comment new weekly-view client code 2022-03-21 14:52:01 -04:00
pr3cast
d884f146b3 Fix weekly-view view targeting for new Notion databases 2022-03-21 14:51:31 -04:00
dragonwocky
c806bfd5be [repo] tweaks: fix #32 wrap tables to page width 2022-03-10 11:54:21 +00:00
dragonwocky
5fd7daec9c [repo] tweaks: fix #32 wrap tables to page width 2022-03-10 11:54:21 +00:00
4e2253d0ac tweaks: fix #32 wrap tables to page width 2022-03-10 22:53:50 +11:00
dragonwocky
532e51abdd [repo] collapsible headers: deprecate in favour of toggle headers 2022-03-10 11:41:03 +00:00
dragonwocky
f7a708d8da [repo] collapsible headers: deprecate in favour of toggle headers 2022-03-10 11:41:02 +00:00
ec5af9735d collapsible headers: deprecate in favour of toggle headers 2022-03-10 22:40:34 +11:00
dragonwocky
854c15b61b [repo] indentation lines: fix #51, full support for toggle headers 2022-03-10 11:37:00 +00:00
dragonwocky
98ef8fa926 [repo] indentation lines: fix #51, full support for toggle headers 2022-03-10 11:36:59 +00:00
ad248ece6b indentation lines: fix #51, full support for toggle headers 2022-03-10 22:36:21 +11:00
dragonwocky
58b3b22274 [repo] Merge pull request #72 from amv146/dev
toggle header indentation lines, adjacent tab nav hotkeys
2022-03-10 10:56:33 +00:00
dragonwocky
db22ab5f0e [repo] Merge pull request #72 from amv146/dev
toggle header indentation lines, adjacent tab nav hotkeys
2022-03-10 10:56:30 +00:00
Tom
7358873fba Merge pull request #72 from amv146/dev
toggle header indentation lines, adjacent tab nav hotkeys
2022-03-10 21:56:12 +11:00
dragonwocky
d4dd19e1db [repo] fix #28: overlap b/w tabs and macos window buttons 2022-03-10 10:50:34 +00:00
dragonwocky
c607b1a705 [repo] fix #28: overlap b/w tabs and macos window buttons 2022-03-10 10:50:30 +00:00
7993f0f257 fix #28: overlap b/w tabs and macos window buttons 2022-03-10 21:50:03 +11:00
442d6d3427
fix #684: normalise target paths on windows 2022-03-10 21:30:46 +11:00
amv146
300f876a28 Added Ctrl-Tab and Ctrl-Shift-Tab functionality 2022-02-08 20:06:32 -08:00
amv146
6c1e6ffbce toggle header support added 2022-02-08 17:52:34 -08:00
dragonwocky
8d317c9381 [repo] pr #47 by @blorbb: colour fixes
light gray tags missing - #42

gruvbox dark tag colours not applying - #35
list and timeline tag colours not applying - #35

also added: apply theme variables to the palette of colours when selecting a tag/background colour
2022-01-21 01:20:36 +00:00
dragonwocky
a2a5649327 [repo] pr #47 by @blorbb: colour fixes
light gray tags missing - #42

gruvbox dark tag colours not applying - #35
list and timeline tag colours not applying - #35

also added: apply theme variables to the palette of colours when selecting a tag/background colour
2022-01-21 01:20:33 +00:00
Tom
b93f730170 pr #47 by @blorbb: colour fixes
light gray tags missing - #42

gruvbox dark tag colours not applying - #35
list and timeline tag colours not applying - #35

also added: apply theme variables to the palette of colours when selecting a tag/background colour
2022-01-21 12:20:13 +11:00
blorbb
43bcc8101c add tag_default back 2022-01-21 12:04:10 +11:00
waaaaaaaaaaaaaaaaaaaaa
48fa479001 add light_gray colours 2022-01-06 11:55:08 +11:00
waaaaaaaaaaaaaaaaaaaaa
6d598bf6e3 Merge branch 'dev' of https://github.com/blorbb/repo into dev 2022-01-06 10:25:32 +11:00
waaaaaaaaaaaaaaaaaaaaa
ed743d2f65 fix tag colours not applying in list and timeline 2022-01-06 10:21:40 +11:00
waaaaaaaaaaaaaaaaaaaaa
ce500c7f8e fix tag colours not applying in list and timeline views 2022-01-05 23:08:32 +11:00
waaaaaaaaaaaaaaaaaaaaa
d8999da4a6 Merge branch 'dev' of https://github.com/blorbb/repo into dev 2022-01-05 18:59:36 +11:00
waaaaaaaaaaaaaaaaaaaaa
91986905c8 update to light_gray 2022-01-05 18:59:31 +11:00
waaaaaaaaaaaaaaaaaaaaa
69ca30a105 update to light_gray 2022-01-05 14:52:18 +11:00
waaaaaaaaaaaaaaaaaaaaa
195c0917ff rename to light_gray 2022-01-05 11:24:50 +11:00
waaaaaaaaaaaaaaaaaaaaa
6178c59951 add custom colours to tag and highlight previews 2022-01-04 15:09:47 +11:00
waaaaaaaaaaaaaaaaaaaaa
eb7e840ad8 add light gray, testing 2022-01-04 12:38:15 +11:00
dragonwocky
fe41861a5e [repo] Merge pull request #26 from EliWimmer/patch-1
Fixed grouped DB headings not receiving bg color
2022-01-01 01:14:15 +00:00
dragonwocky
8e9442336d [repo] Merge pull request #26 from EliWimmer/patch-1
Fixed grouped DB headings not receiving bg color
2022-01-01 01:14:14 +00:00
Tom
0fe314420c Merge pull request #26 from EliWimmer/patch-1
Fixed grouped DB headings not receiving bg color
2022-01-01 12:13:57 +11:00
Eli
6f4774391b Fixed grouped DB headings not receiving bg color
Thoroughly tested every DB view and group type and the style z-index: 84 does appear to be exclusive to this element.
2021-12-31 18:01:59 -07:00
4f222a3b73
npm publish 2021-12-31 23:43:04 +11:00
5b8f37f226 v0.11.0 2021-12-31 20:47:46 +11:00
2ec3d6db1c readme: website -> browser 2021-12-31 20:47:34 +11:00
f417de519a
v0.11.0 2021-12-31 20:39:01 +11:00
dragonwocky
832db26d2e [media] upload chrome webstore promo imgs 2021-12-29 07:14:50 +00:00
dragonwocky
3eb704cb5c [media] upload chrome webstore promo imgs 2021-12-29 07:14:49 +00:00
74e21aa4be upload chrome webstore promo imgs 2021-12-29 18:14:17 +11:00
dragonwocky
a4584c2a19 [repo] theming: remove block on left of table head 2021-12-29 04:40:56 +00:00
dragonwocky
bef2ec8106 [repo] theming: remove block on left of table head 2021-12-29 04:40:49 +00:00
c8163ef2bd theming: remove block on left of table head 2021-12-29 15:40:23 +11:00
dragonwocky
d66446285b [repo] adjust menu input icon position 2021-12-29 04:26:02 +00:00
dragonwocky
57f2337d51 [repo] adjust menu input icon position 2021-12-29 04:25:57 +00:00
33aaf8b50d adjust menu input icon position 2021-12-29 15:25:30 +11:00
825020bdae remove unnecessary host permissions 2021-12-28 23:00:13 +11:00
6fc767b5a2 remove unnecessary async in worker 2021-12-28 22:44:59 +11:00
1acaac2484 update README with link to new website 2021-12-28 16:28:34 +11:00
207757644a
update README with link to new website 2021-12-28 16:25:30 +11:00
dragonwocky
2fd4b30319 [repo] change donation link, update README with link to new website 2021-12-28 05:24:09 +00:00
dragonwocky
ee6c1f5ee2 [repo] change donation link, update README with link to new website 2021-12-28 05:24:09 +00:00
38b2bcc47b change donation link, update README with link to new website 2021-12-28 16:23:46 +11:00
dragonwocky
00702dffbd [api] update README with link to new website 2021-12-28 05:23:29 +00:00
dragonwocky
6dec43e649 [api] update README with link to new website 2021-12-28 05:23:26 +00:00
dragonwocky
68c8a71c76 [dep] update README with link to new website 2021-12-28 05:23:20 +00:00
01abef3e30 update README with link to new website 2021-12-28 16:23:19 +11:00
dragonwocky
6282377416 [dep] update README with link to new website 2021-12-28 05:23:19 +00:00
0c337cae3d update README with link to new website 2021-12-28 16:22:52 +11:00
21d5660ec3 update README with link to new website 2021-12-28 16:21:29 +11:00
4f0204aee3
make --patch available, remove cli sign 2021-12-28 02:31:44 +11:00
7e4febd730 board view + code caption + calendar select colour patches 2021-12-28 02:29:56 +11:00
dragonwocky
94f1d9bf84 [repo] update welcome message 2021-12-27 08:00:13 +00:00
a000e73847 update welcome message 2021-12-27 18:57:08 +11:00
3f5ff3642d env jsdoc changes 2021-12-24 23:36:46 +11:00
e84967ae6c
env jsdoc changes 2021-12-24 23:30:55 +11:00
dragonwocky
eb8673c484 [repo] icons/tweaks page links, remove unnecessary async, update profiles explanation 2021-12-24 12:30:24 +00:00
dragonwocky
88acc12530 [repo] icons/tweaks page links, remove unnecessary async, update profiles explanation 2021-12-24 12:30:21 +00:00
10a1ba4099 icons/tweaks page links, remove unnecessary async, update profiles explanation 2021-12-24 23:29:59 +11:00
dragonwocky
170a1e3eda [api] minor cleanups, jsdoc changes 2021-12-24 12:29:02 +00:00
dragonwocky
0ff214bce5 [api] minor cleanups, jsdoc changes 2021-12-24 12:28:55 +00:00
0052fa4cdd minor cleanups, jsdoc changes 2021-12-24 23:28:33 +11:00
dragonwocky
8fa498ef95 [api] var -> const 2021-12-20 01:14:30 +00:00
dragonwocky
6f2018bcd1 [api] var -> const 2021-12-20 01:14:29 +00:00
081e59bb78 var -> const 2021-12-20 12:14:03 +11:00
dragonwocky
d3ad47d152 [dep] remove prism.js 2021-12-20 01:07:05 +00:00
dragonwocky
d425c7b228 [dep] remove prism.js 2021-12-20 01:07:00 +00:00
0f912ebef8 remove prism.js 2021-12-20 12:06:34 +11:00
34189f6a41
mark bin.mjs as executable 2021-12-14 21:13:13 +11:00
dragonwocky
7eb7f1be12 [repo] detect tab icons when sidebar closed, weekly view support expanded cal rows 2021-12-14 10:12:17 +00:00
dragonwocky
0a7a45baf3 [repo] detect tab icons when sidebar closed, weekly view support expanded cal rows 2021-12-14 10:12:14 +00:00
2724f3d859 detect tab icons when sidebar closed, weekly view support expanded cal rows 2021-12-14 21:11:47 +11:00
e900bdb681
reduce multi-process file op interference 2021-12-14 11:58:21 +11:00
50a77d05a1
electron fs: use notion:// for api reqs instead of http:// 2021-12-13 18:40:26 +11:00
dragonwocky
5ea1e70813 [api] electron fs: use notion:// for api reqs instead of http:// 2021-12-13 07:40:12 +00:00
dragonwocky
a44ae9f4f1 [api] electron fs: use notion:// for api reqs instead of http:// 2021-12-13 07:40:10 +00:00
caa0117318 electron fs: use notion:// for api reqs instead of http:// 2021-12-13 18:39:41 +11:00
dragonwocky
068199dbaf [repo] [imgbot] optimize screenshots 2021-12-13 07:14:07 +00:00
dragonwocky
df2719ed71 [repo] [imgbot] optimize screenshots 2021-12-13 07:14:03 +00:00
Tom
3753126c26 [imgbot] optimize screenshots 2021-12-13 18:13:45 +11:00
ImgBotApp
e5ac4e204f [ImgBot] Optimize images
*Total -- 154.67kb -> 118.21kb (23.57%)

/integrated-titlebar/integrated-titlebar.jpg -- 19.25kb -> 8.38kb (56.46%)
/truncated-titles/truncated-titles.jpg -- 10.88kb -> 5.63kb (48.2%)
/always-on-top/always-on-top.jpg -- 8.18kb -> 4.74kb (42.03%)
/tray/tray.jpg -- 12.59kb -> 8.88kb (29.44%)
/view-scale/view-scale.jpg -- 7.05kb -> 5.29kb (25.03%)
/tabs/tabs.jpg -- 27.36kb -> 23.54kb (13.95%)
/collapsible-properties/collapsible-properties.jpg -- 69.36kb -> 61.75kb (10.98%)

Signed-off-by: ImgBotApp <ImgBotHelp@gmail.com>
2021-12-13 07:13:02 +00:00
dragonwocky
5e15e8cfa5 [repo] desktop-only ext. screenshots + fix custom icons in tab 2021-12-13 07:02:47 +00:00
dragonwocky
670ea3f178 [repo] desktop-only ext. screenshots + fix custom icons in tab 2021-12-13 07:02:43 +00:00
179dbabd5a desktop-only ext. screenshots + fix custom icons in tab 2021-12-13 18:02:17 +11:00
Tom
1a03c08b36
merge v0.11.0 rework into dev 2021-12-13 14:24:08 +11:00
b1351069b9
delete everything: prep for merge 2021-12-13 14:23:33 +11:00
b8647deede
sign cli cmd (macos), bugfix error printing, fs dep on __enhancerElectronApi, actually apply systemMenu patch 2021-12-13 13:36:06 +11:00
dragonwocky
f0e8bff9ec [api] make fs ops depend on __enhancerElectronApi for relative paths
(update bundled index.cjs)
2021-12-13 02:35:09 +00:00
2775fab5d4 make fs ops depend on __enhancerElectronApi for relative paths
(update bundled index.cjs)
2021-12-13 13:34:39 +11:00
dragonwocky
33856dea20 [repo] tabs: remember last open 2021-12-12 12:56:38 +00:00
dd463ee9d8 tabs: remember last open 2021-12-12 23:56:09 +11:00
3af07ca21a
notion window helpers, pull submodules 2021-12-12 23:13:17 +11:00
dragonwocky
2a8ab5c51e [api] strict hotkey modifiers + electron window helpers 2021-12-12 12:13:05 +00:00
2428a42103 strict hotkey modifiers + electron window helpers 2021-12-12 23:12:38 +11:00
dragonwocky
a925981b16 [repo] prevent attempts to open notion urls in menu window 2021-12-12 12:12:08 +00:00
01dc6d4bb7 prevent attempts to open notion urls in menu window 2021-12-12 23:11:41 +11:00
dragonwocky
d0a0b90274 [repo] tabs: restore prev. hotkey 2021-12-12 11:24:48 +00:00
ab300633ec tabs: restore prev. hotkey 2021-12-12 22:24:13 +11:00
dragonwocky
bc73baa86a [repo] theme tag colours in selection input 2021-12-12 06:18:24 +00:00
af977b1bfa theme tag colours in selection input 2021-12-12 17:17:59 +11:00
dragonwocky
b2c46d7fbe [repo] tabs: intercept ctrl+click & 'open in desktop app'
create a new tab instead of a new window
2021-12-12 06:10:42 +00:00
fc4cd900cf tabs: intercept ctrl+click & 'open in desktop app'
create a new tab instead of a new window
2021-12-12 17:10:11 +11:00
57c4d63275
handle proper loading of frame.mjs & frame.css 2021-12-12 13:33:38 +11:00
dragonwocky
041980aca4 [repo] focus mode + tabs integration 2021-12-12 02:33:30 +00:00
01ae8d67a2 focus mode + tabs integration 2021-12-12 13:32:59 +11:00
e4943d9abb
patch systemMenu: mod window menu template 2021-12-11 18:04:03 +11:00
dragonwocky
0169506c49 [api] tooltip text-align: auto -> start 2021-12-11 07:03:29 +00:00
8cc057a5f8 tooltip text-align: auto -> start 2021-12-11 18:03:05 +11:00
dragonwocky
7cd96cbc7b [repo] tab hotkeys: close, open, select + better drag indicator 2021-12-11 07:02:51 +00:00
2ffc6e1e12 tab hotkeys: close, open, select + better drag indicator 2021-12-11 18:02:22 +11:00
dragonwocky
961b79ad12 [api] make tooltip text alignment match relative pos 2021-12-11 03:20:42 +00:00
4fcb467351 make tooltip text alignment match relative pos 2021-12-11 14:20:14 +11:00
dragonwocky
f6ff48f072 [repo] tab layout & label styles 2021-12-11 03:20:04 +00:00
3e07f23128 tab layout & label styles 2021-12-11 14:19:08 +11:00
9bf68ca679
attempt to reduce fs conflicts (storage)
needs more work... interference b/w processes corrupting file and resetting storage?
2021-12-10 23:40:05 +11:00
dragonwocky
a4c29b4056 [api] remove hasFocus checks: not all events were being responded to 2021-12-10 12:39:17 +00:00
e4187700b2 remove hasFocus checks: not all events were being responded to 2021-12-10 23:38:47 +11:00
dragonwocky
2feb7b9e0a [repo] tabs: inter-window drag, titles & icons 2021-12-10 12:37:28 +00:00
cb7838350f tabs: inter-window drag, titles & icons 2021-12-10 23:36:56 +11:00
dragonwocky
13d9b484c9 [api] perf: only observe if window/document/tab is focused 2021-12-10 03:41:58 +00:00
d984c8bc36 perf: only observe if window/document/tab is focused 2021-12-10 14:41:29 +11:00
dragonwocky
7f5815bb92 [repo] draggable & slide-in/out tabs 2021-12-10 03:41:20 +00:00
2dcfef0b6b draggable & slide-in/out tabs 2021-12-10 14:40:47 +11:00
3e927e75b8
imitate notion ipc channels, pull submodules 2021-12-10 00:06:09 +11:00
dragonwocky
f0a5f83255 [repo] basic tabs: open/close/navigate between, integrated titlebar btns 2021-12-09 13:05:54 +00:00
58b44d556e basic tabs: open/close/navigate between, integrated titlebar btns 2021-12-10 00:05:28 +11:00
dragonwocky
74b0ae9f65 [api] optionally imitate notion ipc channels 2021-12-09 13:05:09 +00:00
9b47ede63e optionally imitate notion ipc channels 2021-12-10 00:04:36 +11:00
dragonwocky
09a86e074c [repo] stricter theme application, ipc broadcasting 2021-12-09 10:15:24 +00:00
eaf9c001dd stricter theme application, ipc broadcasting 2021-12-09 21:14:49 +11:00
e1190fe3f8
fs.notionPath, pull submodules 2021-12-08 22:42:03 +11:00
dragonwocky
d334b6490d [repo] tabs progress: recreate default webview + search 2021-12-08 11:41:47 +00:00
e071940a0d tabs progress: recreate default webview + search 2021-12-08 22:41:17 +11:00
dragonwocky
53589378b2 [api] fs.notionPath, minify index.cjs 2021-12-08 11:40:43 +00:00
21d4991f38 fs.notionPath, minify index.cjs 2021-12-08 22:40:09 +11:00
1f0a738610
esbuild mjs api -> cjs, simplify env folder to dep. fully on __enhancerElectronApi 2021-12-07 23:18:25 +11:00
dragonwocky
9a574bee3a [repo] ctrl+click open in new window, style ctrl+f popup 2021-12-07 12:18:06 +00:00
c32b41021b ctrl+click open in new window, style ctrl+f popup 2021-12-07 23:17:19 +11:00
dragonwocky
6c3e2b941a [api] use esbuild to compile .mjs to .cjs 2021-12-07 11:13:17 +00:00
b770eb64f0 use esbuild to compile .mjs to .cjs 2021-12-07 22:12:46 +11:00
3b787c1aa4
menu: handle external links, force open new windows 2021-12-05 17:54:29 +11:00
dragonwocky
e466ceff7a [repo] added tooltips to topbar mods, centered/tightened menu nav 2021-12-05 06:54:02 +00:00
16f1510779 added tooltips to topbar mods, centered/tightened menu nav 2021-12-05 17:53:39 +11:00
dragonwocky
2a4083c511 [api] overhauled tooltips: directional positioning, wrapping to max lines 2021-12-05 06:53:20 +00:00
24bc8bda66 overhauled tooltips: directional positioning, wrapping to max lines 2021-12-05 17:52:50 +11:00
dragonwocky
75311ddc98 [repo] prevent topbar collapse, api.electron, treat menu as hideable window 2021-12-05 02:39:28 +00:00
d5a38c80ea prevent topbar collapse, api.electron, treat menu as hideable window 2021-12-05 13:38:58 +11:00
2732d00825
allow menu to run in background via the tray mod 2021-12-04 23:24:21 +11:00
dragonwocky
761ae8f394 [api] add api.electron to access __enhancerElectronApi 2021-12-04 06:36:35 +00:00
7abbb779f5 add api.electron to access __enhancerElectronApi 2021-12-04 17:36:08 +11:00
dragonwocky
1bd52d42db [repo] make view scale desktop-only (browsers have zoom builtin already) 2021-12-04 02:14:10 +00:00
ec91fb6bf0 make view scale desktop-only (browsers have zoom builtin already) 2021-12-04 13:13:42 +11:00
d3b4461b49
update changelog, add webFrame to electronApi 2021-12-04 13:11:17 +11:00
dragonwocky
32b9732139 [repo] extension: view scale by @SP12893678 2021-12-04 02:11:12 +00:00
SP12893678
e79de360f1
extension: view-scale (#532)
* add a mod that can use to scale the notion view

* fix bugs and add hotkey & mouse wheel options

* add select_modifier option and add resize event to fix bugs

* add silder ui to scale view
2021-12-04 13:10:54 +11:00
2ec9808e34 extension: view scale by @SP12893678 2021-12-04 13:10:41 +11:00
dragonwocky
8d8fe96c68 [repo] desktop-only extension: always on top (inc. customisable icons) 2021-12-03 03:12:04 +00:00
eca85f7a7d desktop-only extension: always on top (inc. customisable icons) 2021-12-03 14:11:35 +11:00
a9a6c33436
expose isMenuOpen from worker 2021-12-03 00:56:51 +11:00
97ef140789 tray: open on startup, handle notion:// protocol 2021-12-03 00:55:49 +11:00
dragonwocky
8f936c3d7d [repo] tray: hotkey, run in background
window management features patched to reduce unexpected behaviour e.g. duplicate windows
2021-12-02 13:01:32 +00:00
e761b309bf tray: hotkey, run in background
window management features patched to reduce unexpected behaviour e.g. duplicate windows
2021-12-03 00:00:16 +11:00
Solirpa
895334c640
fix open duplicate notion windows when click dock notion icon on macos bigsur (#487) 2021-12-02 18:53:08 +11:00
da70a3ed4f
allow modification of parent file globals via __eval 2021-12-02 17:59:44 +11:00
dragonwocky
6625c9a328 [repo] desktop-only extension: integrated titlebar (inc. customisable icons) 2021-12-02 06:58:48 +00:00
7f53445ab0 desktop-only extension: integrated titlebar (inc. customisable icons) 2021-12-02 17:58:24 +11:00
dragonwocky
40c0b8acb3 [api] fix cjs internal reference to registry.get 2021-12-02 06:58:04 +00:00
39f66c5bb8 fix cjs internal reference to registry.get 2021-12-02 17:57:37 +11:00
dragonwocky
0eb13f3eac [repo] use .reverse()[0] instead of .at(-1): not supported in older app env 2021-11-30 12:04:31 +00:00
022613d66b use .reverse()[0] instead of .at(-1): not supported in older app env 2021-11-30 23:04:02 +11:00
507731cefa
pull refactors/fixes from other repos, push license 2021-11-30 22:57:44 +11:00
dragonwocky
801d79542b [repo] icon sets: encode imgs to data url to prevent quality reduction 2021-11-30 11:47:10 +00:00
8f8dafeb70 icon sets: encode imgs to data url to prevent quality reduction 2021-11-30 22:46:41 +11:00
dragonwocky
6d6ac532eb [repo] outliner: more reliable page detection 2021-11-30 10:19:50 +00:00
da85adcd33 outliner: more reliable page detection 2021-11-30 21:19:22 +11:00
dragonwocky
fb00c09cbe [api] push mit license, corner action color vars 2021-11-30 09:44:33 +00:00
461533fa07 push mit license, corner action color vars 2021-11-30 20:44:04 +11:00
dragonwocky
7ecf07fd0c [repo] patch notion theme changes
bar at top of db, cards, db groups, simple tables, calendar tags, calendar selection, code captions, corner actions, etc.
2021-11-30 09:43:55 +00:00
7bdae2faa6 patch notion theme changes
bar at top of db, cards, db groups, simple tables, calendar tags, calendar selection, code captions, corner actions, etc.
2021-11-30 20:43:20 +11:00
dragonwocky
0f0e234ae7 [repo] collapsible properties: remove 'hide/show' from button title 2021-11-30 07:14:49 +00:00
a90c5fe946 collapsible properties: remove 'hide/show' from button title 2021-11-30 18:14:23 +11:00
dragonwocky
0429da10a4 [api] update component stylesheet paths 2021-11-30 07:14:04 +00:00
8afc70da95 update component stylesheet paths 2021-11-30 18:13:39 +11:00
80a4b66128 _.mjs -> index.mjs, fix storage callback doc 2021-11-30 18:12:04 +11:00
dragonwocky
19c2da54c3 [repo] update imports for api folder structure 2021-11-30 07:01:45 +00:00
6874b905eb update imports for api folder structure 2021-11-30 18:01:15 +11:00
dragonwocky
a0c54a3ea3 [api] cleanup folder structure, fix storage callback doc 2021-11-30 07:00:24 +00:00
b6b997ca23 cleanup folder structure, fix storage callback doc 2021-11-30 17:59:48 +11:00
dragonwocky
390613f1e7 [repo] misc fixes (see desc)
themes: remove mono text hue rotate
word-counter/outliner: update properly on focus
collapsible headers: dont error if addedNodes is text node
theming: style quick search, force help button opaque
2021-11-08 11:20:57 +00:00
6ee8086b51 misc fixes (see desc)
themes: remove mono text hue rotate
word-counter/outliner: update properly on focus
collapsible headers: dont error if addedNodes is text node
theming: style quick search, force help button opaque
2021-11-08 22:20:30 +11:00
dragonwocky
cc34222e6f [api] remove conflict with corner buttons 2021-11-08 11:16:01 +00:00
6008623812 remove conflict with corner buttons 2021-11-08 22:15:35 +11:00
3f57f01f55
fix env fs, electron js init, handle search/hash in scheme 2021-11-07 23:40:44 +11:00
dragonwocky
bd94a6bb93 [api] require() just before use 2021-11-07 11:57:54 +00:00
5cab38d07c require() just before use 2021-11-07 22:57:21 +11:00
616686b3c8 move changelog 2021-11-07 18:57:22 +11:00
4df4235ad2
create menu window 2021-11-07 18:54:08 +11:00
dragonwocky
1337c3619c [repo] scope all await statements 2021-11-07 07:53:50 +00:00
a72441ee86 scope all await statements 2021-11-07 18:53:18 +11:00
dragonwocky
6be658b41f [api] env.notionRequire 2021-11-07 07:49:08 +00:00
bf3948f9c5 env.notionRequire 2021-11-07 18:48:38 +11:00
d8cec4368d
patch scheme, init client, cjs api, env storage
hidden --dev-patch apply opt
2021-11-07 16:10:42 +11:00
dragonwocky
21f588a289 [api] port to cjs 2021-11-07 05:08:51 +00:00
0aabc67774 port to cjs 2021-11-07 16:08:22 +11:00
dragonwocky
0ab9cb127f [dep] add mime db (stream protocol intercept) 2021-11-06 06:49:41 +00:00
ba6008efd7 add mime db (stream protocol intercept) 2021-11-06 17:49:10 +11:00
dragonwocky
aedad789c6 [media] link to notion-enhancer/desktop repo 2021-11-06 05:11:06 +00:00
781686f78b link to notion-enhancer/desktop repo 2021-11-06 16:10:53 +11:00
dragonwocky
099723d469 [repo] link to notion-enhancer/desktop repo 2021-11-06 05:10:52 +00:00
302408f265 link to notion-enhancer/desktop repo 2021-11-06 16:10:43 +11:00
496305597a link to notion-enhancer/desktop repo 2021-11-06 16:10:25 +11:00
dragonwocky
83ae3a0e4c [api] link to notion-enhancer/desktop repo 2021-11-06 05:10:21 +00:00
671266fff0 link to notion-enhancer/desktop repo 2021-11-06 16:07:39 +11:00
da2be9d7c9
link submodules 2021-11-06 15:59:15 +11:00
affa314f08
new cli 2021-11-06 14:26:35 +11:00
8306472bbd rename launcher to init 2021-11-06 11:13:31 +11:00
dragonwocky
599d15bbff [repo] update menu colour var mapping 2021-11-06 00:08:17 +00:00
b91ce6f56c update menu colour var mapping 2021-11-06 11:07:45 +11:00
Jason Antwi-Appah
b1b1a1b725
Add support for user-only Notion installs on macOS (#375)
* feat(macOS): add support for user-only Notion installs

* feat(macOS): add support for user-only Notion installs

* fixing an accidental change
2021-11-03 22:14:53 +11:00
dragonwocky
5a796ab68d [repo] corner button cross-theme readability 2021-11-03 10:53:27 +00:00
e3b598dc00 corner button cross-theme readability 2021-11-03 21:53:21 +11:00
f1705d97fb corner button cross-theme readability 2021-11-03 21:52:59 +11:00
dragonwocky
1a6ffa8b4e [repo] update themes to match new colour variables 2021-11-02 11:19:59 +00:00
a4119cd3b9 update themes to match new colour variables 2021-11-02 22:19:25 +11:00
dragonwocky
a6663ceea8 [repo] light board colors 2021-11-02 09:22:23 +00:00
cd0a4d1ba3 light board colors 2021-11-02 20:21:55 +11:00
dragonwocky
8d1fb6ec56 [repo] map updated notion colours to theming system 2021-11-02 06:40:52 +00:00
e06b320ae6 map updated notion colours to theming system 2021-11-02 17:40:26 +11:00
dragonwocky
79bae0116c [repo] optimise preview imgs 2021-11-01 23:46:07 +00:00
Tom
177c16bce6 optimise preview imgs 2021-11-02 10:45:30 +11:00
dragonwocky
d8806fa15e [repo] Merge pull request #1 from notion-enhancer/imgbot
[ImgBot] Optimize images
2021-11-01 23:44:07 +00:00
ImgBotApp
9c4f24d4fa [ImgBot] Optimize images
*Total -- 2,184.53kb -> 1,463.27kb (33.02%)

/code-line-numbers/code-line-numbers.png -- 19.24kb -> 5.80kb (69.86%)
/indentation-lines/indentation-lines.jpg -- 11.01kb -> 3.50kb (68.19%)
/outliner/outliner.png -- 56.84kb -> 23.91kb (57.93%)
/quick-note/quick-note.png -- 6.14kb -> 3.16kb (48.47%)
/scroll-to-top/scroll-to-top.png -- 7.39kb -> 4.23kb (42.81%)
/word-counter/word-counter.jpg -- 96.53kb -> 58.77kb (39.12%)
/nord/nord.png -- 128.75kb -> 80.83kb (37.22%)
/pinky-boom/pinky-boom.png -- 127.48kb -> 80.19kb (37.1%)
/gruvbox-light/gruvbox-light.png -- 128.38kb -> 80.83kb (37.04%)
/light+/light+.png -- 128.08kb -> 80.97kb (36.78%)
/dracula/dracula.png -- 130.01kb -> 82.51kb (36.54%)
/playful-purple/playful-purple.png -- 129.07kb -> 82.17kb (36.34%)
/cherry-cola/cherry-cola.png -- 128.49kb -> 82.03kb (36.16%)
/material-ocean/material-ocean.png -- 129.12kb -> 82.61kb (36.02%)
/gruvbox-dark/gruvbox-dark.png -- 128.07kb -> 82.22kb (35.8%)
/neutral/neutral.png -- 122.60kb -> 79.65kb (35.03%)
/pastel-dark/pastel-dark.png -- 125.10kb -> 81.53kb (34.83%)
/dark+/dark+.png -- 125.41kb -> 81.94kb (34.66%)
/right-to-left/right-to-left.jpg -- 19.99kb -> 13.47kb (32.61%)
/calendar-scroll/calendar-scroll.png -- 19.31kb -> 14.18kb (26.58%)
/topbar-icons/topbar-icons.jpg -- 2.19kb -> 1.70kb (22.42%)
/weekly-view/weekly-view.jpg -- 24.93kb -> 19.67kb (21.11%)
/simpler-databases/simpler-databases.jpg -- 36.85kb -> 30.15kb (18.2%)
/collapsible-headers/collapsible-headers.gif -- 145.67kb -> 122.05kb (16.21%)
/truncated-titles/truncated-titles.jpg -- 12.41kb -> 10.88kb (12.34%)
/icon-sets/icon-sets.jpg -- 36.42kb -> 31.95kb (12.28%)
/emoji-sets/emoji-sets.jpg -- 79.34kb -> 70.98kb (10.53%)
/collapsible-properties/collapsible-properties.jpg -- 70.71kb -> 63.32kb (10.45%)
/global-block-links/global-block-links.jpg -- 9.01kb -> 8.09kb (10.21%)

Signed-off-by: ImgBotApp <ImgBotHelp@gmail.com>
2021-11-01 12:22:26 +00:00
dragonwocky
7e5c1b1575 [repo] extension + integration screenshots 2021-11-01 11:58:38 +00:00
f5f81547bf extension + integration screenshots 2021-11-01 22:58:09 +11:00
dragonwocky
c4c2ce7165 [repo] tweak welcome text and add modal to notif badge count 2021-10-31 09:46:31 +00:00
bdba993cf8 tweak welcome text and add modal to notif badge count 2021-10-31 20:46:07 +11:00
b8966c8459 reformat changelog under headers 2021-10-31 18:37:50 +11:00
dragonwocky
b6efd3301e [repo] added welcome/changelog/license acceptance modal 2021-10-31 07:36:34 +00:00
1ac78b64e8 added welcome/changelog/license acceptance modal 2021-10-31 18:36:01 +11:00
dragonwocky
521e9d1148 [api] removed welcome notification from registry 2021-10-31 07:25:30 +00:00
76df1e7676 removed welcome notification from registry 2021-10-31 18:25:01 +11:00
dragonwocky
782082ceba [repo] integration: quick note 2021-10-31 01:22:01 +00:00
9b1a1ea509 integration: quick note 2021-10-31 12:21:39 +11:00
dragonwocky
9876e195f9 [api] handle space and plus in hotkeys 2021-10-31 01:21:33 +00:00
21c56590a5 handle space and plus in hotkeys 2021-10-31 12:21:02 +11:00
Ryo Hilmawan
0a64c4eb5c
update cloudhill mods (#600) 2021-10-30 23:06:10 +11:00
a678716120 changelog: icon sets, collapse headers, simpler dbs, img alignment 2021-10-30 22:37:36 +11:00
dragonwocky
8566d0b5ed [repo] extension: icon sets part 2 (enhancer + custom sets, sprites & spinners) 2021-10-30 11:32:15 +00:00
805cbb836d extension: icon sets part 2 (enhancer + custom sets, sprites & spinners) 2021-10-30 22:31:49 +11:00
dragonwocky
d2127207f3 [repo] extension: icon sets part 1 (save/reuse recent uploads) 2021-10-30 08:14:48 +00:00
96988d67e0 extension: icon sets part 1 (save/reuse recent uploads) 2021-10-30 19:14:22 +11:00
dragonwocky
1c203f56d7 [api] notion api upload file, registry integration tag 2021-10-30 08:14:00 +00:00
a497618ea4 notion api upload file, registry integration tag 2021-10-30 19:13:27 +11:00
dragonwocky
c9f63e27ab [repo] menu: sidebar responds to changes in history, restores on reload 2021-10-29 01:22:50 +00:00
6a84fbec91 menu: sidebar responds to changes in history, restores on reload 2021-10-29 12:22:24 +11:00
dragonwocky
ae6f5b86dd [repo] tweak: image alignment (center/left/right) 2021-10-24 02:31:01 +00:00
4d8eec7399 tweak: image alignment (center/left/right) 2021-10-24 13:30:21 +11:00
dragonwocky
2329bc5520 [api] add keydown & in-input hotkey opts 2021-10-23 12:13:06 +00:00
10a58ca18d add keydown & in-input hotkey opts 2021-10-23 23:12:30 +11:00
dragonwocky
8dd4872f05 [repo] extension: simpler databases 2021-10-23 12:12:29 +00:00
047b4cce7d extension: simpler databases 2021-10-23 23:11:52 +11:00
dragonwocky
6996ab84d6 [repo] extension: collapsible headers 2021-10-21 12:28:28 +00:00
38484b7e42 extension: collapsible headers 2021-10-21 23:27:58 +11:00
e72115fd51 update panel, hotkey comment 2021-10-21 23:27:19 +11:00
790ddcbac2 Merge branch 'dev' of github.com:notion-enhancer/extension into dev 2021-10-20 23:05:05 +11:00
dragonwocky
fc418bc838 [repo] extension: global block links 2021-10-20 12:04:57 +00:00
1f47f1be34 update changelog, clipboard read perm 2021-10-20 23:04:55 +11:00
dbb94bc29b extension: global block links 2021-10-20 23:04:26 +11:00
dragonwocky
9517395f44 [api] clipboard helpers 2021-10-20 12:02:54 +00:00
7da5f65c85 clipboard helpers 2021-10-20 23:02:26 +11:00
dragonwocky
70b302a912 [api] smoother tooltips, delay arg 2021-10-20 10:03:48 +00:00
db5ec9a934 smoother tooltips, delay arg 2021-10-20 21:03:20 +11:00
dragonwocky
82564420e4 [repo] extension: truncated titles 2021-10-20 10:02:27 +00:00
b1ced4fac8 extension: truncated titles 2021-10-20 21:01:52 +11:00
b294964b26 extension: code line numbers 2021-10-20 15:42:22 +11:00
dragonwocky
1ecfd102ba [repo] extension: weekly view 2021-10-19 12:43:18 +00:00
6d40349389 extension: weekly view 2021-10-19 23:42:48 +11:00
41e1258469 update changelog 2021-10-19 21:29:41 +11:00
dragonwocky
921f89ec4b [api] adjust url regex 2021-10-19 10:28:27 +00:00
690b677dc9 adjust url regex 2021-10-19 21:28:01 +11:00
dragonwocky
1146e9eaa9 [repo] extension: right-to-left 2021-10-19 10:26:38 +00:00
8a1eb811a6 extension: right-to-left 2021-10-19 21:26:05 +11:00
dragonwocky
642f9accbb [repo] extension: outliner 2021-10-19 05:41:15 +00:00
382451156c extension: outliner 2021-10-19 16:40:50 +11:00
dragonwocky
8e97cf1030 [api] focus/blur listeners on side panel 2021-10-19 05:39:15 +00:00
fe42600ab8 focus/blur listeners on side panel 2021-10-19 16:38:48 +11:00
dragonwocky
04a1e4938c [repo] observe topbar icons for in previews 2021-10-18 12:54:10 +00:00
a9427f4520 observe topbar icons for in previews 2021-10-18 23:53:42 +11:00
dragonwocky
67cc36c35a [repo] extension: topbar icons 2021-10-18 12:28:02 +00:00
d25d4f2f13 extension: topbar icons 2021-10-18 23:27:27 +11:00
Alejandro Romano
3e7319464e
Fix persistent high CPU usage when emoji-sets is enabled (#586)
MutationObserver enters into an infinite loop by iterating on itself. By disconnecting the observer and reconnecting it after parsing the DOM for emoji-sets to work, we stop the problem and Notion goes back to sane 0% CPU idle usage.
2021-10-18 22:19:34 +11:00
dragonwocky
18ee0d9ea9 [repo] emoji-sets: fix native emoji alignment 2021-10-18 11:16:22 +00:00
b8fdad91c3 emoji-sets: fix native emoji alignment 2021-10-18 22:15:27 +11:00
Alejandro Romano
0198e29f46
Fix margins for every emoji set (#585)
Custom emojis, when displayed as icons in every page and in the left sidebar of Notion, are a bit misaligned. This is not the case for the emojis in the page content. Just specific places when they act like icons. They are a few pixels off but it triggers an OCD in me 🤣

This fixes it and it looks awesome. Precisely aligned as the default emoji set.
2021-10-18 22:05:35 +11:00
dragonwocky
e09443a048 [repo] extension: emoji sets 2021-10-18 09:35:15 +00:00
a824ba8856 extension: emoji sets 2021-10-18 20:34:48 +11:00
dragonwocky
2dfb3bb539 [api] observe child changes better 2021-10-18 09:34:16 +00:00
860a943437 observe child changes better 2021-10-18 20:33:45 +11:00
Hayk Aprikyan
63bf82507f
restore messenger emojis (#362)
* added messenger set

* added messenger value
2021-10-18 18:01:04 +11:00
110d2f3551 Merge branch 'dev' of github.com:notion-enhancer/extension into dev 2021-10-18 17:29:34 +11:00
7048abaedd update changelog, add clipboardwrite perm to manifest 2021-10-18 17:29:18 +11:00
dragonwocky
9bc748d67c [repo] extension: word counter (moved to panel) 2021-10-18 06:28:54 +00:00
1f9537b755 extension: word counter (moved to panel) 2021-10-18 17:28:28 +11:00
dragonwocky
00090853b9 [api] panel: work without right-sidebar pre-existing
+ smaller tweaks inc. icon pos, default uuid gen, clickthrough hover area to topbar
2021-10-18 06:28:24 +00:00
85dbb33fab panel: work without right-sidebar pre-existing
+ smaller tweaks inc. icon pos, default uuid gen, clickthrough hover area to topbar
2021-10-18 17:27:53 +11:00
dragonwocky
0bc838113e [repo] restore theme patches to perma-applied 2021-10-17 12:12:22 +00:00
d5c45a8771 restore theme patches to perma-applied 2021-10-17 23:11:58 +11:00
dragonwocky
9641a35a06 [repo] extension: focus mode
+ only inject theming css if a theme is enabled, to reduce lag
2021-10-17 11:58:12 +00:00
9b3cf23b08 extension: focus mode
+ only inject theming css if a theme is enabled, to reduce lag
2021-10-17 22:57:41 +11:00
dragonwocky
9ca4e51fab [repo] rainbow indentation lines + block dragger bg fix 2021-10-17 11:18:13 +00:00
69c3034a8b rainbow indentation lines + block dragger bg fix 2021-10-17 22:17:33 +11:00
dragonwocky
c77cf71163 [repo] extension: indentation lines
+ new feature: toc support
2021-10-15 12:21:58 +00:00
f9b95ef848 extension: indentation lines
+ new feature: toc support
2021-10-15 23:21:26 +11:00
Tom
54319a8bf2
Update README.md 2021-10-14 08:42:16 +11:00
Jordan Robinson
e990c51c00
Adds gruvbox-dark theme based on morhetz gruvbox vim theme (#382) 2021-10-13 14:21:44 +11:00
50f8683ec8 update changelog: font chooser, property layout, scroll to top 2021-10-13 14:08:04 +11:00
dragonwocky
fd0a635ae7 [api] component: corner action 2021-10-13 03:05:47 +00:00
a03168d980 component: corner action 2021-10-13 14:05:22 +11:00
dragonwocky
883f7915c7 [repo] extension: scroll to top 2021-10-13 03:05:14 +00:00
1fda0e9c86 extension: scroll to top 2021-10-13 14:04:40 +11:00
dragonwocky
6a5c6e2a73 [repo] extension: collapse properties
formerly property-layout, added per-page memory
2021-10-13 00:20:15 +00:00
1e5c985e76 extension: collapse properties
formerly property-layout, added per-page memory
2021-10-13 11:19:38 +11:00
dragonwocky
d99983105f [repo] incomplete theme colour mapping update
https://discord.com/channels/748161877045149706/748162220424167534/897602187067269130 more changes to come
2021-10-12 23:16:04 +00:00
3b4030d12b incomplete theme colour mapping update
https://discord.com/channels/748161877045149706/748162220424167534/897602187067269130 more changes to come
2021-10-13 10:07:06 +11:00
dragonwocky
84ae933d4a [api] allow empty strings as option values 2021-10-12 07:39:29 +00:00
cc1418ee5d allow empty strings as option values 2021-10-12 18:39:04 +11:00
dragonwocky
ecac94a8b3 [repo] extension: font chooser 2021-10-12 07:39:03 +00:00
ef05403caf extension: font chooser 2021-10-12 18:38:23 +11:00
9d0994e225 update changelog: tweaks + themes 2021-10-10 16:36:31 +11:00
dragonwocky
b9b8c5fa67 [repo] tweak: full width pages + table wrap bugfixes
+ neutral theme font size fix + menu focused mod b/w tab switching fix
2021-10-10 05:33:32 +00:00
7f266f207b tweak: full width pages + table wrap bugfixes
+ neutral theme font size fix + menu focused mod b/w tab switching fix
2021-10-10 16:33:06 +11:00
dragonwocky
f372f0d361 [repo] tweak: accented links 2021-10-10 04:19:30 +00:00
1af8878748 tweak: accented links 2021-10-10 15:19:03 +11:00
dragonwocky
f718410970 [repo] tweak: responsive columns breakpoint (%) 2021-10-10 03:21:46 +00:00
4fa312c19b tweak: responsive columns breakpoint (%) 2021-10-10 14:21:18 +11:00
dragonwocky
445714df01 [repo] upload theme previews 2021-10-10 02:03:54 +00:00
6aacd075fb upload theme previews 2021-10-10 13:03:21 +11:00
dragonwocky
3986fc5670 [repo] light+ configurable highlight color for text/lines etc. 2021-10-10 01:30:39 +00:00
81c0e13523 light+ configurable highlight color for text/lines etc. 2021-10-10 12:30:06 +11:00
dragonwocky
4ac45ea2d2 [api] adjust rgb contrast constant 2021-10-10 01:28:12 +00:00
5f52f520f4 adjust rgb contrast constant 2021-10-10 12:27:42 +11:00
dragonwocky
83d4518d27 [repo] load js in menu, configurable accents in dark+/light+ 2021-10-09 14:28:50 +00:00
02c085e832 load js in menu, configurable accents in dark+/light+ 2021-10-10 01:28:24 +11:00
dragonwocky
661f5ee341 [api] shade/contrast rgb helpers 2021-10-09 14:00:33 +00:00
824da94ffb shade/contrast rgb helpers 2021-10-10 00:59:55 +11:00
dragonwocky
1dbaf2967d [api] prevent double-reading of registry, allow menu/frame scripts 2021-10-09 13:01:29 +00:00
9b4b21c297 prevent double-reading of registry, allow menu/frame scripts 2021-10-10 00:00:59 +11:00
dragonwocky
de5e7e846e [repo] themes: light+, pinky boom, playful purple
+ quotation marks tweak extracted from prev. littlepig
2021-10-09 07:17:25 +00:00
bb42117024 themes: light+, pinky boom, playful purple
+ quotation marks tweak extracted from prev. littlepig
2021-10-09 18:16:56 +11:00
dragonwocky
2376d33e19 [repo] remove csp-blocked fonts from themes 2021-10-08 06:03:55 +00:00
809df67a14 remove csp-blocked fonts from themes 2021-10-08 17:03:12 +11:00
dragonwocky
9017edd1cd [repo] theme: gruvbox light (sepia) 2021-10-08 05:53:47 +00:00
60a7b57ccc theme: gruvbox light (sepia) 2021-10-08 16:53:16 +11:00
dragonwocky
9da41805f6 [api] include commit msg in submodule update 2021-10-08 04:46:12 +00:00
579050e349 include commit msg in submodule update 2021-10-08 15:45:48 +11:00
dragonwocky
a962b0044e [dep] include commit msg in submodule update 2021-10-08 04:45:30 +00:00
c43dec218b include commit msg in submodule update 2021-10-08 15:45:08 +11:00
dragonwocky
cd18658d55 [media] include commit msg in submodule update 2021-10-08 04:44:49 +00:00
a4d4e4e007 include commit msg in submodule update 2021-10-08 15:44:21 +11:00
dragonwocky
ad217c0b6b repo submodule: include commit msg in submodule update 2021-10-08 04:42:26 +00:00
e15370bede include commit msg in submodule update 2021-10-08 15:42:01 +11:00
dragonwocky
07a408f477 update submodule: api 2021-10-08 04:32:11 +00:00
e78d6c2965 rename bg_popup to bg_card for side panel 2021-10-08 15:31:42 +11:00
dragonwocky
4345ff172f update submodule: repo 2021-10-08 04:26:14 +00:00
c569d4190a theme: gruvbox dark 2021-10-08 15:25:47 +11:00
dragonwocky
d14b53acd5 update submodule: repo 2021-10-08 02:31:14 +00:00
daa8489175 theme: nord 2021-10-08 13:30:43 +11:00
a8ed96e09b remove id from path to menu 2021-10-08 12:31:18 +11:00
dragonwocky
c83003cfac update submodule: repo 2021-10-08 01:29:12 +00:00
c4e6b6b62a theme: neutral
remove ids from mod folder names
2021-10-08 12:28:44 +11:00
dragonwocky
c9dd292f68 update submodule: repo 2021-10-07 12:07:39 +00:00
1f88c043dc make author email optional 2021-10-07 23:07:09 +11:00
86721aabba theme: pastel dark 2021-10-07 23:06:11 +11:00
dragonwocky
574e5100cd update submodule: repo 2021-10-07 11:36:53 +00:00
7aa3f04f8b theme: dracula
improve tweaks table width normalisation
2021-10-07 22:36:20 +11:00
dragonwocky
dc3c78d134 update submodule: repo 2021-10-06 12:03:16 +00:00
7418e33d1e theme embed + template blocks 2021-10-06 23:02:42 +11:00
3a781bb94c apply on onboarding page 2021-10-06 21:57:28 +11:00
dragonwocky
9e0b089e07 update submodule: repo 2021-10-06 10:56:36 +00:00
17c51e7110 theme: dark+ 2021-10-06 21:56:07 +11:00
dragonwocky
d3a9e968ba update submodule: repo 2021-10-06 09:37:49 +00:00
6f4ca93d14 enhance onboarding pages 2021-10-06 20:37:09 +11:00
dragonwocky
f4bf94a0c8 update submodule: repo 2021-10-06 07:00:22 +00:00
2df027e888 theme: cherry cola 2021-10-06 17:58:21 +11:00
dragonwocky
801ce4ec91 update submodule: repo 2021-10-06 06:55:54 +00:00
dragonwocky
8d369bbef2 update submodule: repo 2021-10-06 06:06:49 +00:00
c894758f1a theme: material-ocean + related bugfixes 2021-10-06 17:06:10 +11:00
f7a888e590 more robust storage path handling 2021-10-05 00:09:39 +11:00
dragonwocky
7b2761460e update submodule: api 2021-10-04 13:05:11 +00:00
a5ecd02068 improved tooltip styling 2021-10-05 00:04:43 +11:00
dragonwocky
c8ed9d3769 update submodule: repo 2021-10-04 13:04:13 +00:00
b463b2b7e7 tweaks: update, add table wrapping & hide '/' for commands
+ other bugfixes
2021-10-05 00:03:40 +11:00
dragonwocky
a394c25d3d update submodule: repo 2021-10-04 11:45:18 +00:00
f2928371b6 update + port: bypass-preview, calendar-scroll 2021-10-04 22:44:51 +11:00
ea0992a9d8 more reliable launcher check 2021-10-04 22:10:41 +11:00
dragonwocky
e035f385ca update submodule: api 2021-10-04 07:40:24 +00:00
ae400099eb attempt #2: notion unofficial api wrapper
(capable of getting, searching, creating and editing all content)
2021-10-04 18:39:56 +11:00
dragonwocky
79039d0661 update submodule: api 2021-10-04 07:35:52 +00:00
fd2b729228 fix typos, rename notion.add to notion.create 2021-10-04 18:35:22 +11:00
dragonwocky
f49be7a01f update submodule: api 2021-10-04 07:32:38 +00:00
9bb779149b add unofficial notion api wrapper (capable of getting, searching, creating and editing all content) 2021-10-04 18:32:03 +11:00
dragonwocky
5f169ddf01 update submodule: repo 2021-10-03 12:55:41 +00:00
022c08fe75 remove font_heading and font_quote variables
interfered with page sans/serif/mono setting: https://github.com/notion-enhancer/notion-repackaged/issues/44
2021-10-03 23:55:14 +11:00
dragonwocky
270cf0e82c update submodule: api 2021-10-03 12:08:05 +00:00
a86b363ea2 workflow: add loop 2021-10-03 23:07:42 +11:00
dragonwocky
00595ce5bf update submodule: dep 2021-10-03 12:07:35 +00:00
7a5a954aa7 workflow: fix branch, add loop 2021-10-03 23:07:15 +11:00
dragonwocky
c52a849049 update submodule: repo 2021-10-03 12:06:59 +00:00
f9147991e8 workflow: add loop 2021-10-03 23:06:38 +11:00
dragonwocky
d98e1ccfe0 update submodule: media 2021-10-03 12:06:31 +00:00
74ea0e4bbf workflow: fix branch, add loop 2021-10-03 23:06:09 +11:00
dragonwocky
b9bf75d541 update submodule: media 2021-10-03 12:05:28 +00:00
dragonwocky
dd85d86052 update submodule: api 2021-10-03 12:00:14 +00:00
19fbe73949 workflow: update parent repo 2021-10-03 23:00:13 +11:00
539fcc6f2e workflow: update parent repo 2021-10-03 23:00:00 +11:00
cce4788aba workflow: update parent repo 2021-10-03 22:59:48 +11:00
dragonwocky
ecb702ae2c update submodule: repo 2021-10-03 11:43:21 +00:00
c36dc27637 use name env var in workflow 2021-10-03 22:42:56 +11:00
dragonwocky
6a820424ea update submodule: repo 2021-10-03 11:35:10 +00:00
2902e33d11 update parent workflow 2021-10-03 22:34:46 +11:00
e9e0c2b557 add update submodules workflow 2021-10-03 21:32:31 +11:00
Tom
7dcafa12f6 typo in readme 2021-10-03 21:04:21 +11:00
3f81a32351 move dep to submodule 2021-10-03 21:02:22 +11:00
039baf6ea7 upload latest 2021-10-03 21:01:43 +11:00
Tom
59489df994 Initial commit 2021-10-03 21:00:40 +11:00
c80ee2635f move icon to media submodule 2021-10-03 20:48:42 +11:00
44f8693490 move icon/ to media/ 2021-10-03 20:47:03 +11:00
a432a2d612 upload icons 2021-10-03 20:43:40 +11:00
Tom
3192050de8 Initial commit 2021-10-03 20:42:46 +11:00
bb8220c66c move repo to submodule 2021-10-03 19:48:26 +11:00
d4b344900f upload progress of ported mods from extension repo 2021-10-03 19:47:11 +11:00
8159fc2256 move api to submodule 2021-10-03 19:31:54 +11:00
cdf8537153 upload progress from extension repo 2021-10-03 19:29:18 +11:00
Tom
d1fc35c608 Initial commit 2021-10-03 19:23:00 +11:00
9dcbfc213f menu: store theme outside profile, reload env on profile change 2021-10-03 15:08:42 +11:00
42cfc69357 cleanup panel: fade long titles, remember last open, bugfixes 2021-10-03 13:50:19 +11:00
6656b639d7 side panel switcher 2021-10-03 03:13:10 +11:00
bc38f5d972 side panel component: working except switcher 2021-10-03 00:13:50 +10:00
33e8907d4f replace hook with pseudo-mod, env folder = simpler documentation 2021-10-02 12:31:07 +10:00
a7549cd9db api hooks - enabling core mods to extend the api
moved tooltips to one, preparing to set up sidebar
2021-10-02 00:12:30 +10:00
8d377e1ac2 split up mega-validation registry function 2021-10-01 20:54:56 +10:00
ac12b164da refactor api ready for submodule 2021-10-01 17:39:45 +10:00
abbfce9af1 profile json exports/imports 2021-10-01 16:06:23 +10:00
95621cd029 downgrade to manifest v2 for firefox support (tested/working) 2021-10-01 00:23:26 +10:00
ccbcdaf76a force one theme per mode, move notif cache outside of profile, cleanup test data 2021-09-30 23:46:24 +10:00
775d8412d0 added profile switcher + split up menu js 2021-09-30 23:09:22 +10:00
ea53f672ee options in menu sidebar + new hotkey input 2021-09-30 20:59:32 +10:00
8c1b5c0835 refactor twind to classes + support notion.site + bugfixes 2021-09-29 21:04:49 +10:00
ea30d01296 menu breakpoints + reload notif + profile db in registry api 2021-09-29 11:55:03 +10:00
7734e3977e searchable categorised mod lists + bypass-preview extension update 2021-09-28 23:22:49 +10:00
f7c597ebb3 menu routing/structure + nav 2021-09-28 12:59:56 +10:00
a2b93cb513 init menu with twind + notifications 2021-09-28 00:11:47 +10:00
b900c00641 bugfix storage, theming, validation, update launcher 2021-09-27 19:01:21 +10:00
5bb8b5f3fc improved registry validation + methods, prep for profiles 2021-09-27 16:32:27 +10:00
f383e6b401 split up + improve api part 1: all except registry, temp disabled launcher 2021-09-27 02:24:36 +10:00
f98dcb174a update dependencies + manifest + icons 2021-09-26 16:39:19 +10:00
6eff0289f4 bugfix relation input styling, restore light theme 2021-09-26 12:36:17 +10:00
bafc714b59 theming system theoretically light/dark consistent, full coverage (needs more testing) 2021-09-26 00:28:10 +10:00
d857209b19 theming system rework progress 2021-09-24 23:52:52 +10:00
Jaime Martínez Rincón
8393aa325b
Update README for notion-repackaged (#523) 2021-07-04 22:09:39 +10:00
Jaime Martínez Rincón
b248ffa3ba
Update README to reference notion-app-enhanced (#500)
* Update README to reference notion-app-enhanced

* temp remove incompatible notion-app from list

Co-authored-by: Tom <thedragonring.bod@gmail.com>
2021-05-30 00:43:53 +10:00
46879fe47a calendar-scroll + temp disable of broken theming 2021-05-16 22:58:17 +10:00
99c166a1ac add new colour picker option 2021-05-13 10:59:08 +10:00
6f3c1691c4 add fontawesome. all of it. its too much.
extra can be deleted when dev finished.
2021-05-11 23:34:17 +10:00
a55482d62d add menu filters + unspaghetti menu code 2021-05-11 23:29:12 +10:00
4e3f921ee3 bypass-preview 2021-05-05 23:35:26 +10:00
f6d8f222a6 clear file picker button 2021-05-05 21:25:47 +10:00
cb4aad5ba0 removed js insert, css insert moved to tweaks mod 2021-05-04 23:43:09 +10:00
28bb0663d4 jsdoc namespaces 2021-05-04 23:13:03 +10:00
2671410fe9 helpers -> api, jsdoc comment them 2021-05-03 23:44:15 +10:00
af7e659c0e merge bracketed-links with tweaks 2021-05-03 21:39:16 +10:00
86723e28eb changelogs, css insert, tweaks 2021-05-03 12:02:18 +10:00
ecfb3c1e50 error notifications + theme conflict resolution 2021-04-29 14:20:27 +10:00
69b73f6140 toggle hotkey option + tab reload on changes 2021-04-28 22:43:19 +10:00
62ba127793 add mod enabling/disabling 2021-04-27 22:06:45 +10:00
16ab3a4969 tooltips ✔ 2021-04-27 20:21:34 +10:00
1d9785f804 tooltips wip 2021-04-26 15:12:24 +10:00
15b34ef638 save options in menu 2021-04-25 23:29:01 +10:00
8d0ce2e777 notification receiving system complete 2021-04-24 10:50:46 +10:00
042ddfed13 mockup alerts, add header icons, copy plaintext code button 2021-04-23 12:33:57 +10:00
0bc27b9a57 prism.js theme created/matched to notion (documentation styling complete) 2021-04-23 01:00:06 +10:00
8454f86024 more reliable menu tab focusing + shortcut/icon -> notion 2021-04-21 23:52:35 +10:00
9a0ffdd9bc add ids to markdown headings 2021-04-21 21:24:31 +10:00
f3c384e25d refactoring + move storage to helpers for compatibility 2021-04-21 20:17:19 +10:00
d58d5b1850 syntax higlighting ✔ (needs proper theming) 2021-04-21 11:51:21 +10:00
93e764fb0a sync menu & notion themes, mod page routing, better component gen 2021-04-21 00:07:49 +10:00
d2292137da move svg icons into own files, add dep to manifest 2021-04-20 17:19:26 +10:00
a5c4d47a6d add mod pages to menu: options & docs (README.md rendered) 2021-04-20 00:16:46 +10:00
5fe31ac703 bugfix tab management for menu, transfer to mod folder, start menu mockup 2021-04-18 02:32:56 +10:00
05760882b5 transfer validation to at runtime for better module dev + add buttons to open the menu 2021-04-17 02:29:53 +10:00
68ae14d967 basic script loading system
// to investigate: will the module approach break things when porting back to desktop
2021-04-12 00:19:24 +10:00
f1dcc694de basic theming system + module validator tool 2021-04-11 23:54:28 +10:00
Tom
5b516c21c6 Initial commit 2021-04-11 23:45:05 +10:00
Torch
b417882eec
Add link to fix 2021-04-10 11:12:27 -04:00
David Bailey
df92f22b0b
Update links + install info for notion-linux (#412) 2021-02-07 09:25:46 +11:00
a362f804d9
new theming: missing styles + bugfixes 2020-12-18 22:44:38 +11:00
5aa36fef41
upload wip rework: installer + themer 2020-12-16 10:29:21 +11:00
95bf160ba9 remove core mods 2020-12-15 23:49:49 +11:00
35d9c915b1
rename folders prepping to move to new repos 2020-12-15 21:57:07 +11:00
311e435e2e rename folders prepping to move to new repos 2020-12-15 21:57:07 +11:00
CloudHill
21552ee041 topbar icons: more specific selectors
+ fixes compatibility with global links mod
2020-12-14 20:57:26 +07:00
CloudHill
b79a40777d topbar icons: more specific selectors
+ fixes compatibility with global links mod
2020-12-14 20:57:26 +07:00
CloudHill
c21fe3e2f1 simpler databases: fix initialization of store variables 2020-12-14 20:42:39 +07:00
CloudHill
e724fe7aef simpler databases: fix initialization of store variables 2020-12-14 20:42:39 +07:00
Emir
458089a953
pinky boom theme: make the title lowercase (#350) 2020-12-14 10:11:20 +11:00
Emir
1b773a580a pinky boom theme: make the title lowercase (#350) 2020-12-14 10:11:20 +11:00
Emir
7e87b6f5c7
global linking blocks: bug fix and new option (#347)
* global linking blocks: fix wrong appending

* global linking blocks: option: show the page link button

* global linking blocks: color changes, new icon, new button text
2020-12-14 04:58:52 +11:00
Emir
ae12bb81a7 global linking blocks: bug fix and new option (#347)
* global linking blocks: fix wrong appending

* global linking blocks: option: show the page link button

* global linking blocks: color changes, new icon, new button text
2020-12-14 04:58:52 +11:00
Ryo Hilmawan
652e67d6c7
new extension: simpler databases (#349) 2020-12-14 04:25:19 +11:00
Ryo Hilmawan
a6bea11d93 new extension: simpler databases (#349) 2020-12-14 04:25:19 +11:00
mugiwarafx
e12b3bac85
feat(theme): add pinky boom light theme (#348)
* feat(theme): add pinky boom theme

* fix: folder theme path

* fix: proper var syntax

* feat(theme): add pinky boom theme

* feat(theme): add pinky boom light theme

* add newline mod.js

* add newline variables.css
2020-12-14 04:18:13 +11:00
mugiwarafx
f97ab060ce feat(theme): add pinky boom light theme (#348)
* feat(theme): add pinky boom theme

* fix: folder theme path

* fix: proper var syntax

* feat(theme): add pinky boom theme

* feat(theme): add pinky boom light theme

* add newline mod.js

* add newline variables.css
2020-12-14 04:18:13 +11:00
CloudHill
2b18b2ef68 notion-icons: add search bar (notion-enhancer/icons#4)
+ overall optimizations
2020-12-13 23:52:44 +07:00
CloudHill
00014e402c notion-icons: add search bar (notion-enhancer/icons#4)
+ overall optimizations
2020-12-13 23:52:44 +07:00
Emir
2245f8ccdd
extension: global linking blocks (#345) 2020-12-13 09:03:38 +11:00
Emir
6caffa14ae extension: global linking blocks (#345) 2020-12-13 09:03:38 +11:00
CloudHill
c4b4d31aa2 code-line-numbers: horizontal scroll bugfix
added background so that numbers no longer appear over the code
2020-12-11 17:15:35 +07:00
CloudHill
1478d44828 code-line-numbers: horizontal scroll bugfix
added background so that numbers no longer appear over the code
2020-12-11 17:15:35 +07:00
CloudHill
3f48fa42ab outliner optimizations, making use of fragments 2020-12-11 16:59:15 +07:00
CloudHill
80d1694977 outliner optimizations, making use of fragments 2020-12-11 16:59:15 +07:00
Ryo Hilmawan
31c43fdded
update dracula theme variables (#341) 2020-12-10 23:00:04 +11:00
Ryo Hilmawan
74641c8d6c update dracula theme variables (#341) 2020-12-10 23:00:04 +11:00
CloudHill
4b7c26f5b3 fix scroll-db_toolbars tweak's selectors
stops unrelated elements from being selected
2020-12-10 18:15:25 +07:00
CloudHill
7b91dbcd85 fix scroll-db_toolbars tweak's selectors
stops unrelated elements from being selected
2020-12-10 18:15:25 +07:00
Ryo Hilmawan
943f58630e
theming bugfixes and additional variables (#309)
* theming bugfixes and additional variables

* add theming code variables for constant and regex
2020-12-10 15:57:35 +11:00
Ryo Hilmawan
4211bd6189 theming bugfixes and additional variables (#309)
* theming bugfixes and additional variables

* add theming code variables for constant and regex
2020-12-10 15:57:35 +11:00
Tom
a17de1e768
revert accidental merge of still-wip "tweak: hide empty sidebar arrow"
This reverts commit 5305c0b9b0.
2020-12-10 15:56:39 +11:00
Tom
81216b8a90 revert accidental merge of still-wip "tweak: hide empty sidebar arrow"
This reverts commit 5305c0b9b0.
2020-12-10 15:56:39 +11:00
Emir
5305c0b9b0
tweak: hide empty sidebar arrow (#319) 2020-12-10 15:55:04 +11:00
Emir
5f69306429 tweak: hide empty sidebar arrow (#319) 2020-12-10 15:55:04 +11:00
MANNEN
b48efb2f39
Add files via upload (#335) 2020-12-09 11:18:20 +11:00
MANNEN
64ba7c79a1 Add files via upload (#335) 2020-12-09 11:18:20 +11:00
CloudHill
2d43b79735 outliner: refactor to increase performance
reduced the number of times the outliner has to refresh
in response to issue #333
2020-12-09 01:34:59 +07:00
CloudHill
bb9a0e197d outliner: refactor to increase performance
reduced the number of times the outliner has to refresh
in response to issue #333
2020-12-09 01:34:59 +07:00
CloudHill
07d0836e97 side-panel: detect keyboard events to navigate the panel switcher
(tab, arrows, enter, space, escape)
2020-12-08 00:24:01 +07:00
CloudHill
b0430b4541 side-panel: detect keyboard events to navigate the panel switcher
(tab, arrows, enter, space, escape)
2020-12-08 00:24:01 +07:00
CloudHill
6a8006823f outliner fix inline page links not rendering right 2020-12-07 16:52:14 +07:00
CloudHill
134fd074c2 outliner fix inline page links not rendering right 2020-12-07 16:52:14 +07:00
CloudHill
67b396e511 outliner now follows the headers' inline styling 2020-12-07 16:26:40 +07:00
CloudHill
88bbff62fe outliner now follows the headers' inline styling 2020-12-07 16:26:40 +07:00
Emir
927ba69695
remove tag, change avatar source, make curly braces consistent (#327) 2020-12-06 22:43:33 +11:00
Emir
6fe02a710c remove tag, change avatar source, make curly braces consistent (#327) 2020-12-06 22:43:33 +11:00
Emir
ca605ec049
add font variables for headings, headings input for font chooser and fix quote variable (#322)
* add font variables for headings, headings input for font chooser and fix quote variable

* fix spaces for consistent code

* fix version number (semver)

* bring together all heading styles

* bring together all heading variables

* update font styling loop
2020-12-06 12:54:25 +11:00
Emir
e68f3c7b79 add font variables for headings, headings input for font chooser and fix quote variable (#322)
* add font variables for headings, headings input for font chooser and fix quote variable

* fix spaces for consistent code

* fix version number (semver)

* bring together all heading styles

* bring together all heading variables

* update font styling loop
2020-12-06 12:54:25 +11:00
Emir
4eb63f751f
fix the sizing of avatars in enhancer menu (#324) 2020-12-06 12:54:10 +11:00
Emir
24cb16c0e6 fix the sizing of avatars in enhancer menu (#324) 2020-12-06 12:54:10 +11:00
Ryo Hilmawan
1c3b8e5fa4
new extension: collapsible headers (#320) 2020-12-06 05:13:01 +11:00
Ryo Hilmawan
38dded687e new extension: collapsible headers (#320) 2020-12-06 05:13:01 +11:00
6f4534c9fc
link back to main notion-deb-builder 2020-12-05 13:07:26 +11:00
4dc14699e1
v0.10.2 2020-12-05 12:44:31 +11:00
caa2360a3d replace feature requests with feature proposals + prep changelog for v0.10.2 2020-12-05 12:37:02 +11:00
cb55a79971
replace feature requests with feature proposals + prep changelog for v0.10.2 2020-12-05 12:37:02 +11:00
Ryo Hilmawan
406e6fa5d9 new extension: panel-sites (#313) 2020-12-05 10:19:34 +11:00
Ryo Hilmawan
b76c04428c
new extension: panel-sites (#313) 2020-12-05 10:19:34 +11:00
Emir
0476cd316f option: hide 'page details' text for word counter (#315)
* option: hide 'page details' text for word counter

* fix version number (semver)

* set camel case to snake case
2020-12-05 09:07:47 +11:00
Emir
4998b34cbe
option: hide 'page details' text for word counter (#315)
* option: hide 'page details' text for word counter

* fix version number (semver)

* set camel case to snake case
2020-12-05 09:07:47 +11:00
Emir
74d95498ac add quote variable for themes and quote input for font chooser (#314)
* add quote variable for themes and quote input for font chooser

* fix version number (semver)
2020-12-05 09:06:40 +11:00
Emir
6501ad9b80
add quote variable for themes and quote input for font chooser (#314)
* add quote variable for themes and quote input for font chooser

* fix version number (semver)
2020-12-05 09:06:40 +11:00
CloudHill
23c477232f fix 'littlepig-dark' typo (was 'littilepig-dark') 2020-12-04 14:24:31 +07:00
CloudHill
22d4f216d4 fix 'littlepig-dark' typo (was 'littilepig-dark') 2020-12-04 14:24:31 +07:00
CloudHill
ba8a044e4f pass store() into panel mods' js file in the same way as other extensions 2020-12-04 14:19:20 +07:00
CloudHill
168f76e62e pass store() into panel mods' js file in the same way as other extensions 2020-12-04 14:19:20 +07:00
ba0fcf5115
#300 support unpacked app folders 2020-12-04 15:44:17 +11:00
be40723e1c refactor __notion back to a function for error handling, replace missing executables with backups in check 2020-12-04 15:02:28 +11:00
eadbec249b
refactor __notion back to a function for error handling, replace missing executables with backups in check 2020-12-04 15:02:28 +11:00
CloudHill
894f2578df fix outliner lines not scrolling with the container 2020-12-04 10:54:26 +07:00
CloudHill
e91a363b08 fix outliner lines not scrolling with the container 2020-12-04 10:54:26 +07:00
d0b0493b16
upgrade check.js to handle unpacked notion installations 2020-12-04 13:08:37 +11:00
CloudHill
7d61ca130b prevent the outliner's styles from being overriden 2020-12-03 22:17:51 +07:00
CloudHill
b04e3dc5f7 prevent the outliner's styles from being overriden 2020-12-03 22:17:51 +07:00
790dac681c
changelog since v0.10.1 2020-12-04 00:00:15 +11:00
70dd43bad4 #306 file explorer no longer opens when enhancer menu is opened 2020-12-03 23:27:45 +11:00
af2af96d46
#306 file explorer no longer opens when enhancer menu is opened 2020-12-03 23:27:45 +11:00
CloudHill
b598aa451e fix side panel mod being stuck outside the screen 2020-12-03 19:00:36 +07:00
CloudHill
c767449bbf fix side panel mod being stuck outside the screen 2020-12-03 19:00:36 +07:00
f85f3b19e5 fix tabs for electron/notion update 2020-12-03 21:43:45 +11:00
4807cfd339
fix tabs for electron/notion update 2020-12-03 21:43:45 +11:00
f77f5040a4 add potential duplicates workflow
+ declare window variable with let as per https://github.com/notion-enhancer/notion-enhancer/issues/298#issuecomment-737749264
2020-12-03 20:40:49 +11:00
7ef483a7f1
add potential duplicates workflow
+ declare window variable with let as per https://github.com/notion-enhancer/notion-enhancer/issues/298#issuecomment-737749264
2020-12-03 20:40:49 +11:00
CloudHill
c410b14c19 minor style changes to code-line-numbers, outliner, side-panel 2020-12-03 16:31:24 +07:00
CloudHill
26d3a702b5 minor style changes to code-line-numbers, outliner, side-panel 2020-12-03 16:31:24 +07:00
Kirakeru
23f823d6ef Fix White Window Bug #298 (#302) 2020-12-03 09:22:12 +11:00
Kirakeru
a96ab5f3b0
Fix White Window Bug #298 (#302) 2020-12-03 09:22:12 +11:00
CloudHill
5f096034c1 side-panel: bind toggling to Ctrl+Shift+Backslash 2020-12-02 22:33:20 +07:00
CloudHill
e8f8576f63 side-panel: bind toggling to Ctrl+Shift+Backslash 2020-12-02 22:33:20 +07:00
CloudHill
416a343809 side-panel: styling for reload button 2020-12-02 22:29:16 +07:00
CloudHill
8267d8e143 side-panel: styling for reload button 2020-12-02 22:29:16 +07:00
CloudHill
03139a5e7c outliner: add option to show indentation lines 2020-12-02 22:10:05 +07:00
CloudHill
3b08e51dcb outliner: add option to show indentation lines 2020-12-02 22:10:05 +07:00
Ryo Hilmawan
b5bdeb108b move indentation lines from tweaks to its own mod (#287)
* move indentation lines from tweaks to its own mod

* changed indentation lines author to runargs

* refactor indentation lines to use the map function
2020-12-02 23:10:35 +11:00
Ryo Hilmawan
b7e80cf25b
move indentation lines from tweaks to its own mod (#287)
* move indentation lines from tweaks to its own mod

* changed indentation lines author to runargs

* refactor indentation lines to use the map function
2020-12-02 23:10:35 +11:00
Emir
c352cc9438 extension: truncated table titles (#292) 2020-12-02 23:09:31 +11:00
Emir
039670e639
extension: truncated table titles (#292) 2020-12-02 23:09:31 +11:00
Fabio
0beebe8052
Update references to new tweaks repository (#289) 2020-12-02 23:05:42 +11:00
Ryo Hilmawan
c08f7cdb86 New extensions: side panel and outliner (#284)
* upload side panel core

* upload outliner extension

* add onLoad, onSwitch, and onResize functions to panel extensions

* Update panel.js

* Add onLock and onUnlock functions to panel extensions

* Reordered side panel content loading

* change panel switcher font color

* fix loading last opened panel

* use svg icon files instead of defining them inline

* outliner: add full height option
2020-12-02 23:05:33 +11:00
Ryo Hilmawan
4a9d6477b7
New extensions: side panel and outliner (#284)
* upload side panel core

* upload outliner extension

* add onLoad, onSwitch, and onResize functions to panel extensions

* Update panel.js

* Add onLock and onUnlock functions to panel extensions

* Reordered side panel content loading

* change panel switcher font color

* fix loading last opened panel

* use svg icon files instead of defining them inline

* outliner: add full height option
2020-12-02 23:05:33 +11:00
Ryo Hilmawan
44fe96a56f Modify the enhancer menu's toggle styles to follow notion's toggles (#283)
* notion-enhancer#261 modify toggle styles to look similiar to notion's toggle

* remove `dot` spans from toggles
2020-12-02 23:05:14 +11:00
Ryo Hilmawan
794cb17ead
Modify the enhancer menu's toggle styles to follow notion's toggles (#283)
* notion-enhancer#261 modify toggle styles to look similiar to notion's toggle

* remove `dot` spans from toggles
2020-12-02 23:05:14 +11:00
Ryo Hilmawan
353a538585 notion-icons: fix toggle icon shrinking 2020-11-24 18:39:37 +07:00
Ryo Hilmawan
b25f24f5fe
notion-icons: fix toggle icon shrinking 2020-11-24 18:39:37 +07:00
CloudHill
9deb75361a notion-icons: toggleable community icon packs 2020-11-24 13:53:06 +07:00
CloudHill
0d37708ad8 notion-icons: toggleable community icon packs 2020-11-24 13:53:06 +07:00
Ryo Hilmawan
c86e2f7548 remove console logging 2020-11-21 22:38:25 +07:00
Ryo Hilmawan
f86e87a1ab
remove console logging 2020-11-21 22:38:25 +07:00
Ryo Hilmawan
aa46310b3a fix description capitalization 2020-11-21 22:37:50 +07:00
Ryo Hilmawan
01e17041a8
fix description capitalization 2020-11-21 22:37:50 +07:00
Ryo Hilmawan
cd9283dcd0 Fix visual issues and modal detection (#279) 2020-11-21 17:18:21 +11:00
Ryo Hilmawan
8bcd6eb79a
Fix visual issues and modal detection (#279) 2020-11-21 17:18:21 +11:00
Ryo Hilmawan
2fbe7a3798 Update mod.js (#276) 2020-11-19 22:44:51 +11:00
Ryo Hilmawan
14e6712037
Update mod.js (#276) 2020-11-19 22:44:51 +11:00
Ryo Hilmawan
9b644b3f86 Fix code line wrapping + other improvements (#275) 2020-11-19 20:00:15 +11:00
Ryo Hilmawan
e2d088d312
Fix code line wrapping + other improvements (#275) 2020-11-19 20:00:15 +11:00
Ryo Hilmawan
3c5fd94c2e Notion-icons: better popup modal detection (#274)
* Update mod.js

* Fix modal detection on preview pages
2020-11-19 15:03:00 +11:00
Ryo Hilmawan
ca9788136c
Notion-icons: better popup modal detection (#274)
* Update mod.js

* Fix modal detection on preview pages
2020-11-19 15:03:00 +11:00
Eddie Zhang
7d239e6c0f
Add note about supported linux client versions (#273) 2020-11-19 14:31:37 +11:00
Ryo Hilmawan
57bdccf7dc Update mod.js (#270) 2020-11-19 07:09:17 +11:00
Ryo Hilmawan
0064ae35dc
Update mod.js (#270) 2020-11-19 07:09:17 +11:00
7949d6e451
v0.10.1 2020-11-18 23:15:11 +11:00
ecc74244c4 prep changelog for pre/bugfix release, replace links 2020-11-18 23:08:59 +11:00
dbd2044600
prep changelog for pre/bugfix release, replace links 2020-11-18 23:08:59 +11:00
Emir
66b97450d4 add auto-shrink system for tabs (#262)
* add auto-shrink system for tabs

* decrease tab limit and minimum width of non-current tabs
2020-11-16 11:32:32 +11:00
Emir
206c347838
add auto-shrink system for tabs (#262)
* add auto-shrink system for tabs

* decrease tab limit and minimum width of non-current tabs
2020-11-16 11:32:32 +11:00
Ryo Hilmawan
a53f5ad652 Patch Notion Icons Extension (#257)
* Remove console logging in mod.js

* Rename styles.css to app.css
2020-11-15 12:08:03 +11:00
Ryo Hilmawan
5c151f133b
Patch Notion Icons Extension (#257)
* Remove console logging in mod.js

* Rename styles.css to app.css
2020-11-15 12:08:03 +11:00
Ryo Hilmawan
4cd612f92b Extension: Notion Icons (#250)
* Upload mod.js, styles.css, and icons.json

* Move icon.json to notion-enhancer/icons

* Update mod.js to work with the new icons repo
2020-11-14 09:29:50 +11:00
Ryo Hilmawan
afd7879ec6
Extension: Notion Icons (#250)
* Upload mod.js, styles.css, and icons.json

* Move icon.json to notion-enhancer/icons

* Update mod.js to work with the new icons repo
2020-11-14 09:29:50 +11:00
1568d31b0c
clearer notion is running error message 2020-11-12 23:06:56 +11:00
06b56538cf #211 remove z-index override preventing format dropdowns in table view from opening 2020-11-12 00:58:48 +11:00
621aaa3978
#211 remove z-index override preventing format dropdowns in table view from opening 2020-11-12 00:58:48 +11:00
a94f4165d0 tabs focus properly 2020-11-12 00:20:55 +11:00
722d0fe00a
tabs focus properly 2020-11-12 00:20:55 +11:00
0b29387c53 #220 stricter keyboard shortcut listeners 2020-11-12 00:05:03 +11:00
c067d93a74
#220 stricter keyboard shortcut listeners 2020-11-12 00:05:03 +11:00
0acb214b19 #192 remove draggable-indicator dots from tabs ext. in menu 2020-11-11 23:39:42 +11:00
9597dcc4d5
#192 remove draggable-indicator dots from tabs ext. in menu 2020-11-11 23:39:42 +11:00
c43b4a14f1 #204 empty hotkeys won't trigger 2020-11-11 23:29:48 +11:00
bc46ac39b0
#204 empty hotkeys won't trigger 2020-11-11 23:29:48 +11:00
81f539f65e don't try loading the default page if it doesn't exist 2020-11-11 23:04:23 +11:00
c5c02bc7e7
don't try loading the default page if it doesn't exist 2020-11-11 23:04:23 +11:00
d919493094 less-broken loading of things 2020-11-11 22:55:35 +11:00
967f8a61f9
less-broken loading of things 2020-11-11 22:55:35 +11:00
53d7472c5e load tabs.css and variables.css into notion container 2020-11-11 22:29:02 +11:00
8567f2fa90
load tabs.css and variables.css into notion container 2020-11-11 22:29:02 +11:00
fb88dc5a90 load menu.css and variables.css into enhancer menu 2020-11-11 22:14:05 +11:00
61b6b6deca
load menu.css and variables.css into enhancer menu 2020-11-11 22:14:05 +11:00
2fc1e66cff split app.css and variables.css, load both into main window + add relationship lines to numbered lists
fonts should now be added to variables.css instead of mod.js
2020-11-11 21:48:54 +11:00
00e5b3f40e
split app.css and variables.css, load both into main window + add relationship lines to numbered lists
fonts should now be added to variables.css instead of mod.js
2020-11-11 21:48:54 +11:00
imgbot[bot]
e7ad0fb51e [ImgBot] Optimize images (#241)
*Total -- 245.40kb -> 231.45kb (5.68%)

/mods/core/icons/question.svg -- 0.91kb -> 0.83kb (8.61%)
/notion-enhancer v0.10.0 banner.jpg -- 242.90kb -> 229.08kb (5.69%)
/mods/topbar-icons/icons/updates_on.svg -- 0.16kb -> 0.15kb (4.32%)
/mods/topbar-icons/icons/favorite_on.svg -- 0.19kb -> 0.19kb (4.04%)
/mods/topbar-icons/icons/updates_off.svg -- 0.20kb -> 0.19kb (3.48%)
/mods/topbar-icons/icons/share.svg -- 0.46kb -> 0.44kb (2.78%)
/mods/topbar-icons/icons/favorite_off.svg -- 0.36kb -> 0.35kb (2.71%)
/mods/scroll-to-top/arrow.svg -- 0.23kb -> 0.23kb (0.43%)

Signed-off-by: ImgBotApp <ImgBotHelp@gmail.com>

Co-authored-by: ImgBotApp <ImgBotHelp@gmail.com>
2020-11-11 18:48:55 +11:00
imgbot[bot]
479e4a6e0a
[ImgBot] Optimize images (#241)
*Total -- 245.40kb -> 231.45kb (5.68%)

/mods/core/icons/question.svg -- 0.91kb -> 0.83kb (8.61%)
/notion-enhancer v0.10.0 banner.jpg -- 242.90kb -> 229.08kb (5.69%)
/mods/topbar-icons/icons/updates_on.svg -- 0.16kb -> 0.15kb (4.32%)
/mods/topbar-icons/icons/favorite_on.svg -- 0.19kb -> 0.19kb (4.04%)
/mods/topbar-icons/icons/updates_off.svg -- 0.20kb -> 0.19kb (3.48%)
/mods/topbar-icons/icons/share.svg -- 0.46kb -> 0.44kb (2.78%)
/mods/topbar-icons/icons/favorite_off.svg -- 0.36kb -> 0.35kb (2.71%)
/mods/scroll-to-top/arrow.svg -- 0.23kb -> 0.23kb (0.43%)

Signed-off-by: ImgBotApp <ImgBotHelp@gmail.com>

Co-authored-by: ImgBotApp <ImgBotHelp@gmail.com>
2020-11-11 18:48:55 +11:00
efb20ebd19 add pull requests to changelog, rename styles.css to app.css 2020-11-11 18:44:23 +11:00
decf8862de
add pull requests to changelog, rename styles.css to app.css 2020-11-11 18:44:23 +11:00
Ryo Hilmawan
2b41b0e82c Extension: Code Line Numbers (#231)
* Added extension folder with mod.js and styles.css

* Option to show numbering on single-lined blocks

Disabled by default

* Update styles.css

* Update mod.js

Changed class name from numbered to code-numbered

* Update styles.css

Changed class name from numbered to code-numbered
2020-11-11 12:06:05 +11:00
Ryo Hilmawan
f38d781133
Extension: Code Line Numbers (#231)
* Added extension folder with mod.js and styles.css

* Option to show numbering on single-lined blocks

Disabled by default

* Update styles.css

* Update mod.js

Changed class name from numbered to code-numbered

* Update styles.css

Changed class name from numbered to code-numbered
2020-11-11 12:06:05 +11:00
Ryo Hilmawan
3b997d3c73 Extension: Topbar Icons (#216)
* Add icons, mod.js, and styles.css files

* Added options to mod.js

Added the option to individually select which buttons (share, updates, favorite) to replace with icons

* Update styles.css to work with options
2020-11-06 14:01:38 +11:00
Ryo Hilmawan
06f4d7b519
Extension: Topbar Icons (#216)
* Add icons, mod.js, and styles.css files

* Added options to mod.js

Added the option to individually select which buttons (share, updates, favorite) to replace with icons

* Update styles.css to work with options
2020-11-06 14:01:38 +11:00
Ryo Hilmawan
285aae3227 Modified tweak: Indentation lines (#215)
* Update mod.js

Changed indentation lines to a select option

* Update styles.css

Styling for each line type

* Update mod.js

Re-added description

* Update mod.js

Added a description to the scroll database toolbars tweak
2020-11-06 14:01:19 +11:00
Ryo Hilmawan
29be14801c
Modified tweak: Indentation lines (#215)
* Update mod.js

Changed indentation lines to a select option

* Update styles.css

Styling for each line type

* Update mod.js

Re-added description

* Update mod.js

Added a description to the scroll database toolbars tweak
2020-11-06 14:01:19 +11:00
Alexa B
80ab7b7b92 bugfix: fix #187 and typo (#208)
* docs: Fixed link to dev build instructions

* docs: README installation clarification

* docs: README installation clarification

* update windows keystroke for task manager

* More README alterations

* Add bullet condensing & bullet lines tweaks

* Revert "Add bullet condensing & bullet lines tweaks"

This reverts commit 71bc6e2c30.

* interactive hover color fixes #187

* README revert

Co-authored-by: Tom <thedragonring.bod@gmail.com>
2020-11-05 20:47:31 +11:00
Alexa B
50fcc2604b
bugfix: fix #187 and typo (#208)
* docs: Fixed link to dev build instructions

* docs: README installation clarification

* docs: README installation clarification

* update windows keystroke for task manager

* More README alterations

* Add bullet condensing & bullet lines tweaks

* Revert "Add bullet condensing & bullet lines tweaks"

This reverts commit 71bc6e2c30.

* interactive hover color fixes #187

* README revert

Co-authored-by: Tom <thedragonring.bod@gmail.com>
2020-11-05 20:47:31 +11:00
Ryo Hilmawan
a306fcea23 Changed the scroll-to-top extension to use an SVG icon (#205)
* Create arrow.svg

* Update mod.js

* Update styles.css
2020-11-05 20:47:00 +11:00
Ryo Hilmawan
3557987988
Changed the scroll-to-top extension to use an SVG icon (#205)
* Create arrow.svg

* Update mod.js

* Update styles.css
2020-11-05 20:47:00 +11:00
Ryo Hilmawan
ce6c58ef76 Property Layout Modification (#201)
* Update mod.js

* Update styles.css
2020-11-03 22:51:47 +11:00
Ryo Hilmawan
f1dd72cf11
Property Layout Modification (#201)
* Update mod.js

* Update styles.css
2020-11-03 22:51:47 +11:00
Alexa B
b671451261
docs: README installation clarification (#189)
* docs: Fixed link to dev build instructions

* docs: README installation clarification

* docs: README installation clarification

* update windows keystroke for task manager

* More README alterations

* Add bullet condensing & bullet lines tweaks

Co-authored-by: Tom <thedragonring.bod@gmail.com>
2020-11-03 22:45:40 +11:00
Alexa B
faa4ed02ed tweaks: condensed bullets and indentation lines (#199)
* docs: Fixed link to dev build instructions

* docs: README installation clarification

* docs: README installation clarification

* update windows keystroke for task manager

* More README alterations

* Add bullet condensing & bullet lines tweaks

* Revert "Add bullet condensing & bullet lines tweaks"

This reverts commit 71bc6e2c30.

* Add bullet condensing & bullet lines tweaks

* Attempt to revert README back to normal lol

* Attempt 2 to revert README back to unmodified

Co-authored-by: Tom <thedragonring.bod@gmail.com>
2020-11-03 22:44:12 +11:00
Alexa B
74b3bd4b52
tweaks: condensed bullets and indentation lines (#199)
* docs: Fixed link to dev build instructions

* docs: README installation clarification

* docs: README installation clarification

* update windows keystroke for task manager

* More README alterations

* Add bullet condensing & bullet lines tweaks

* Revert "Add bullet condensing & bullet lines tweaks"

This reverts commit 71bc6e2c30.

* Add bullet condensing & bullet lines tweaks

* Attempt to revert README back to normal lol

* Attempt 2 to revert README back to unmodified

Co-authored-by: Tom <thedragonring.bod@gmail.com>
2020-11-03 22:44:12 +11:00
Ryo Hilmawan
58297e35bf New tweak: Scroll Database Toolbars (#198)
* Update mod.js

Added an option for scroll database toolbars

* Update styles.css

Added styling for scroll database toolbars
2020-11-03 22:39:54 +11:00
Ryo Hilmawan
590d4d9595
New tweak: Scroll Database Toolbars (#198)
* Update mod.js

Added an option for scroll database toolbars

* Update styles.css

Added styling for scroll database toolbars
2020-11-03 22:39:54 +11:00
1e1c536f7b disable fadein of selected block halo with snappy transitions 2020-11-02 17:10:00 +11:00
b02ed3e36a
disable fadein of selected block halo with snappy transitions 2020-11-02 17:10:00 +11:00
7bfb1bb9f9
add missing theme, release date & install cmd to changelog 2020-11-02 16:41:40 +11:00
ad4f1650f4
v0.10.0 2020-11-02 16:01:21 +11:00
4d5754779a tabs: fallback if text name is undefined 2020-11-02 15:58:10 +11:00
eb3aa26220
tabs: fallback if text name is undefined 2020-11-02 15:58:10 +11:00
9553a49bd0 add new theme screenshots + remove bgs of existing 2020-11-02 12:18:48 +11:00
2a20d67be2
add new theme screenshots + remove bgs of existing 2020-11-02 12:18:48 +11:00
c4a890ea91 README feature list updates: new mods + options 2020-11-01 22:53:11 +11:00
27525428fc
README feature list updates: new mods + options 2020-11-01 22:53:11 +11:00
b985217611 #113 only open a new window from the dock if already visible 2020-11-01 21:04:26 +11:00
64c1e8bc5c
#113 only open a new window from the dock if already visible 2020-11-01 21:04:26 +11:00
f4d269bcdc #113 reshow hidden windows when clicking app icon in dock 2020-11-01 15:00:22 +11:00
ba8163a456
#113 reshow hidden windows when clicking app icon in dock 2020-11-01 15:00:22 +11:00
e24608f734 better selectors for detecting favicons in databases 2020-11-01 14:39:06 +11:00
6b161eb403
better selectors for detecting favicons in databases 2020-11-01 14:39:06 +11:00
17ceb88ed7 tab favicon fixes: support uploaded images, use emoji for window title, work on macos 2020-11-01 14:21:00 +11:00
650ac550fa
tab favicon fixes: support uploaded images, use emoji for window title, work on macos 2020-11-01 14:21:00 +11:00
f6ec94b46e show 'untitled' for new pages instead of empty space 2020-11-01 12:30:10 +11:00
8e2087d2fd
show 'untitled' for new pages instead of empty space 2020-11-01 12:30:10 +11:00
293e5ce228 fix #183 make weekly calendar view name case insensitive 2020-11-01 12:16:31 +11:00
5ac7e521b6
fix #183 make weekly calendar view name case insensitive 2020-11-01 12:16:31 +11:00
dccef7815f fix menu hotkey + tab title emojis on macOS 2020-11-01 12:04:04 +11:00
667c727ac9
fix menu hotkey + tab title emojis on macOS 2020-11-01 12:04:04 +11:00
eeea1c6b53 make ctrl+f popover shadow less extreme 2020-11-01 00:03:18 +11:00
9e1417ac3e
make ctrl+f popover shadow less extreme 2020-11-01 00:03:18 +11:00
6a326a12be #177 made extra padding at the bottom in focus mode toggleable 2020-10-28 17:50:55 +11:00
eeb83a14e0
#177 made extra padding at the bottom in focus mode toggleable 2020-10-28 17:50:55 +11:00
dc60b3d9ae platform-specific option overrides for features not required on macOS 2020-10-28 12:19:39 +11:00
29ef9a57b8
platform-specific option overrides for features not required on macOS 2020-10-28 12:19:39 +11:00
bf800a0889 tabs: #84 include page icons in title 2020-10-27 20:43:32 +11:00
64c88f5c42
tabs: #84 include page icons in title 2020-10-27 20:43:32 +11:00
9d3ab72eb7 tweaks: #155 more readable line spacing 2020-10-27 19:57:20 +11:00
1f802e5146
tweaks: #155 more readable line spacing 2020-10-27 19:57:20 +11:00
64d9394a27
refactor loader 2020-10-27 10:07:11 +11:00
5d439bbdc8 common tweaks ext: #168, #176 - scrollbars, thicker bold, responsiveness, hide help, snappy transitions, dragarea height 2020-10-26 23:38:46 +11:00
a21bcc9803
common tweaks ext: #168, #176 - scrollbars, thicker bold, responsiveness, hide help, snappy transitions, dragarea height 2020-10-26 23:38:46 +11:00
cbe505c374 macos bugfixes: #151 dark mode detection in night shift + adding menubar to extensions menu + padding-left with tabs to give traffic lights space 2020-10-24 22:25:05 +11:00
f66c78a381
macos bugfixes: #151 dark mode detection in night shift + adding menubar to extensions menu + padding-left with tabs to give traffic lights space 2020-10-24 22:25:05 +11:00
e79685a6e8 make dragarea transparent in new themes 2020-10-24 13:14:41 +11:00
35bbb12524
make dragarea transparent in new themes 2020-10-24 13:14:41 +11:00
230bc5e248 #146 dracula theme readability/contrast improvements 2020-10-24 11:05:04 +11:00
3acbaa1f12
#146 dracula theme readability/contrast improvements 2020-10-24 11:05:04 +11:00
96e13a03f0 #175 neutral theme: contrast in calendar views 2020-10-23 19:41:44 +11:00
b1a42f2bf5
#175 neutral theme: contrast in calendar views 2020-10-23 19:41:44 +11:00
d307a2bedf additional menu option descriptions on hover 2020-10-23 16:49:58 +11:00
c76750c088
additional menu option descriptions on hover 2020-10-23 16:49:58 +11:00
84e895dccc
-n cli option, safer handling of application/removal file manipulation 2020-10-23 15:22:13 +11:00
f3eda9a34b auto-resolve conflicts core option 2020-10-22 12:17:53 +11:00
c2a0b8e857
auto-resolve conflicts core option 2020-10-22 12:17:53 +11:00
c0f847949f clarity improvements for scroll to top extension 2020-10-22 08:54:06 +11:00
7885b2c204
clarity improvements for scroll to top extension 2020-10-22 08:54:06 +11:00
Ryo Hilmawan
d74eae1ebf new mod: scroll-to-top (#172)
* Basic Functionality

* Added smooth scrolling option

* Added scroll down distance option

* Button hide/show animation

* Add percentage distance option

* Calculate percentage every update (as opposed to every page change)

* Calculate percentage height based on scroller and content height

* Licensing and Conventions

* Hide button on initialization if top distance is set

* Fixed bug with button visibility when opening pages
2020-10-21 22:32:10 +11:00
Ryo Hilmawan
fb63601ebf
new mod: scroll-to-top (#172)
* Basic Functionality

* Added smooth scrolling option

* Added scroll down distance option

* Button hide/show animation

* Add percentage distance option

* Calculate percentage every update (as opposed to every page change)

* Calculate percentage height based on scroller and content height

* Licensing and Conventions

* Hide button on initialization if top distance is set

* Fixed bug with button visibility when opening pages
2020-10-21 22:32:10 +11:00
394a891115 theme conflicts notice in menu 2020-10-21 22:30:58 +11:00
cdbce12b11
theme conflicts notice in menu 2020-10-21 22:30:58 +11:00
d94629d9ed update themes to new variables 2020-10-20 11:59:22 +11:00
275c5ed4cd
update themes to new variables 2020-10-20 11:59:22 +11:00
ca30e70062 make block text colour apply to text with backgrounds 2020-10-20 09:55:38 +11:00
8f35a99fdd
make block text colour apply to text with backgrounds 2020-10-20 09:55:38 +11:00
78667d16d6 fix theming primary-colour text + right-to-left text in columns 2020-10-19 22:50:25 +11:00
84ef0d9440
fix theming primary-colour text + right-to-left text in columns 2020-10-19 22:50:25 +11:00
b1714dce54
Merge branch 'dev' of github.com:dragonwocky/notion-enhancer into dev 2020-10-19 22:04:14 +11:00
1618b5d966 use event communication instead of variable checking for cross-window dragging = less-buggy 2020-10-19 22:03:54 +11:00
b2491a0a09
use event communication instead of variable checking for cross-window dragging = less-buggy 2020-10-19 22:03:54 +11:00
a37fcb300e cross-window draggable tabs 2020-10-19 09:33:08 +11:00
1d1503c826
cross-window draggable tabs 2020-10-19 09:33:08 +11:00
4519409050 cherry cola adjustments + tab button hover fixes + closing tabs shouldn't create weird broken things anymore 2020-10-18 22:35:56 +11:00
cf4d8e63ee
cherry cola adjustments + tab button hover fixes + closing tabs shouldn't create weird broken things anymore 2020-10-18 22:35:56 +11:00
Alexa B
1e9adc51df
docs: Fixed link to dev build instructions (#167) 2020-10-18 11:55:17 +11:00
Alexa B
0ac87603db theme: cherry cola updates (#165)
- added some contrast and new variables
- border colour brighter
2020-10-17 10:03:41 +11:00
Alexa B
f25e9f7f98
theme: cherry cola updates (#165)
- added some contrast and new variables
- border colour brighter
2020-10-17 10:03:41 +11:00
c40e678974 ctrl+click open in new tab 2020-10-16 22:15:14 +11:00
91ab4a1547
ctrl+click open in new tab 2020-10-16 22:15:14 +11:00
d6a95a77df
patch application and fix potential .asar.asar changes (#163) 2020-10-16 17:29:33 +11:00
088c4f746b middle click close tabs + better tab animations 2020-10-16 17:26:18 +11:00
a264cbf832
middle click close tabs + better tab animations 2020-10-16 17:26:18 +11:00
b4b54fdca3
alternate for #163 (otherwise it would break notion-deb-builder) 2020-10-16 10:28:25 +11:00
ad645227d5 #164 fix width-stretching of collections in breadcrumbs 2020-10-16 10:14:58 +11:00
995587afa0
#164 fix width-stretching of collections in breadcrumbs 2020-10-16 10:14:58 +11:00
02fbd0e2bc better tab picking on close: find closest/next instead of jumping to first 2020-10-15 23:59:18 +11:00
ed5ededcbe
better tab picking on close: find closest/next instead of jumping to first 2020-10-15 23:59:18 +11:00
63a88e1d66 fix tab reordering and animations 2020-10-15 23:48:54 +11:00
b10ec8f136
fix tab reordering and animations 2020-10-15 23:48:54 +11:00
e3391f7017 fix tab opening/closing/titles 2020-10-15 22:34:51 +11:00
796fac9466
fix tab opening/closing/titles 2020-10-15 22:34:51 +11:00
82e5db751f + left/right tab switching, + remove cmd/ctrl+w from menu, - break tab opening/closing/titles 2020-10-15 22:01:28 +11:00
b23b928289
+ left/right tab switching, + remove cmd/ctrl+w from menu, - break tab opening/closing/titles 2020-10-15 22:01:28 +11:00
Alexa B
db3f89e3c4 theme: cherry cola (#161) 2020-10-15 17:57:16 +11:00
Alexa B
dcbe96f447
theme: cherry cola (#161) 2020-10-15 17:57:16 +11:00
d917c3aefd #161 removed typo in variable name for brown text. 2020-10-15 12:37:51 +11:00
0910e56d53
#161 removed typo in variable name for brown text. 2020-10-15 12:37:51 +11:00
4b464ca87c #160 patch opening by notion://url on linux + tab bugfixes 2020-10-15 12:19:21 +11:00
e826b9ff73
#160 patch opening by notion://url on linux + tab bugfixes 2020-10-15 12:19:21 +11:00
6a9f9bf79a add tab keybinds: alt+1...9 tab select, ctrl+t new tab, ctrl+w close tab
need to remove ctrl+w from the window menu so it doesn't override and close the whole window
2020-10-13 23:07:45 +11:00
21b8c3ca15
add tab keybinds: alt+1...9 tab select, ctrl+t new tab, ctrl+w close tab
need to remove ctrl+w from the window menu so it doesn't override and close the whole window
2020-10-13 23:07:45 +11:00
Alejandro Romano
a26914fdbe Tweaking readability in text-blocks by adding new variables (#156)
add line-height and margin-top variables for text-block (see #155)
2020-10-13 07:04:43 +11:00
Alejandro Romano
a580315593
Tweaking readability in text-blocks by adding new variables (#156)
add line-height and margin-top variables for text-block (see #155)
2020-10-13 07:04:43 +11:00
dee0a31283 accept url or id for default page, don't override direct notion:// opens 2020-10-12 22:06:03 +11:00
01bc88a783
accept url or id for default page, don't override direct notion:// opens 2020-10-12 22:06:03 +11:00
ee88c63f38 split tabs into separate mod, add opt for default page, load default settings when store()-ing another mod id 2020-10-12 21:44:12 +11:00
9b49e6034b
split tabs into separate mod, add opt for default page, load default settings when store()-ing another mod id 2020-10-12 21:44:12 +11:00
22b4153703 Revert "squashed last 5 commits together (#154)"
This reverts commit 9da9c2aee4.
2020-10-12 19:36:51 +11:00
d82dda1ff8
Revert "squashed last 5 commits together (#154)"
This reverts commit 9da9c2aee4.
2020-10-12 19:36:51 +11:00
cb4407279e un-break titlebar+menu (not sure how it was broken?) 2020-10-12 19:36:34 +11:00
d2cd8baa77
un-break titlebar+menu (not sure how it was broken?) 2020-10-12 19:36:34 +11:00
Mimi Shahzad
01fe0d6782 squashed last 5 commits together (#154) 2020-10-12 19:30:58 +11:00
Mimi Shahzad
9da9c2aee4
squashed last 5 commits together (#154) 2020-10-12 19:30:58 +11:00
Alejandro Romano
d0f273381b Add font sizing to page previews (#153)
Font sizes remained the same as default for page previews. They are not affected by custom font sizes. This change takes the same variables the full open page and apply it. Tested and working 10/10

Full open pages: .notion-frame
Overlay, preview pages: .notion-overlay-container (and .notion-scroller.vertical WITHOUT .horizontal)
2020-10-12 06:34:44 +11:00
Alejandro Romano
add5bb8d9c
Add font sizing to page previews (#153)
Font sizes remained the same as default for page previews. They are not affected by custom font sizes. This change takes the same variables the full open page and apply it. Tested and working 10/10

Full open pages: .notion-frame
Overlay, preview pages: .notion-overlay-container (and .notion-scroller.vertical WITHOUT .horizontal)
2020-10-12 06:34:44 +11:00
b69fa97572 change window title to match page name instead of just 'Notion Desktop' 2020-10-11 23:38:03 +11:00
45bc46aae4
change window title to match page name instead of just 'Notion Desktop' 2020-10-11 23:38:03 +11:00
1f38797cc4 enhancer icon next to tabs opens menu + less aggressive tab focusing so searching can actually work 2020-10-11 23:31:09 +11:00
3493bfac24
enhancer icon next to tabs opens menu + less aggressive tab focusing so searching can actually work 2020-10-11 23:31:09 +11:00
4886bbbd3e use keyup listeners instead of a globalShortcut for the enhancements menu toggle 2020-10-11 16:02:45 +11:00
d75368b020
use keyup listeners instead of a globalShortcut for the enhancements menu toggle 2020-10-11 16:02:45 +11:00
3d64a5e8c8 bugfixes: match up some styling better, EACCES dest chmodding, csp re-allow fonts 2020-10-11 15:28:56 +11:00
61206db05a
bugfixes: match up some styling better, EACCES dest chmodding, csp re-allow fonts 2020-10-11 15:28:56 +11:00
bb18f2a1e6 bugfixes so dracula will actually load 2020-10-09 20:56:21 +11:00
b8b4af4f89
bugfixes so dracula will actually load 2020-10-09 20:56:21 +11:00
09d13f3ed3 Merge branch 'dev' of github.com:dragonwocky/notion-enhancer into dev 2020-10-09 20:46:23 +11:00
40b1dc0877
Merge branch 'dev' of github.com:dragonwocky/notion-enhancer into dev 2020-10-09 20:46:23 +11:00
0472460161 draggable/nicer-looking tabs + icon 2020-10-09 20:42:34 +11:00
09069e03e9
draggable/nicer-looking tabs + icon 2020-10-09 20:42:34 +11:00
Mimi Shahzad
0448e8643e Dracula theme (#145)
A theme created based on the official Dracula Theme palette found https://draculatheme.com/contribute/.
2020-10-09 09:03:41 +11:00
Mimi Shahzad
3c71e945a3
Dracula theme (#145)
A theme created based on the official Dracula Theme palette found https://draculatheme.com/contribute/.
2020-10-09 09:03:41 +11:00
f0232fea9e functional tabs: needs more ux work 2020-10-08 22:50:27 +11:00
5ae9aed260
functional tabs: needs more ux work 2020-10-08 22:50:27 +11:00
51f7f564f6 #143 remove focus mode footer from neutral theme 2020-10-08 11:05:25 +11:00
ec0d5517bc
#143 remove focus mode footer from neutral theme 2020-10-08 11:05:25 +11:00
51acd63748 menu will now respect integrated titlebar setting 2020-10-08 09:58:35 +11:00
39ecd12943
menu will now respect integrated titlebar setting 2020-10-08 09:58:35 +11:00
c0d7d5170b replace dragarea with titlebar in tabbable mode 2020-10-07 23:58:22 +11:00
6e43097a3f
replace dragarea with titlebar in tabbable mode 2020-10-07 23:58:22 +11:00
50b2ff180d separate search working for tabs 2020-10-06 22:12:25 +11:00
157d440254
separate search working for tabs 2020-10-06 22:12:25 +11:00
9ef6317529 attempt 3 at #66 2020-10-06 11:47:33 +11:00
5bec4e31b6
attempt 3 at #66 2020-10-06 11:47:33 +11:00
7a6411ad23 catch EBUSY, potential csp fix for #66 2020-10-06 10:15:30 +11:00
85636647bd
catch EBUSY, potential csp fix for #66 2020-10-06 10:15:30 +11:00
ecdae521b4 tabs proof-of-concept (unusable) 2020-10-02 23:06:44 +10:00
4f053dc23f
tabs proof-of-concept (unusable) 2020-10-02 23:06:44 +10:00
45c2ebf340 #134 bypass preview operates more reliably when keyboard shortcuts are used 2020-10-02 15:46:11 +10:00
bc646ab8ae
#134 bypass preview operates more reliably when keyboard shortcuts are used 2020-10-02 15:46:11 +10:00
dbf1240034 improved responsiveness + snappier transitions mode 2020-10-02 13:44:16 +10:00
e82fb38ce8
improved responsiveness + snappier transitions mode 2020-10-02 13:44:16 +10:00
9429426ded
#124 tweak: sticky table/list rows. 2020-10-02 11:22:07 +10:00
a2f23a850e bugfix: toc blocks can have text colours 2020-10-02 10:43:47 +10:00
4cc002c7c2
bugfix: toc blocks can have text colours 2020-10-02 10:43:47 +10:00
f0b62d8df3 re-orderable mods: order is saved + mods are loaded according to priority 2020-10-01 23:11:31 +10:00
80fca9d187
re-orderable mods: order is saved + mods are loaded according to priority 2020-10-01 23:11:31 +10:00
Tom
e55159e3fa Merge pull request #130 from Blacksuan19/dev
mods: add material ocean theme
2020-10-01 18:40:30 +10:00
Tom
c8d34ea042
Merge pull request #130 from Blacksuan19/dev
mods: add material ocean theme
2020-10-01 18:40:30 +10:00
398e727ddb re-orderable mods: menu elements can be dragged 2020-10-01 13:32:50 +10:00
74a03a176c
re-orderable mods: menu elements can be dragged 2020-10-01 13:32:50 +10:00
blacksuan19
9d342fa99e mods: add material ocean theme
Signed-off-by: blacksuan19 <i@blacksuan19.me>
2020-10-01 03:10:43 +08:00
blacksuan19
4c480c5b5d
mods: add material ocean theme
Signed-off-by: blacksuan19 <i@blacksuan19.me>
2020-10-01 03:10:43 +08:00
cacb6554c7 night shift checks every interaction to respond to system changes 2020-09-30 14:43:23 +10:00
a3e8758280
night shift checks every interaction to respond to system changes 2020-09-30 14:43:23 +10:00
904cae5375 #126 board view shadow, remove messenger emoji set, bugfix EACCES response 2020-09-30 14:25:59 +10:00
8f1612e43c
#126 board view shadow, remove messenger emoji set, bugfix EACCES response 2020-09-30 14:25:59 +10:00
b16281f28e
v0.9.1 2020-09-26 17:02:51 +10:00
1b0d6d5cc3
prep for release 2020-09-26 16:55:37 +10:00
40336e78c1 #119 emoji sets achieve without user agent 2020-09-26 16:48:36 +10:00
481500c563
#119 emoji sets achieve without user agent 2020-09-26 16:48:36 +10:00
03c12b137d #101 weekly calendar view even if not first on page 2020-09-25 17:56:39 +10:00
677fd3cf10
#101 weekly calendar view even if not first on page 2020-09-25 17:56:39 +10:00
287e3551b5 #114 back/forward arrows work with bypass preview 2020-09-25 17:44:55 +10:00
21b3802e08
#114 back/forward arrows work with bypass preview 2020-09-25 17:44:55 +10:00
5655043b36 #116 fix indents and #107 unenforce full width in neutral 2020-09-25 16:23:10 +10:00
a31a93e206
#116 fix indents and #107 unenforce full width in neutral 2020-09-25 16:23:10 +10:00
Tom
9638a5765d
Merge pull request #108 from CloudHill/master
Font chooser bug fix
2020-09-20 18:52:34 +10:00
CloudHill
b09d89645c Font chooser bug fix
Fixed a small bug where it stops iterating through the font options if it encounters a blank option.
2020-09-20 14:53:09 +07:00
CloudHill
1b56426c94
Font chooser bug fix
Fixed a small bug where it stops iterating through the font options if it encounters a blank option.
2020-09-20 14:53:09 +07:00
f021bec086
v0.9.0 2020-09-20 15:11:25 +10:00
db1b0790e4
add release date to changelog 2020-09-20 15:10:37 +10:00
f39e497f4c #106: make use of user-agent for emoji sets 2020-09-20 14:41:26 +10:00
840f994081
#106: make use of user-agent for emoji sets 2020-09-20 14:41:26 +10:00
0c18f19dac extension screenshots 2020-09-20 12:43:27 +10:00
00451b1bde
extension screenshots 2020-09-20 12:43:27 +10:00
d3b444827a new theme screenshots + bugfixes 2020-09-19 23:05:43 +10:00
1011875530
new theme screenshots + bugfixes 2020-09-19 23:05:43 +10:00
cc4e2fb807 word counter extension (#88) + bugfixes 2020-09-18 14:21:09 +10:00
fe2a0115c3
word counter extension (#88) + bugfixes 2020-09-18 14:21:09 +10:00
bb3439429f properly align rtl bullet points/checkboxes #55
though more performant, this also limits rtl to page content
2020-09-17 14:37:04 +10:00
1a66d769cc
properly align rtl bullet points/checkboxes #55
though more performant, this also limits rtl to page content
2020-09-17 14:37:04 +10:00
525ff40bb3 page width variables - #81 2020-09-17 13:44:16 +10:00
dfcac064e8
page width variables - #81 2020-09-17 13:44:16 +10:00
6299ce4b81 #97, #98, and small alterations.
(removed platform support requests, replaced getNotion() with __notion)
2020-09-17 13:06:49 +10:00
2303783319
#97, #98, and small alterations.
(removed platform support requests, replaced getNotion() with __notion)
2020-09-17 13:06:49 +10:00
76ff3bd101 bypass preview extension - #81 2020-09-08 22:44:51 +10:00
211b24d2d7
bypass preview extension - #81 2020-09-08 22:44:51 +10:00
9147eb06e4 callout theming + more text colour variables 2020-09-08 22:24:02 +10:00
6dbef0e292
callout theming + more text colour variables 2020-09-08 22:24:02 +10:00
Tom
8dd5c7b479 Merge pull request #89 from coryzibell/master
Add module to hide help button
2020-09-07 08:23:32 +10:00
Tom
53a1638549
Merge pull request #89 from coryzibell/master
Add module to hide help button
2020-09-07 08:23:32 +10:00
Cory Zibell
b48cecf54d Add hide-help module content 2020-09-06 18:17:41 -04:00
Cory Zibell
f304f78177 Add hide-help module content 2020-09-06 18:17:41 -04:00
Cory Zibell
d66ff14e2c Add module to hide help button 2020-09-06 18:11:23 -04:00
Cory Zibell
a93ca8270c Add module to hide help button 2020-09-06 18:11:23 -04:00
72551c5f67 fix #73 - block-level text colours are now changed properly, add tick/cross to search filters 2020-09-03 12:12:58 +10:00
9998857aa1
fix #73 - block-level text colours are now changed properly, add tick/cross to search filters 2020-09-03 12:12:58 +10:00
988b78085a calendar scroll extension - #74 2020-09-02 22:56:40 +10:00
f9e53ad0b3
calendar scroll extension - #74 2020-09-02 22:56:40 +10:00
666925c081 fix property-layout again 2020-09-02 22:33:47 +10:00
54531f0a6d
fix property-layout again 2020-09-02 22:33:47 +10:00
c5f5ce30e5 ext. menu search upgrades (more stuff is searchable + case insensitive + cmd/ctrl f) 2020-09-02 22:21:17 +10:00
67f9ffaddb
ext. menu search upgrades (more stuff is searchable + case insensitive + cmd/ctrl f) 2020-09-02 22:21:17 +10:00
63e4b574a9 separate always-on-top into separate mod 2020-09-02 21:46:17 +10:00
ecdf9a70c0
separate always-on-top into separate mod 2020-09-02 21:46:17 +10:00
6ff3a96d81 fix #69 - option to completely hide window buttons 2020-09-02 21:16:51 +10:00
58ff18724b
fix #69 - option to completely hide window buttons 2020-09-02 21:16:51 +10:00
513b5f4cab remove extension menu hotkey conflict + make configurable 2020-09-02 20:40:35 +10:00
e158cc25b3
remove extension menu hotkey conflict + make configurable 2020-09-02 20:40:35 +10:00
0355c7d344 fix #76, height of weekly calendar view 2020-09-02 15:39:53 +10:00
648c1f8b99
fix #76, height of weekly calendar view 2020-09-02 15:39:53 +10:00
3a608f8aff page titles now respond to small-text mode 2020-09-02 11:33:18 +10:00
fed816b8da
page titles now respond to small-text mode 2020-09-02 11:33:18 +10:00
850610b213 font imports working in menu + font-chooser improvements 2020-09-02 11:10:01 +10:00
1e1808fd09
font imports working in menu + font-chooser improvements 2020-09-02 11:10:01 +10:00
ae3760e193 halved selectors for better perf., added wip littlepig themes 2020-09-02 08:49:38 +10:00
6c7e1cec9d
halved selectors for better perf., added wip littlepig themes 2020-09-02 08:49:38 +10:00
Tom
15963bae36 Merge pull request #63 from torchatlas/master
NEW MOD: Font Chooser
2020-08-29 22:22:26 +10:00
Tom
23d79c5abc
Merge pull request #63 from torchatlas/master
NEW MOD: Font Chooser
2020-08-29 22:22:26 +10:00
752bd8a45b
v0.8.5 2020-08-29 17:18:26 +10:00
890c3cbc40
hopefully address CSP problem with #66 2020-08-29 17:15:09 +10:00
Tom
e420172f31
Merge pull request #64 from Nek-12/master
Fixed and improved installation instructions
2020-08-29 14:32:32 +10:00
1b66802fc6 fix #65: separate bg and select variables. 2020-08-29 14:25:39 +10:00
2cb25fec12
fix #65: separate bg and select variables. 2020-08-29 14:25:39 +10:00
Nek-12
ff3a89f65c Github doesn't like markdown inside html blocks 2020-08-28 21:05:06 +03:00
Nek-12
51ad989a94 Fixed and improved installation instructions 2020-08-28 21:01:04 +03:00
Torch
591b39494a Update mod.js 2020-08-28 12:10:50 -04:00
Torch
69cdc4aff5 Update mod.js 2020-08-28 12:10:50 -04:00
44e1472c0b
v0.8.4 2020-08-29 02:09:40 +10:00
9d79f7462f property-layout finally working regardless of banner 2020-08-29 02:09:04 +10:00
9f66acccd8
property-layout finally working regardless of banner 2020-08-29 02:09:04 +10:00
Torch
45282e0abf Added Font Chooser 2020-08-28 12:01:46 -04:00
Torch
33777fb2f7 Added Font Chooser 2020-08-28 12:01:46 -04:00
44423552af
v0.8.3 2020-08-29 01:28:07 +10:00
219260ad68
un-break installer on windows 2020-08-29 01:26:42 +10:00
e93d5a7657
v0.8.2 2020-08-29 00:58:56 +10:00
9f5cacefd1
fixed the auto-installer
to use `./bin.js` instead of `notion-enhancer`
2020-08-29 00:57:33 +10:00
39ebd0e3fd
v0.8.1 2020-08-29 00:21:54 +10:00
30e82d321e
changelog 2020-08-29 00:20:45 +10:00
ffa35c74ae better erroring (#56), menu tweaking, gallery fullpage variable fix 2020-08-29 00:14:29 +10:00
017cb66564
better erroring (#56), menu tweaking, gallery fullpage variable fix 2020-08-29 00:14:29 +10:00
5117836dc7 fix #59 + clarity improvements 2020-08-28 22:37:46 +10:00
ab7771cba9
fix #59 + clarity improvements 2020-08-28 22:37:46 +10:00
b019a1cb83 fix #54 + padding issues in preview 2020-08-28 14:41:34 +10:00
b94c8e7b77
fix #54 + padding issues in preview 2020-08-28 14:41:34 +10:00
21a6cc2be5 fix #58 (caret-color was being mistaken for color) 2020-08-28 12:48:23 +10:00
4b2d0e71dc
fix #58 (caret-color was being mistaken for color) 2020-08-28 12:48:23 +10:00
438ad10522 update property-layout mod to notion's changes 2020-08-28 09:13:34 +10:00
2eee02cfbd
update property-layout mod to notion's changes 2020-08-28 09:13:34 +10:00
Tom
0081e3b1a1
Merge pull request #53 from Nek-12/master
Fixed typo in the path to binary (linux)
2020-08-28 08:11:46 +10:00
Nek-12
b2fc6cc8ed fixed typo in path to binary 2020-08-27 18:02:56 +03:00
Tom
8ca164e723
Merge pull request #51 from dragonwocky/imgbot
[ImgBot] Optimize images
2020-08-27 23:25:04 +10:00
ImgBotApp
1901832d0e [ImgBot] Optimize images
*Total -- 58.29kb -> 10.21kb (82.49%)

/mods/core/icons/user.png -- 44.36kb -> 0.32kb (99.29%)
/mods/core/icons/mac+linux.png -- 12.08kb -> 8.10kb (32.94%)
/mods/core/icons/file.svg -- 0.29kb -> 0.28kb (4.97%)
/mods/core/icons/close.svg -- 0.34kb -> 0.33kb (3.48%)
/mods/core/icons/minimize.svg -- 0.19kb -> 0.19kb (3.03%)
/mods/core/icons/alwaysontop_on.svg -- 0.23kb -> 0.23kb (2.53%)
/mods/core/icons/alwaysontop_off.svg -- 0.23kb -> 0.23kb (2.53%)
/mods/core/icons/maximize_off.svg -- 0.24kb -> 0.23kb (2.49%)
/mods/core/icons/maximize_on.svg -- 0.32kb -> 0.32kb (1.81%)

Signed-off-by: ImgBotApp <ImgBotHelp@gmail.com>
2020-08-27 13:22:57 +00:00
ImgBotApp
dfe7dbbd6c
[ImgBot] Optimize images
*Total -- 58.29kb -> 10.21kb (82.49%)

/mods/core/icons/user.png -- 44.36kb -> 0.32kb (99.29%)
/mods/core/icons/mac+linux.png -- 12.08kb -> 8.10kb (32.94%)
/mods/core/icons/file.svg -- 0.29kb -> 0.28kb (4.97%)
/mods/core/icons/close.svg -- 0.34kb -> 0.33kb (3.48%)
/mods/core/icons/minimize.svg -- 0.19kb -> 0.19kb (3.03%)
/mods/core/icons/alwaysontop_on.svg -- 0.23kb -> 0.23kb (2.53%)
/mods/core/icons/alwaysontop_off.svg -- 0.23kb -> 0.23kb (2.53%)
/mods/core/icons/maximize_off.svg -- 0.24kb -> 0.23kb (2.49%)
/mods/core/icons/maximize_on.svg -- 0.32kb -> 0.32kb (1.81%)

Signed-off-by: ImgBotApp <ImgBotHelp@gmail.com>
2020-08-27 13:22:57 +00:00
f71990b87a
...remove beta notice from readme 2020-08-27 23:21:48 +10:00
143f690740
v0.8.0 2020-08-27 23:13:00 +10:00
b9ce8e5e54 add discord to tray 2020-08-27 23:11:49 +10:00
4279bbc00d
add discord to tray 2020-08-27 23:11:49 +10:00
bae4c4cc06
merge js branch into master 2020-08-27 23:06:01 +10:00
a8dd8cb145 custom logo + discord invite 2020-08-27 23:00:08 +10:00
21380453be
custom logo + discord invite 2020-08-27 23:00:08 +10:00
6081bdb563 dev docs 2020-08-26 22:42:49 +10:00
b502d77545
dev docs 2020-08-26 22:42:49 +10:00
8c18bb5174
tweaks 2020-08-23 14:21:11 +10:00
a2ce74fc86
how to create a css file 2020-08-20 23:08:27 +10:00
8691ac735d
update guide 2020-08-20 23:03:18 +10:00
48eba9b956 contributing guide, css transitions, bg of account picker 2020-08-20 10:08:29 +10:00
371b89a262
contributing guide, css transitions, bg of account picker 2020-08-20 10:08:29 +10:00
4ad8a9446b readme additions + bugfixes 2020-08-19 00:13:14 +10:00
19bf587b8a
readme additions + bugfixes 2020-08-19 00:13:14 +10:00
05c7ca099f property layout width fixed for fullpage pages 2020-08-18 21:44:41 +10:00
934b3093bb
property layout width fixed for fullpage pages 2020-08-18 21:44:41 +10:00
7af9b7836d property layout extension ✔ 2020-08-18 11:40:05 +10:00
4e09b4f135
property layout extension ✔ 2020-08-18 11:40:05 +10:00
5275d71fd6 reduce lag due to maximization detect + wip property layout 2020-08-18 09:53:34 +10:00
f95b5eadd0
reduce lag due to maximization detect + wip property layout 2020-08-18 09:53:34 +10:00
58bbb9e385 remove loop from core client.js 2020-08-17 23:43:43 +10:00
8ee12ea57e
remove loop from core client.js 2020-08-17 23:43:43 +10:00
e787427214 weekly view working, app relaunch btn in menu modified alert 2020-08-17 22:55:51 +10:00
0af0e5026f
weekly view working, app relaunch btn in menu modified alert 2020-08-17 22:55:51 +10:00
583e96ac1a various perf stuff + bugfixes + attempt at weekly view 2020-08-16 23:44:45 +10:00
bb660a6bbb
various perf stuff + bugfixes + attempt at weekly view 2020-08-16 23:44:45 +10:00
ebc9adc616 perf improvements + fullwidth page banner in preview 2020-08-16 12:42:03 +10:00
7b12815953
perf improvements + fullwidth page banner in preview 2020-08-16 12:42:03 +10:00
94830b5da5 make it possible to enabled/disable multiple extensions 2020-08-15 22:44:31 +10:00
a57b7ca57a
make it possible to enabled/disable multiple extensions 2020-08-15 22:44:31 +10:00
3c14f1e85d right-to-left extension 2020-08-15 22:20:20 +10:00
fac7a72654
right-to-left extension 2020-08-15 22:20:20 +10:00
cf0d516234 night shift extension 2020-08-15 21:58:39 +10:00
330e5a5f62
night shift extension 2020-08-15 21:58:39 +10:00
3aa5cdef18 emoji sets extension 2020-08-14 23:58:21 +10:00
60dcb0f13d
emoji sets extension 2020-08-14 23:58:21 +10:00
a9a6fc5929 pastel dark theme + custom author definitions 2020-08-14 23:18:38 +10:00
69d45d9d77
pastel dark theme + custom author definitions 2020-08-14 23:18:38 +10:00
7f2270904b purple gameish theme... need to link to reddit account 2020-08-13 23:24:45 +10:00
64358e5576
purple gameish theme... need to link to reddit account 2020-08-13 23:24:45 +10:00
ff86afd8b8 add colour picker for dark+ primary colour 2020-08-12 23:32:44 +10:00
616cb839fc
add colour picker for dark+ primary colour 2020-08-12 23:32:44 +10:00
44b0a0e378 dark+ fixes, general slicking up 2020-08-11 21:24:00 +10:00
b348bf871b
dark+ fixes, general slicking up 2020-08-11 21:24:00 +10:00
554f93efac various css tweaks and fixes 2020-08-11 18:38:18 +10:00
b818f39386
various css tweaks and fixes 2020-08-11 18:38:18 +10:00
f7c71eb9c7 patch removal and help commands + separate focus mode into an extension 2020-08-11 11:16:23 +10:00
18f24804d2
patch removal and help commands + separate focus mode into an extension 2020-08-11 11:16:23 +10:00
db7992cfc3 patch font sizes 2020-08-11 10:04:55 +10:00
7b06843fe1
patch font sizes 2020-08-11 10:04:55 +10:00
72236295e3
patch escape slashes in realpath 2020-08-11 09:43:11 +10:00
a45601b2ad theoretically patch broken windows install 2020-08-11 09:32:22 +10:00
606f6e6325
theoretically patch broken windows install 2020-08-11 09:32:22 +10:00
78b9510e93 port alejandro's neutral theme to new variables 2020-08-09 23:29:27 +10:00
ff41cb78d5
port alejandro's neutral theme to new variables 2020-08-09 23:29:27 +10:00
eae51c6021 toggle window tray opt + css preview padding 2020-08-09 19:35:03 +10:00
c5ef5b5b70
toggle window tray opt + css preview padding 2020-08-09 19:35:03 +10:00
3c50f5cbbc dark+ theme (code highlight needs work) 2020-08-06 21:33:21 +10:00
0b123cc41b
dark+ theme (code highlight needs work) 2020-08-06 21:33:21 +10:00
7f779e35e5 working menu search 2020-08-05 21:48:11 +10:00
5ba3971dee
working menu search 2020-08-05 21:48:11 +10:00
0a6a540a41 menu options: input + files 2020-08-04 23:02:20 +10:00
170700dffe
menu options: input + files 2020-08-04 23:02:20 +10:00
92501bd7e1 menu options: toggles + selects 2020-08-04 15:32:04 +10:00
d40462aa1b
menu options: toggles + selects 2020-08-04 15:32:04 +10:00
6dacae46b5 improve markdown regexes 2020-08-04 13:13:01 +10:00
4e953c198d
improve markdown regexes 2020-08-04 13:13:01 +10:00
158ce182df mod toggling, first extension: bracketed links 2020-08-04 00:31:27 +10:00
583e6f9adf
mod toggling, first extension: bracketed links 2020-08-04 00:31:27 +10:00
d8351aa49e mod info boxes 2020-08-02 23:14:13 +10:00
e0bc581b41
mod info boxes 2020-08-02 23:14:13 +10:00
19593e1d01 add alerts (inc. update checker) to menu 2020-08-02 16:26:28 +10:00
933ee3d3b1
add alerts (inc. update checker) to menu 2020-08-02 16:26:28 +10:00
7d0560228b button spacing bugfix 2020-08-02 09:01:12 +10:00
060b1ffbe9
button spacing bugfix 2020-08-02 09:01:12 +10:00
f6b52e5c02 theme menu window + bunch of other tweaks 2020-08-01 23:46:43 +10:00
7d51ea9caa
theme menu window + bunch of other tweaks 2020-08-01 23:46:43 +10:00
8dd7d0649c menu window creation 2020-07-28 19:03:35 +10:00
9f8c70c203
menu window creation 2020-07-28 19:03:35 +10:00
9f92ff325d refactor some stuff, fix some bugs, apply theme to ctrl+f popup 2020-07-26 16:32:55 +10:00
aadcc50a11
refactor some stuff, fix some bugs, apply theme to ctrl+f popup 2020-07-26 16:32:55 +10:00
06211688b6 light theoretically 100% :) 2020-07-25 22:49:08 +10:00
160fad9f60
light theoretically 100% :) 2020-07-25 22:49:08 +10:00
5c84fbebb6 light 90% + other tweaks 2020-07-25 15:16:15 +10:00
2edfcd63fe
light 90% + other tweaks 2020-07-25 15:16:15 +10:00
771aa0b493 dark theme bugfixes: focused input borders, weird lines in tables 2020-07-25 11:29:39 +10:00
c246b24af2
dark theme bugfixes: focused input borders, weird lines in tables 2020-07-25 11:29:39 +10:00
2f74449189 dark theme variables complete (?) + scrollbar fix + titlebar tweaks 2020-07-24 23:52:21 +10:00
5752a95c23
dark theme variables complete (?) + scrollbar fix + titlebar tweaks 2020-07-24 23:52:21 +10:00
830492e181 more dark theme stuff, inc. bugfixes + code higlight 2020-07-23 23:33:09 +10:00
fd22fa286c
more dark theme stuff, inc. bugfixes + code higlight 2020-07-23 23:33:09 +10:00
2d37b807ea collect theme variables: dark colours 2020-07-22 23:45:59 +10:00
a0edfd1c27
collect theme variables: dark colours 2020-07-22 23:45:59 +10:00
8a1709c559 maximise icon updates as needed 2020-07-21 09:47:19 +10:00
2f90f65f1c
maximise icon updates as needed 2020-07-21 09:47:19 +10:00
9a012eb3a7 configurable dragarea size 2020-07-21 00:09:24 +10:00
9d32ae16e0
configurable dragarea size 2020-07-21 00:09:24 +10:00
35a411b977 titlebar + scrollbars 2020-07-19 22:50:59 +10:00
d2c5a800cb
titlebar + scrollbars 2020-07-19 22:50:59 +10:00
a3e55c6121 taken control of createWindow 2020-07-19 16:24:34 +10:00
21facae40c
taken control of createWindow 2020-07-19 16:24:34 +10:00
0621a5710c de-async-ifying stuff = tray fixed! 2020-07-18 23:50:56 +10:00
d423bdbfd0
de-async-ifying stuff = tray fixed! 2020-07-18 23:50:56 +10:00
48fa62d75d tray failing: every time something starts working everything else falls apart :/ 2020-07-18 23:21:55 +10:00
b13fe98d40
tray failing: every time something starts working everything else falls apart :/ 2020-07-18 23:21:55 +10:00
4faa7bb919
loader working + enhancement:// protocol registered 2020-07-17 18:42:55 +10:00
8440a3dcca add store to loader, bugfix homedir with wsl 2020-07-16 23:48:09 +10:00
75cd042bd5
add store to loader, bugfix homedir with wsl 2020-07-16 23:48:09 +10:00
8925234dc5 modloader insertion 2020-07-16 23:14:41 +10:00
b7038cda28
modloader insertion 2020-07-16 23:14:41 +10:00
0d2dfaca61
redesign module system again after technological / enable-disable considerations 2020-07-16 19:00:36 +10:00
26a82f400f
that should be a bit clearer, if verbose 2020-07-14 22:43:40 +10:00
2237df4334
i did not know markdown did that 2020-07-14 21:37:40 +10:00
2cb52722dd redesign module system after considerations of what is most technologically friendly (both to implement and use) 2020-07-14 21:28:05 +10:00
382af7117c
redesign module system after considerations of what is most technologically friendly (both to implement and use) 2020-07-14 21:28:05 +10:00
5c648be5eb document mod.js, prepare to load 2020-07-14 13:01:07 +10:00
c60299ed85
document mod.js, prepare to load 2020-07-14 13:01:07 +10:00
7cbb744e5f basic mod structure 2020-07-14 00:23:21 +10:00
2131e4e19d
basic mod structure 2020-07-14 00:23:21 +10:00
78865542b7
start readme, add check command, apply complete except for enhancements 2020-07-13 22:37:52 +10:00
574741de06
remove: prompt deletion of ~/.notion-enhancer 2020-07-13 15:28:38 +10:00
a8472b2da0
add contributing guide 2020-07-13 14:45:24 +10:00
7f31c24e92
begin work, day 1: cli + removal script 2020-07-13 00:40:30 +10:00
78ca0b719d
...update docs 2020-07-09 21:32:59 +10:00
b5043508d9
add update notifier 2020-07-09 21:31:17 +10:00
d1875ffc90
Merge pull request #16 from dragonwocky/imgbot
[ImgBot] Optimize images
2020-07-09 20:45:50 +10:00
ImgBotApp
c32417ca54
[ImgBot] Optimize images
*Total -- 719.40kb -> 325.92kb (54.7%)

/docs/screenshots/app-enhanced.jpg -- 171.97kb -> 67.03kb (61.02%)
/screenshots/app-enhanced.jpg -- 171.97kb -> 67.03kb (61.02%)
/docs/screenshots/theme-dark+.jpg -- 158.51kb -> 70.03kb (55.82%)
/screenshots/theme-dark+.jpg -- 158.51kb -> 70.03kb (55.82%)
/screenshots/app-tray.jpg -- 28.49kb -> 25.18kb (11.63%)
/docs/screenshots/app-tray.jpg -- 28.49kb -> 25.18kb (11.63%)
/resources/icons/close.svg -- 0.34kb -> 0.33kb (3.48%)
/resources/icons/minimise.svg -- 0.18kb -> 0.17kb (2.22%)
/resources/icons/alwaysontop_off.svg -- 0.21kb -> 0.21kb (1.83%)
/resources/icons/alwaysontop_on.svg -- 0.21kb -> 0.21kb (1.83%)
/resources/icons/maximise_off.svg -- 0.22kb -> 0.21kb (1.79%)
/resources/icons/maximise_on.svg -- 0.31kb -> 0.30kb (1.28%)

Signed-off-by: ImgBotApp <ImgBotHelp@gmail.com>
2020-07-09 10:42:49 +00:00
03b598a17d
0.7.0 release-ready: replace twitter emojiset with system emojis 2020-07-09 20:42:01 +10:00
faab6d013f
fix misidentification of mac as windows 2020-07-09 19:39:14 +10:00
0e168b5f84
close to tray for macOS's native close window button 2020-07-09 18:50:50 +10:00
422be7410a
use png for tray on linux 2020-07-09 18:32:20 +10:00
156b4061b0
support for notion-deb-builder 2020-07-09 18:26:29 +10:00
e5820f5c18 add platform support issue template 2020-07-09 18:11:29 +10:00
633a4250d1
swap maximise buttons 2020-07-09 16:55:16 +10:00
582a8cc642
replace unicode window button icons with svgs 2020-07-09 14:58:10 +10:00
31ccd13ced
remove extra buttons from macOS 2020-07-08 23:22:28 +10:00
96571f2b31
tray support improvements for mac/linux (untested) 2020-07-08 22:58:52 +10:00
a5afb72f57
bump version number to clean up conflicts 2020-07-07 16:15:35 +10:00
89a2dd9c39
fix cleaner linux filepath 2020-07-07 08:47:15 +10:00
31c75c7d70
fix icon size on mac 2020-07-06 22:17:39 +10:00
f459d10c39
potential linux/macOS tray support 2020-07-06 14:21:29 +10:00
6960c0915f
Merge pull request #14 from torchatlas/master
Enhanced window control buttons - hover effect and cursor change
2020-07-04 11:49:26 +10:00
TorchAtlas
d2ba4cd346 Enhanced window control buttons - hover effect and cursor change
Close window control button now turns red when hovered over

Window control buttons no longer show a pointer cursor when hovered over - now uses default cursor, like other apps
2020-07-02 10:33:46 -04:00
ded4323544
improve titlebar selectors + syntax errors 2020-07-02 12:58:50 +10:00
8dfb764199
force resource path to use / 2020-07-02 12:53:23 +10:00
6cedb7790a
add --theme-table_border 2020-07-02 12:36:30 +10:00
d3d3b4007f
app no longer crashes when sidebar toggled 2020-07-01 19:12:57 +10:00
382843668a
more filepath improvements: relative paths + /opt/notion if /opt/notion-app not present 2020-07-01 16:48:15 +10:00
f20aaf1f4c
remove my-system-specific python path 2020-07-01 16:46:49 +10:00
babcae1bd5
fix cross-platform filepaths and cleaner.py? 2020-07-01 16:13:03 +10:00
3b908e1e27
remove beta notice + add version downloads 2020-06-30 16:24:27 +10:00
c1d6041c6a
remove ~beta from customiser.py 2020-06-30 16:01:10 +10:00
6f6edb2cd8
responsive window buttons 2020-06-30 15:59:11 +10:00
ef7718e920
Merge pull request #9 from dragonwocky/imgbot
[ImgBot] Optimize images
2020-06-29 18:26:36 +10:00
ImgBotApp
d2ff1a855a
[ImgBot] Optimize images
*Total -- 8,076.88kb -> 6,153.09kb (23.82%)

/web-logo.png -- 11.14kb -> 7.02kb (36.95%)
/docs/web-logo.png -- 11.14kb -> 7.02kb (36.95%)
/screenshots/theme-dark+.jpg -- 234.39kb -> 158.51kb (32.38%)
/docs/screenshots/theme-dark+.jpg -- 234.39kb -> 158.51kb (32.38%)
/screenshots/discussion-hidden.jpg -- 61.41kb -> 43.29kb (29.52%)
/docs/screenshots/discussion-hidden.jpg -- 61.41kb -> 43.29kb (29.52%)
/screenshots/discussion-default.jpg -- 76.04kb -> 53.91kb (29.11%)
/docs/screenshots/discussion-default.jpg -- 76.04kb -> 53.91kb (29.11%)
/screenshots/table-columnunder100px.jpg -- 118.36kb -> 84.69kb (28.45%)
/docs/screenshots/table-columnunder100px.jpg -- 118.36kb -> 84.69kb (28.45%)
/screenshots/board-hidehidden.jpg -- 76.51kb -> 55.01kb (28.1%)
/docs/screenshots/board-hidehidden.jpg -- 76.51kb -> 55.01kb (28.1%)
/screenshots/board-hideaddgroup.jpg -- 80.35kb -> 58.83kb (26.78%)
/docs/screenshots/board-hideaddgroup.jpg -- 80.35kb -> 58.83kb (26.78%)
/screenshots/board-shrinkpadding.jpg -- 80.14kb -> 58.88kb (26.52%)
/docs/screenshots/board-shrinkpadding.jpg -- 80.14kb -> 58.88kb (26.52%)
/screenshots/board-hideaddnew.jpg -- 78.64kb -> 58.14kb (26.07%)
/docs/screenshots/board-hideaddnew.jpg -- 78.64kb -> 58.14kb (26.07%)
/screenshots/board-default.jpg -- 81.61kb -> 60.34kb (26.06%)
/docs/screenshots/board-default.jpg -- 81.61kb -> 60.34kb (26.06%)
/screenshots/app-default.jpg -- 224.85kb -> 167.37kb (25.57%)
/docs/screenshots/app-default.jpg -- 224.85kb -> 167.37kb (25.57%)
/docs/screenshots/app-enhanced.jpg -- 229.51kb -> 171.97kb (25.07%)
/screenshots/app-enhanced.jpg -- 229.51kb -> 171.97kb (25.07%)
/screenshots/app-unenhanced.jpg -- 243.00kb -> 182.94kb (24.72%)
/docs/screenshots/app-unenhanced.jpg -- 243.00kb -> 182.94kb (24.72%)
/screenshots/fonts-custom.jpg -- 242.63kb -> 184.22kb (24.07%)
/docs/screenshots/fonts-custom.jpg -- 242.63kb -> 184.22kb (24.07%)
/screenshots/table-before.jpg -- 108.02kb -> 82.07kb (24.03%)
/docs/screenshots/table-before.jpg -- 108.02kb -> 82.07kb (24.03%)
/docs/screenshots/fonts-resized.jpg -- 242.58kb -> 184.53kb (23.93%)
/screenshots/fonts-resized.jpg -- 242.58kb -> 184.53kb (23.93%)
/screenshots/table-smallercolumnicons.jpg -- 109.37kb -> 83.43kb (23.72%)
/docs/screenshots/table-smallercolumnicons.jpg -- 109.37kb -> 83.43kb (23.72%)
/screenshots/table-shrinkpadding.jpg -- 110.27kb -> 84.30kb (23.55%)
/docs/screenshots/table-shrinkpadding.jpg -- 110.27kb -> 84.30kb (23.55%)
/docs/screenshots/table-hidecolumnicons.jpg -- 105.68kb -> 80.87kb (23.48%)
/screenshots/table-hidecolumnicons.jpg -- 105.68kb -> 80.87kb (23.48%)
/docs/screenshots/table-hideaddrow.jpg -- 97.89kb -> 74.96kb (23.42%)
/screenshots/table-hideaddrow.jpg -- 97.89kb -> 74.96kb (23.42%)
/screenshots/table-hidecalculationsrow.jpg -- 100.07kb -> 76.76kb (23.29%)
/docs/screenshots/table-hidecalculationsrow.jpg -- 100.07kb -> 76.76kb (23.29%)
/screenshots/table-centredheaders.jpg -- 110.16kb -> 84.54kb (23.26%)
/docs/screenshots/table-centredheaders.jpg -- 110.16kb -> 84.54kb (23.26%)
/docs/screenshots/cover-thinner.jpg -- 269.43kb -> 209.47kb (22.25%)
/screenshots/cover-thinner.jpg -- 269.43kb -> 209.47kb (22.25%)
/screenshots/preview-wider.jpg -- 266.80kb -> 216.57kb (18.83%)
/docs/screenshots/preview-wider.jpg -- 266.80kb -> 216.57kb (18.83%)
/screenshots/cover-default.jpg -- 376.55kb -> 306.16kb (18.69%)
/docs/screenshots/cover-default.jpg -- 376.55kb -> 306.16kb (18.69%)
/screenshots/preview-default.jpg -- 270.57kb -> 221.10kb (18.28%)
/docs/screenshots/preview-default.jpg -- 270.57kb -> 221.10kb (18.28%)
/screenshots/app-tray.jpg -- 32.46kb -> 26.67kb (17.84%)
/docs/screenshots/app-tray.jpg -- 32.46kb -> 26.67kb (17.84%)

Signed-off-by: ImgBotApp <ImgBotHelp@gmail.com>
2020-06-29 08:24:39 +00:00
3cc431c088
documentation update (mainly images) 2020-06-29 18:23:36 +10:00
e9a45f7051 issue templates 2020-06-29 13:42:25 +10:00
aa5df4341b
Merge pull request #4 from Blacksuan19/master
Fix linux installation
2020-06-27 15:25:55 +10:00
Abubakar Yagoub
ba4342dd60 README: add Linux install instructions 2020-06-27 13:01:05 +08:00
blacksuan19
b83c4e958c customizer: fix linux installation
Signed-off-by: blacksuan19 <i@blacksuan19.me>
2020-06-26 23:57:31 +08:00
3a617933aa
move enhancer_version.txt into app folder 2020-06-15 21:32:45 +10:00
0f066316cc
bugfix hotkey, version checker 2020-06-15 19:18:54 +10:00
4a01fe9761
fix docs img width 2020-06-13 11:16:35 +10:00
a5db291250
colour theming: line backgrounds 2020-06-13 11:12:20 +10:00
7e593bfa6f
docs update 2020-06-13 10:40:38 +10:00
5eae5ecb5b
added the first few of the images 2020-06-02 23:09:59 +10:00
b9810c9d62
update docs. still needs screenshots + documentative update 2020-06-01 23:14:36 +10:00
c3436d372a
theme support + hotkey focusing improvement 2020-06-01 20:08:01 +10:00
33fe42b024
theme support 90% 2020-05-31 22:17:59 +10:00
d6eca764d6
missing @import statement: why do i always do these things 2020-05-31 00:34:09 +10:00
b139e2ddf9
missed 1 # -_- = formatting fix, final commit for the day 2020-05-31 00:31:20 +10:00
c30d83bee8
add some slight contrast to dark+ table column title bgs 2020-05-31 00:28:45 +10:00
182303aeda
update changelog to track bugfix 2020-05-31 00:13:04 +10:00
1f3b7fc20f
Merge pull request #2 from etnperlong/master
Fix issue #1: gbk codec can't decode
2020-05-31 00:09:39 +10:00
ad040d83eb
new styles after user feedback 2020-05-31 00:06:56 +10:00
Evans Mike
98ff85dfa8 Fix issue #1: gbk codec can't decode 2020-05-27 01:31:11 +08:00
187 changed files with 13998 additions and 2016 deletions

107
.gitignore vendored
View File

@ -1,104 +1,9 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
# builds
dist/
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
# dependencies
node_modules/
jspm_packages/
# TypeScript v1 declaration files
typings/
# TypeScript cache
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
.env.test
# parcel-bundler cache (https://parceljs.org/)
.cache
# Next.js build output
.next
# Nuxt.js build / generate output
.nuxt
dist
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and *not* Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output
.vuepress/dist
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port
# yarn
.yarn/
.pnp.*

View File

@ -1,7 +1,384 @@
# changelog
if something is ~~crossed out~~, then it is no longer a feature included by default,
but can still easily be enabled by following instructions in the [docs](README.md).
### v0.11.0 (dev)
a complete redesign & rewrite of the enhancer, with new features and a port to the browser as a chrome extension.
#### new
- cross-environment and properly documented api to replace helpers.
- cross-environment mod loader structure.
- "integrations", a category of mods that can access/use an unofficial notion api.
- notifications sourced from an online endpoint for sending global user alerts.
- simplify user installations by depending on the chrome web store and [notion-repackaged](https://github.com/notion-enhancer/notion-repackaged).
- separate menu profiles for mod configurations.
- a hotkey option type that allows typing in/pressing a hotkey to enter it, instead of typing.
- a rainbow indentation lines style.
- border & background style options for the code line numbers extension.
- an icon sets option to encode images to data urls to prevent quality reduction.
- customisation of integrated titlebar & always on top window buttons.
- an open on startup option under the tray mod.
- optional icon or title-only tab labels.
- choice of tab layout styles: traditional tabbed, traditional, bubble and compact.
- a hotkey for reopening closed tabs.
- an option to remember last open tabs for a continue-where-you-left-off experience
(recently active tabs are reopened after an app relaunch).
#### improved
- split the core mod into separate mods for specific features.
- theming variables that are applied more specifically, less laggy, and less complicated.
- merged bracketed-links into tweaks.
- a redesigned menu with nicer ui, separate categories for mods and a sidebar for configuration.
- simplified and smoothened the side panel + moved it to the core so any mod can hook into it.
- font chooser option for heading fonts.
- renamed "property-layout" to "collapsible properties", added per-page memory of collapse state.
- chevron icon instead of arrow for scroll to top.
- moved word counter to display in the side panel instead of within the page,
implemented a more accurate word counter method.
- the topbar icons extension defaults to the notion default topbar icons for comment/updates/favorite/more,
but can revert them to text (it still adds a custom icon for the share button).
- relative indenting in outliner.
- rtl support for toggles, indentation lines, table of contents and databases + force inline math to ltr.
- replaced the "truncated table titles" extension with a "truncated titles" extension
with an option to truncate timeline item titles.
- renamed "notion icons" to "icon sets" with new support for uploading/reusing custom icons
directly within the icon picker.
- moved the tray to its own configurable and enable/disable-able mod, with window management enhancements
that follow more sensible defaults and work more reliably.
- tabs will auto shrink/expand to take up available space instead of wrapping to a second line.
- a visually revamped cli to more clearly and aesthetically communicate status and usage.
- cli can now detect and apply to user-only installations on macOS.
- a shortcut built into the cli to fix the "you do not have permission to open this app" error on macos.
#### removed
- integrated scrollbar tweak (notion now includes by default).
- js insert. css insert moved to tweaks mod.
- majority of layout and font size variables - better to leave former to notion and use `ctrl +`/`ctrl -` for latter.
- the "panel sites" extension, due to it's limited/buggy functionality and incompatibility with reimplementation.
#### fixed
- bypass csp restrictions.
- many. like many many. all the bugfixes. (mostly a side effect of completely rewriting everything,
but reported extension-specific bugs were all intentionally fixed.)
#### themes
- "nord" = an arctic, north-bluish color palette.
- "gruvbox light" = a sepia, 'retro groove' palette based on the vim theme of the same name.
- "gruvbox dark" = a gray, 'retro groove' palette based on the vim theme of the same name.
- "light+" = a simple white theme that brightens coloured text and blocks,
with configurable accents (formerly littlepig light).
- "playful purple" = a purple-shaded theme with bright highlights (formerly littlepig dark and gameish).
- "pinky boom" = pinkify your life.
#### extensions
- "calendar scroll" = add a button to jump down to the current week in fullpage/infinite-scroll calendars.
- "global block links" = easily copy the global link of a page or block.
- "collapsible headers" = adds toggles to collapse header sections of pages.
- "simpler databases" = adds a menu to inline databases to toggle ui elements.
- "view scale" = zoom in/out of the notion window with the mousewheel or a visual slider (`ctrl/cmd +/-` are available in-app by default).
#### tweaks
- wrap tables to page width. - hide "Type '/' for commands".
- quote block quotation marks.
- responsive columns breakpoint (%).
- accented links.
- full width pages.
- image alignment (center/left/right).
#### integrations
- "quick note" = adds a hotkey & a button in the bottom right corner to jump to a new page in a notes database (target database id must be set).
**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/desktop/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/desktop/archive/v0.6.0.zip)
### v0.5.0 (2020-05-23)
@ -9,11 +386,16 @@ but can still easily be enabled by following instructions in the [docs](README.m
- 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 updates.
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.
_(forked by [@dragonwocky](https://github.com/dragonwocky).)_
> 📥 [notion-enhancer.v0.5.0.zip](https://github.com/notion-enhancer/desktop/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)

View File

@ -1,7 +1,6 @@
MIT License
Copyright (c) 2020 TarasokUA
Copyright (c) 2020 dragonwocky
Copyright (c) 2023 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@ -19,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
SOFTWARE.

242
README.md
View File

@ -1,241 +1,5 @@
# notion enhancer
# notion-enhancer/desktop
an enhancer/customiser for the all-in-one productivity workspace [notion.so](https://www.notion.so/)
Customise the all-in-one productivity workspace Notion.
## installation
currently, only win10 is supported. it is possible to run this script via the wsl to modify the win10 notion app.
(the [styles](#styling) should also work for the web version.
these can be installed via an extension like [stylus](https://chrome.google.com/webstore/detail/stylus/clngdbkpkpeebahjckkjfobafhncgmne?hl=en)
or a built-in feature like [userChrome.css](https://www.userchrome.org/).)
1. install [node.js](https://nodejs.org/en/) (if using the wsl, it is recommended to install via [nvm](https://github.com/nvm-sh/nvm#install--update-script)).
2. install [python](https://www.python.org/) (if using the wsl, follow [this guide](https://docs.python-guide.org/starting/install3/linux/)).
3. reboot.
4. in cmd (on windows) or bash (with wsl), run `npm install -g asar` (check installation by running `asar`).
5. [download this enhancer](https://github.com/dragonwocky/notion-enhancer/archive/master.zip) & extract to a location it can safely remain (this must be in the windows filesystem,
even if you are running the script from the wsl).
6. ensure notion is closed.
7. optional: to remove previous versions of notion enhancer, run `cleaner.py`
8. optional: modify the `resources/user.css` files to your liking.
9. run `customiser.py` to build changes.
done: run notion and enjoy.
**oh no, now my app won't open!**
1. kill any notion tasks in the task manager (`ctrl+shift+esc`).
2. run `cleaner.py`.
3. reboot.
4. follow instructions above (ensuring notion _isn't_ running! again, check task manager).
## this is a fork
credit where credit is due, this was originally made by Uzver (github: [@TarasokUA](https://github.com/TarasokUA),
telegram: [UserFromUkraine](https://t.me/UserFromUkraine), discord: Uzver#8760).
he has approved my go-ahead with this fork, as he himself no longer wishes to continue development on the project.
## features
### titlebar
default windows titlebar/frame has been replaced by one more fitting to the theme of the app.
this includes the addition of an extra button, "always on top"
symbolised with an arrow (4th from the right). when toggled to point up,
notion will remain the top visible window even if not focused.
to customise which characters are used for these buttons, open in the `resources/preload.js` file,
find the relevant button (read the comments) and replace its icon with your chosen unicode character (e.g.
replacing `element.innerHTML = '▢';` with `element.innerHTML = '🙄';`).
### nicer scrollbars
i mean, yeah. get rid of those ugly default scrollbars and use nice inconspicuous
ones that actually look as if they're part of notion.
to add these to the web version, copy lines 44 - 75 from `user.css` into your css customiser.
### hotkeys
- **reload window**: in addition to the built-in `CmdOrCtrl+R` reload,
you can now reload a window with `F5`.
- **toggle all notion windows to/from the tray**: `CmdOrCtrl+Shift+A` by default.
to set your own toggle hotkey, open `customiser.py` and change line 16 (`hotkey = 'CmdOrCtrl+Shift+A'`)
to your preference. you will need to run or re-run `customiser.py` afterwards.
### tray
- single-click to toggle app visibility. right click to open menu.
- settings will be saved in `%localappdata%/Programs/Notion/resources/app/user-preferences.json`
- **run on startup**: run notion on boot/startup. (default: true)
- **hide on open**: hide the launch of notion to the tray. (default: false)
- **open maximised**: maximise the app on open. (default: false)
- **close to tray**: close window to tray rather than closing outright
on click of `⨉`. does not apply if multiple notion windows are open. (default: false)
### styling
due to `customiser.py` setting up a direct link to `resources/user.css`,
changes will be applied instantly on notion reload
(no need to re-run `customiser.py` every time you want to change some styles).
these should also work for the web version, if copied into your css customiser.
css below will work for every instance of the element, but if you wish to hide only a specific element
(e.g. the '+ new' table row) it is recommended that you prepend each selector with `[data-block-id='ID']` ([video tutorial on fetching IDs](https://www.youtube.com/watch?v=6V7eqShm_4w)).
#### wider page view
```css
.notion-peek-renderer > div:nth-child(2) {
max-width: 85vw !important;
}
```
#### thinner cover image
```css
[style^='position: relative; width: 100%; display: flex; flex-direction: column; align-items: center; height: 30vh;'] {
height: 12vh !important;
}
[style^='position: relative; width: 100%; display: flex; flex-direction: column; align-items: center; height: 30vh;']
img {
height: 20vh !important;
}
```
#### table columns below 100px
**not recommended!** this is unreliable and will cause bugs.
coincidentally, this is also what the youtube video linked above shows how to do.
as it is a per-table-column style, unlike all others here, it must be prepended with the block ID.
```css
[data-block-id^='ID']
> [style^='display: flex; position: absolute; background: rgb(47, 52, 55); z-index: 82; height: 33px; color: rgba(255, 255, 255, 0.6);']
> div:nth-child(1)
> div:nth-child(10)
> div:nth-child(1),
[data-block-id^='ID']
> [style^='position: relative; min-width: calc(100% - 192px);']
> [data-block-id]
> div:nth-child(10),
[data-block-id^='ID'] > div:nth-child(5) > div:nth-child(10) {
width: 45px !important;
}
[data-block-id^='ID']
[style^='position: absolute; top: 0px; left: 0px; pointer-events: none;']:not(.notion-presence-container) {
display: none;
}
```
#### hide '+ new' table row
```css
.notion-table-view-add-row {
display: none !important;
}
```
#### hide calculations table row
```css
.notion-table-view-add-row + div {
display: none !important;
}
```
#### hide '+ new' board row
```css
.notion-board-group
[style='user-select: none; transition: background 120ms ease-in 0s; cursor: pointer; display: inline-flex; align-items: center; flex-shrink: 0; white-space: nowrap; height: 32px; border-radius: 3px; font-size: 14px; line-height: 1.2; min-width: 0px; padding-left: 6px; padding-right: 8px; color: rgba(255, 255, 255, 0.4); width: 100%;'] {
display: none !important;
}
```
#### hide board view hidden columns
```css
.notion-board-view > [data-block-id] > div:nth-last-child(2),
.notion-board-view > [data-block-id] > div:first-child > div:nth-last-child(2) {
display: none !important;
}
```
#### hide board view 'add a group'
```css
.notion-board-view > [data-block-id] > div:last-child,
.notion-board-view > [data-block-id] > div:first-child > div:last-child {
display: none !important;
}
```
#### centre-align table column headers
```css
.notion-table-view-header-cell > div > div {
margin: 0px auto;
}
```
#### smaller table column header icons
```css
[style^='display: flex; position: absolute; background: rgb(47, 52, 55); z-index: 82; height: 33px; color: rgba(255, 255, 255, 0.6);']
div:nth-child(1)
svg {
height: 10px !important;
width: 10px !important;
margin-right: -4px;
}
```
#### remove icons from table column headers
```css
.notion-table-view-header-cell [style^='margin-right: 6px;'] {
display: none !important;
}
```
#### removing/decreasing side padding for tables
```css
[style^='flex-shrink: 0; flex-grow: 1; width: 100%; max-width: 100%; display: flex; align-items: center; flex-direction: column; font-size: 16px; color: rgba(255, 255, 255, 0.9); padding: 0px 96px 30vh;']
.notion-table-view,
[class='notion-scroller'] > .notion-table-view {
padding-left: 35px !important;
padding-right: 15px !important;
min-width: 0% !important;
}
[style^='flex-shrink: 0; flex-grow: 1; width: 100%; max-width: 100%; display: flex; align-items: center; flex-direction: column; font-size: 16px; color: rgba(255, 255, 255, 0.9); padding: 0px 96px 30vh;']
.notion-selectable
.notion-scroller.horizontal::-webkit-scrollbar-track {
margin-left: 10px;
margin-right: 10px;
}
```
#### removing/decreasing side padding for boards
```css
.notion-board-view {
padding-left: 10px !important;
padding-right: 10px !important;
}
```
## other details
i have an unhealthy habit of avoiding capital letters. nothing enforces this, i just do it.
the notion logo belongs entirely to the notion team, and was sourced from their
[media kit](https://www.notion.so/Media-Kit-205535b1d9c4440497a3d7a2ac096286).
if you have any questions, check [my website](https://dragonwocky.me/) for contact details.
[read the docs online](https://notion-enhancer.github.io/)

331
bin.mjs Executable file
View File

@ -0,0 +1,331 @@
#!/usr/bin/env node
/**
* notion-enhancer
* (c) 2023 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
* (https://notion-enhancer.github.io/) under the MIT license
*/
import os from "node:os";
import { createRequire } from "node:module";
import chalk from "chalk-template";
import arg from "arg";
import {
backupApp,
enhanceApp,
getInsertVersion,
getResourcePath,
restoreApp,
setNotionPath,
} from "./scripts/enhance-desktop-app.mjs";
import { greaterThan } from "./src/core/updateCheck.mjs";
import { existsSync } from "node:fs";
const nodeRequire = createRequire(import.meta.url),
manifest = nodeRequire("./package.json");
let __quiet, __debug;
const print = (...args) => __quiet || process.stdout.write(chalk(...args)),
printObject = (value) => __quiet || console.dir(value, { depth: null }),
clearLine = `\r\x1b[K`,
showCursor = `\x1b[?25h`,
hideCursor = `\x1b[?25l`,
cursorUp = (n) => `\x1b[${n}A`,
cursorForward = (n) => `\x1b[${n}C`;
let __confirmation;
const readStdin = () => {
return new Promise((res) => {
process.stdin.resume();
process.stdin.setEncoding("utf8");
process.stdin.once("data", (key) => {
process.stdin.pause();
res(key);
});
});
},
promptConfirmation = async (prompt) => {
let input;
const validInputs = ["Y", "y", "N", "n"],
promptLength = ` > ${prompt} [Y/n]: `.length;
// prevent line clear remove existing stdout
print`\n`;
do {
// clear line and repeat prompt until valid input is received
print`${cursorUp(1)}${clearLine} {inverse > ${prompt} [Y/n]:} `;
// autofill prompt response if --yes, --no or --quiet flags passed
if (validInputs.includes(__confirmation)) {
input = __confirmation;
print`${__confirmation}\n`;
} else input = (await readStdin()).trim();
if (!input) {
// default to Y if enter is pressed w/out input
input = "Y";
print`${cursorUp(1)}${cursorForward(promptLength)}Y\n`;
}
} while (!validInputs.includes(input));
// move cursor to immediately after input
print`${cursorUp(1)}${cursorForward(promptLength + 1)}`;
return input;
};
let __spinner;
const spinnerFrames = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"],
stopSpinner = () => {
if (!__spinner) return;
clearInterval(__spinner);
// show cursor and overwrite spinner with arrow on completion
print`\b{bold.yellow →}\n${showCursor}`;
__spinner = undefined;
},
startSpinner = () => {
// cleanup prev spinner if necessary
stopSpinner();
// hide cursor and print first frame
print`${hideCursor}{bold.yellow ${spinnerFrames[0]}}`;
let i = 0;
__spinner = setInterval(() => {
i++;
// overwrite spinner with next frame
print`\b{bold.yellow ${spinnerFrames[i % spinnerFrames.length]}}`;
}, 80);
};
const compileOptsToArgSpec = (options) => {
const args = {};
for (const [opt, [type]] of options) {
const aliases = opt.split(", ").map((alias) => alias.split("=")[0]),
param = aliases[1] ?? aliases[0];
args[param] = type;
for (let i = 0; i < aliases.length; i++) {
if (aliases[i] === param) continue;
args[aliases[i]] = param;
}
}
return args;
},
compileOptsToJsonOutput = (options) => {
// the structure used to define options above
// is convenient and compact, but requires additional
// parsing to understand. this function processes
// options into a more explicitly defined structure
return options.map(([opt, [type, description]]) => {
const option = {
aliases: opt.split(", ").map((alias) => alias.split("=")[0]),
type,
description,
},
example = opt
.split(", ")
.map((alias) => alias.split("=")[1])
.find((value) => value);
if (example) option.example = example;
return option;
});
};
let __json;
const printHelp = (commands, options) => {
const { name, version, homepage } = manifest,
usage = `${name} <command> [options]`;
if (__json) {
printObject({
name,
version,
homepage,
usage,
commands: Object.fromEntries(commands),
options: compileOptsToJsonOutput(options),
});
} else {
const cmdPad = Math.max(...commands.map(([cmd]) => cmd.length)),
optPad = Math.max(...options.map((opt) => opt[0].length)),
parseCmd = (cmd) =>
chalk` ${cmd[0].padEnd(cmdPad)} {grey :} ${cmd[1]}`,
parseOpt = (opt) =>
chalk` ${opt[0].padEnd(optPad)} {grey :} ${opt[1][1]}`;
print`{bold.whiteBright.underline ${name} v${version}}\n{grey ${homepage}}
\n{bold.whiteBright USAGE}\n${name} <command> [options]
\n{bold.whiteBright COMMANDS}\n${commands.map(parseCmd).join("\n")}
\n{bold.whiteBright OPTIONS}\n${options.map(parseOpt).join("\n")}\n`;
}
},
printVersion = () => {
if (__json) {
printObject({
[manifest.name]: manifest.version,
node: process.version.slice(1),
platform: process.platform,
architecture: process.arch,
os: os.release(),
});
} else {
const nodeVersion = `node@${process.version}`,
enhancerVersion = `${manifest.name}@v${manifest.version}`,
osVersion = `${process.platform}-${process.arch}/${os.release()}`;
print`${enhancerVersion} via ${nodeVersion} on ${osVersion}\n`;
}
};
try {
const commands = [
// ["command", "description"]
["apply", "Inject the notion-enhancer into Notion desktop."],
["remove", "Restore Notion desktop to its pre-enhanced state."],
["check", "Report Notion desktop's enhancement state."],
],
// prettier-ignore
options = [
// ["alias, option=example", [type, "description"]]
["--path=</path/to/notion/resources>", [String, "Manually provide a Notion installation location."]],
["--no-backup", [Boolean, "Skip backup; enhancement will be irreversible."]],
["--json", [Boolean, "Output JSON from the `check` and `--version` commands."]],
["-y, --yes", [Boolean, 'Skip prompts; assume "yes" and run non-interactively.']],
["-n, --no", [Boolean, 'Skip prompts; assume "no" and run non-interactively.']],
["-q, --quiet", [Boolean, 'Skip prompts; assume "no" unless -y and hide all output.']],
["-d, --debug", [Boolean, "Show detailed error messages and keep extracted files."]],
["-h, --help", [Boolean, "Display usage information for this CLI."]],
["-v, --version", [Boolean, "Display this CLI's version number."]],
];
const args = arg(compileOptsToArgSpec(options));
if (args["--debug"]) __debug = true;
if (args["--quiet"]) __quiet = true;
if (args["--json"]) __json = true;
if (args["--no"] || args["--quiet"]) __confirmation = "n";
if (args["--yes"]) __confirmation = "y";
if (args["--help"]) printHelp(commands, options), process.exit();
if (args["--version"]) printVersion(), process.exit();
if (args["--path"]) setNotionPath(args["--path"]);
const appPath = getResourcePath("app.asar"),
backupPath = getResourcePath("app.asar.bak"),
insertVersion = await getInsertVersion(),
updateAvailable = greaterThan(manifest.version, insertVersion);
const messages = {
"notion-found": insertVersion
? // prettier-ignore
`Notion desktop found with ${manifest.name} v${insertVersion
} applied${updateAvailable ? "" : " (up to date)"}.`
: `Notion desktop found (no enhancements applied).`,
"notion-not-found": `Notion desktop not found.`,
// prettier-ignore
"update-available": chalk`v${manifest.version
} is available! To apply, run {underline ${manifest.name} apply -y}.`,
// prettier-ignore
"update-confirm": `${updateAvailable ? "Upgrade" : "Downgrade"
} to ${manifest.name}${manifest.name} v${manifest.version}?`,
"backup-found": `Restoring to pre-enhanced state from backup...`,
"backup-not-found": chalk`No backup found: to restore Notion desktop to its pre-enhanced state,
uninstall it and reinstall Notion from {underline https://www.notion.so/desktop}.`,
"backup-app": `Backing up app before enhancement...`,
"enhance-app": `Enhancing and patching app sources...`,
};
const SUCCESS = chalk`{bold.whiteBright SUCCESS} {green ✔}`,
FAILURE = chalk`{bold.whiteBright FAILURE} {red ✘}`,
CANCELLED = chalk`{bold.whiteBright CANCELLED} {red ✘}`,
INCOMPLETE = Symbol();
const interactiveRestore = async () => {
if (!backupPath || !existsSync(backupPath)) {
print` {red * ${messages["backup-not-found"]}}\n`;
return FAILURE;
}
print` {grey * ${messages["backup-found"]}} `;
startSpinner();
await restoreApp();
stopSpinner();
return SUCCESS;
};
const getNotion = () => {
if (!appPath || !existsSync(appPath)) {
print` {red * ${messages["notion-not-found"]}}\n`;
return FAILURE;
} else {
print` {grey * ${messages["notion-found"]}}\n`;
return INCOMPLETE;
}
},
compareVersions = async () => {
if (!insertVersion) return INCOMPLETE;
// same version already applied
if (insertVersion === manifest.version) return SUCCESS;
// diff version already applied
print` {grey * ${messages["notion-found"]}}\n`;
const replace = await promptConfirmation(messages["update-confirm"]);
print`\n`;
return ["Y", "y"].includes(replace)
? (await interactiveRestore()) === SUCCESS
? INCOMPLETE
: FAILURE
: CANCELLED;
},
interactiveEnhance = async () => {
if (!args["--no-backup"]) {
print` {grey * ${messages["backup-app"]}} `;
startSpinner();
await backupApp();
stopSpinner();
}
print` {grey * ${messages["enhance-app"]}} `;
startSpinner();
await enhanceApp(__debug);
stopSpinner();
return SUCCESS;
};
switch (args["_"][0]) {
case "apply": {
print`{bold.whiteBright [${manifest.name.toUpperCase()}] APPLY}\n`;
let res = getNotion();
if (res === INCOMPLETE) res = await compareVersions();
if (res === INCOMPLETE) res = await interactiveEnhance();
print`${res}\n`;
break;
}
case "remove": {
print`{bold.whiteBright [${manifest.name.toUpperCase()}] REMOVE}\n`;
let res = getNotion();
if (res === INCOMPLETE) {
res = insertVersion ? await interactiveRestore() : SUCCESS;
}
print`${res}\n`;
break;
}
case "check": {
if (__json) {
const cliVersion = manifest.version,
state = { appPath, backupPath, insertVersion, cliVersion };
if (appPath && !existsSync(appPath)) state.appPath = null;
if (backupPath && !existsSync(backupPath)) state.backupPath = null;
printObject(state), process.exit();
}
print`{bold.whiteBright [${manifest.name.toUpperCase()}] CHECK}\n`;
let res = getNotion();
if (res === INCOMPLETE && updateAvailable) {
print` {grey * ${messages["update-available"]}}\n`;
}
break;
}
default:
printHelp(commands, options);
}
} catch (err) {
stopSpinner();
const message = err.message.split("\n")[0];
if (__debug) {
print`{bold.red ${err.name}:} ${message}\n{grey ${err.stack
.split("\n")
.splice(1)
.map((at) => at.replace(/\s{4}/g, " "))
.join("\n")}}`;
} else {
print`{bold.red Error:} ${message} {grey (Run with -d for more information.)}\n`;
}
}

View File

@ -1,58 +0,0 @@
# Notion Enhancer
# (c) 2020 dragonwocky <thedragonring.bod@gmail.com>
# (c) 2020 TarasokUA
# (https://dragonwocky.me/) under the MIT license
import os
import sys
import platform
import subprocess
from shutil import rmtree
from time import sleep
# f'{bold}=== title ==={normal}' = headers
# '*' = information
# '...' = actions
# '###' = warnings
# '>' = exit
bold = '\033[1m'
normal = '\033[0m'
print(f'{bold}=== NOTION ENHANCER CLEANING LOG ==={normal}\n')
try:
filepath = ''
if 'microsoft' in platform.uname()[3].lower() and sys.platform == 'linux':
filepath = '/mnt/c/' + \
subprocess.run(
['cmd.exe', '/c', 'echo', '%localappdata%'], stdout=subprocess.PIPE).stdout \
.rstrip().decode('utf-8')[3:].replace('\\', '/') + '/Programs/Notion/resources'
elif sys.platform == 'win32':
filepath = subprocess.run(['echo', '%localappdata%'], shell=True, capture_output=True).stdout \
.rstrip().decode('utf-8').replace('\\', '/') + '/Programs/Notion/resources'
else:
print(' > script not compatible with your os!\n (report this to dragonwocky#8449 on discord)')
exit()
if os.path.exists(filepath + '/app'):
print(
f' ...removing folder {filepath}/app/')
rmtree(filepath + '/app')
else:
print(
f' * {filepath}/app/ was not found: step skipped.')
if os.path.isfile(filepath + '/app.asar.bak'):
print(' ...renaming asar.app.bak to asar.app')
os.rename(filepath + '/app.asar.bak', filepath + '/app.asar')
else:
print(
f' * {filepath}/app.asar.bak was not found: step skipped.')
print(f'\n{bold}>>> SUCCESSFULLY CLEANED <<<{normal}')
except Exception as e:
print(f'\n{bold}### ERROR ###{normal}\n{str(e)}')
print(f'\n{bold}=== END OF LOG ==={normal}')

View File

@ -1,175 +0,0 @@
# Notion Enhancer
# (c) 2020 dragonwocky <thedragonring.bod@gmail.com>
# (c) 2020 TarasokUA
# (https://dragonwocky.me/) under the MIT license
import re
import os
import sys
import platform
import subprocess
from shutil import copyfile
from time import sleep
# for toggling notion visibility
hotkey = 'CmdOrCtrl+Shift+A'
# f'{bold}=== title ==={normal}' = headers
# '*' = information
# '...' = actions
# '##' = warnings
# '>' = exit
bold = '\033[1m'
normal = '\033[0m'
print(f'{bold}=== NOTION ENHANCER CUSTOMISATION LOG ==={normal}\n')
try:
filepath = ''
__folder__ = os.path.dirname(os.path.realpath(__file__)).replace('\\', '/')
if 'microsoft' in platform.uname()[3].lower() and sys.platform == 'linux':
filepath = '/mnt/c/' + \
subprocess.run(
['cmd.exe', '/c', 'echo', '%localappdata%'], stdout=subprocess.PIPE).stdout \
.rstrip().decode('utf-8')[3:].replace('\\', '/') + '/Programs/Notion/resources'
drive = __folder__[5].capitalize() if __folder__.startswith(
'/mnt/') else 'C'
__folder__ = drive + ':/' + __folder__[6:]
elif sys.platform == 'win32':
filepath = subprocess.run(['echo', '%localappdata%'], shell=True, capture_output=True).stdout \
.rstrip().decode('utf-8').replace('\\', '/') + '/Programs/Notion/resources'
else:
print(' > script not compatible with your os!\n (report this to dragonwocky#8449 on discord)')
exit()
if os.path.isfile(filepath + '/app.asar'):
print(' ...unpacking app.asar')
subprocess.run(['asar', 'extract', filepath +
'/app.asar', filepath + '/app'], shell=(True if sys.platform == 'win32' else False))
print(' ...renaming asar.app to asar.app.bak')
os.rename(filepath + '/app.asar', filepath + '/app.asar.bak')
else:
print(f' ## file {filepath}/app.asar not found!')
print(' * attempting to locate')
if os.path.exists(filepath + '/app'):
print(' * app.asar was already unpacked: step skipped.')
else:
print(' > nothing found: exiting.')
exit()
if os.path.isfile(filepath + '/app/renderer/preload.js'):
print(f' ...adding preload.js to {filepath}/app/renderer/preload.js')
with open(filepath + '/app/renderer/preload.js') as content:
if '/* === INJECTION MARKER === */' in content.read():
print(' * preload.js already added. replacing it.')
content.seek(0)
original = []
for num, line in enumerate(content):
if '/* === INJECTION MARKER === */' in line:
break
original += line
with open(filepath + '/app/renderer/preload.js', 'w') as write:
write.writelines(original)
else:
with open(filepath + '/app/renderer/preload.js', 'a') as append:
append.write('\n\n')
with open(filepath + '/app/renderer/preload.js', 'a') as append:
print(' ...linking to ./resources/user.css')
with open('./resources/preload.js') as insert:
append.write(insert.read().replace(
'___user.css___', __folder__
+ '/resources/user.css'))
else:
print(
f' * {filepath}/app/renderer/preload.js was not found: step skipped.')
if os.path.isfile(filepath + '/app/main/createWindow.js'):
with open(filepath + '/app/main/createWindow.js') as content:
content = content.read()
print(
f' ...making window frameless @ {filepath}/app/main/createWindow.js')
if '{ frame: false, show: false' not in content:
content = content.replace(
'{ show: false', '{ frame: false, show: false')
print(
f' ...adding "open hidden" capabilities to {filepath}/app/main/createWindow.js')
content = re.sub('\\s*\\/\\* === INJECTION START === \\*\\/.*?\\/\\* === INJECTION END === \\*\\/\\s*',
'window.show()', content, flags=re.DOTALL).replace('window.show()', """
/* === INJECTION START === */
const path = require('path'),
store = new (require(path.join(__dirname, '..', 'store.js')))({
config: 'user-preferences',
defaults: {
openhidden: false,
maximised: false
}
});
if (!store.get('openhidden') || electron_1.BrowserWindow.getAllWindows().some(win => win.isVisible()))
{ window.show(); if (store.get('maximised')) window.maximize(); }
/* === INJECTION END === */
""")
with open(filepath + '/app/main/createWindow.js', 'w') as write:
write.write(content)
else:
print(
f' * {filepath}/app/main/createWindow.js was not found: step skipped.')
if os.path.isfile(filepath + '/app/renderer/index.js'):
with open(filepath + '/app/renderer/index.js') as content:
print(
f' ...adjusting drag area for frameless window in {filepath}/app/renderer/index.js')
content = content.read()
top = content.rfind('top')
content = content[:top] + content[top:].replace(
'right: 0', 'right: 420').replace(
'top: 0', 'top: 1 ').replace(
'height: 34', 'height: 16')
with open(filepath + '/app/renderer/index.js', 'w') as write:
write.write(content)
else:
print(
f' * {filepath}/app/renderer/index.js was not found: step skipped.')
if os.path.isfile(filepath + '/app/main/main.js'):
with open(filepath + '/app/main/main.js') as content:
print(
f' ...adding tray support (inc. context menu with settings) to {filepath}/app/main/main.js')
print(
f' ...adding window toggle hotkey to {filepath}/app/main/main.js')
content = content.read()
with open(filepath + '/app/main/main.js', 'w') as write:
if '/* === INJECTION MARKER === */' in content:
print(' * hotkey.js already added. replacing it.')
original = []
for line in content.splitlines():
if '/* === INJECTION MARKER === */' in line:
break
original.append(line)
write.write('\n'.join(original))
else:
write.write(content.replace(
'electron_1.app.on("ready", handleReady);',
'electron_1.app.on("ready", () => handleReady() && enhancements());') + '\n')
with open(filepath + '/app/main/main.js', 'a') as append:
with open('./resources/hotkey.js') as insert:
append.write('\n' + insert.read().replace(
'___hotkey___', hotkey))
print(
f' ...copying tray icon ./resources/notion.ico to {filepath}/app/main/')
copyfile('./resources/notion.ico',
filepath + '/app/main/notion.ico')
print(
f' ...copying datastore wrapper ./resources/store.js to {filepath}/app/')
copyfile('./resources/store.js', filepath + '/app/store.js')
else:
print(
f' * {filepath}/app/main/main.js was not found: step skipped.')
print(f'\n{bold}>>> SUCCESSFULLY CUSTOMISED <<<{normal}')
except Exception as e:
print(f'\n{bold}### ERROR ###{normal}\n{str(e)}')
print(f'\n{bold}=== END OF LOG ==={normal}')

View File

@ -1,26 +0,0 @@
{
"title": "notion enhancer",
"primary": "rgb(75, 133, 209)",
"git": "https://github.com/dragonwocky/notion-enhancer/blob/master/",
"footer": "[Edit on GitHub](__git__) // © 2020 dragonwocky & Uzver, under the [MIT license](https://choosealicense.com/licenses/mit/).",
"card": {
"description": "an enhancer/customiser for the all-in-one productivity workspace notion.so",
"url": "https://dragonwocky.me/notion-enhancer/"
},
"icon": {
"light": "web-logo.png"
},
"overwrite": true,
"exclude": ["cleaner.py", "customiser.py", "resources/*", ".gitignore"],
"nav": [
["index.html", "README.md"],
"resources",
["changelog.html", "CHANGELOG.md"],
[
"license",
"https://github.com/dragonwocky/notion-enhancer/blob/master/LICENSE"
],
["github", "https://github.com/dragonwocky/notion-enhancer/"],
["me (dragonwocky)", "https://dragonwocky.me/"]
]
}

View File

@ -1,22 +0,0 @@
MIT License
Copyright (c) 2020 TarasokUA
Copyright (c) 2020 dragonwocky
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -1,93 +0,0 @@
<!DOCTYPE html><!-- Documentative--><!-- (c) 2020 dragonwocky <thedragonring.bod@gmail.com>--><!-- (https://dragonwocky.me/) under the MIT license--><html prefix="og: http://ogp.me/ns#"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>changelog | notion enhancer</title><link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Source+Code+Pro|Nunito+Sans"><link rel="stylesheet" href="docs.css"><script src="docs.js"></script><link rel="icon" href="web-logo.png" media="(prefers-color-scheme: dark)"><link rel="icon" href="web-logo.png"><meta name="title" content="changelog | notion enhancer"><meta name="description" content="an enhancer/customiser for the all-in-one productivity workspace notion.so"><meta name="theme-color" content="rgb(75, 133, 209)"><meta property="og:type" content="article"><meta property="og:url" content="https://dragonwocky.me/notion-enhancer/changelog.html"><meta property="og:title" content="changelog"><meta property="og:site_name" content="notion enhancer"><meta property="og:description" content="an enhancer/customiser for the all-in-one productivity workspace notion.so"><meta property="og:image" content="https://dragonwocky.me/notion-enhancer/web-logo.png"><meta property="twitter:card" content="summary"></head><body><aside class="menu"><div><div class="title"><h1>notion enhancer</h1><picture class="icon"><source srcset="web-logo.png" media="(prefers-color-scheme: dark)"><img src="web-logo.png"></picture></div></div><ul class="nav"><li class="entry"><a href="index.html">notion enhancer</a></li><li class="entry"><p>resources</p></li><li class="entry"><a href="#changelog">changelog</a><ul><li class="level-3"><a href="#v050-2020-05-23">v0.5.0 (2020-05-23)</a></li><li class="level-3"><a href="#v041-2020-02-13">v0.4.1 (2020-02-13)</a></li><li class="level-3"><a href="#v040">v0.4.0</a></li><li class="level-3"><a href="#v030">v0.3.0</a></li><li class="level-3"><a href="#v020">v0.2.0</a></li><li class="level-3"><a href="#v010">v0.1.0</a></li></ul></li><li class="entry"><a href="https://github.com/dragonwocky/notion-enhancer/blob/master/LICENSE">license</a></li><li class="entry"><a href="https://github.com/dragonwocky/notion-enhancer/">github</a></li><li class="entry"><a href="https://dragonwocky.me/">me (dragonwocky)</a></li></ul><p class="mark"><a href="https://dragonwocky.me/documentative">docs by documentative</a></p></aside><div class="wrapper"><div class="toggle"><button></button><h1>notion enhancer</h1></div><article class="documentative"><div class="content">
<section class="block" id="changelog">
<h1>
<a href="#changelog">changelog</a>
</h1>
<p>if something is <del>crossed out</del>, then it is no longer a feature included by default,
but can still easily be enabled by following instructions in the <a href="/index.html">docs</a>.</p>
</section>
<section class="block" id="v050-2020-05-23">
<h3>
<a href="#v050-2020-05-23">v0.5.0 (2020-05-23)</a>
</h3>
<ul>
<li>new: running from the wsl.</li>
<li>new: reload window with f5.</li>
<li>improved: code has been refactored and cleaned up,
inc. file renaming and a <code>customiser.py</code> that doesn&#39;t require
a run of <code>cleaner.py</code> to build updates.
improved: scrollbar colours that fit better with notion&#39;s theming.</li>
<li>bugfix: un-break having multiple notion windows open.</li>
</ul>
<p><em>(forked by <a href="https://github.com/dragonwocky">@dragonwocky</a>.)</em></p>
</section>
<section class="block" id="v041-2020-02-13">
<h3>
<a href="#v041-2020-02-13">v0.4.1 (2020-02-13)</a>
</h3>
<ul>
<li>bugfix: wider table &amp; the &quot;+&quot; button not working in database pages.</li>
</ul>
<blockquote>
<p>📥 <a href="https://s3-us-west-2.amazonaws.com/secure.notion-static.com/d239a3cf-d553-4ef3-ab04-8b47892d9f9a/Notion_Customization_v4.1.zip">notion-enhancer.v4.1.zip</a></p>
</blockquote>
</section>
<section class="block" id="v040">
<h3>
<a href="#v040">v0.4.0</a>
</h3>
<ul>
<li>new: tray icon.</li>
<li>new: app startup options (+ saving).</li>
<li>new: <code>Reset.py</code></li>
<li>improved: better output from <code>Customization Patcher.py</code>.</li>
<li>bugfix: wider tables in &quot;short page&quot; mode.</li>
<li>bugfix: unclickable buttons/draggable area (of titlebar).</li>
</ul>
</section>
<section class="block" id="v030">
<h3>
<a href="#v030">v0.3.0</a>
</h3>
<ul>
<li>new: show/hide window hotkey.</li>
<li>new: app startup options.</li>
<li><del>style: smaller table icons.</del></li>
</ul>
<blockquote>
<p>📥 <a href="https://s3-us-west-2.amazonaws.com/secure.notion-static.com/b01aa446-5727-476a-a25e-395472bfb1be/NotionScriptsV3.zip">notion-enhancer.v3.zip</a></p>
</blockquote>
</section>
<section class="block" id="v020">
<h3>
<a href="#v020">v0.2.0</a>
</h3>
<ul>
<li>new: light/dark theme support for window control buttons + scrollbars.</li>
<li>new: custom styles directly linked to the enhancer resources + compatible with web version.</li>
<li><del>improved: making table column width go below 100px.</del></li>
</ul>
</section>
<section class="block" id="v010">
<h3>
<a href="#v010">v0.1.0</a>
</h3>
<ul>
<li>new: custom window control buttons.</li>
<li>removed: default titlebar/menubar.</li>
<li><del>removed: huge padding of board view.</del></li>
<li><del>removed: huge padding of table view.</del></li>
<li><del>optional: making table column width go below 100px.</del></li>
<li><del>style: thinner cover image + higher content block.</del></li>
<li>style: scrollbars.</li>
</ul>
</section></div><footer class="footer"><hr><p><a href="https://github.com/dragonwocky/notion-enhancer/blob/master/CHANGELOG.md">Edit on GitHub</a> // © 2020 dragonwocky &amp; Uzver, under the <a href="https://choosealicense.com/licenses/mit/">MIT license</a>.</p>
</footer><nav><a class="prev" href="index.html"></a></nav></article></div></body></html>

View File

@ -1,498 +0,0 @@
/*
* Documentative Styling
* (c) 2020 dragonwocky <thedragonring.bod@gmail.com>
* (https://dragonwocky.me/) under the MIT license
*/
:root {
--primary: #4b85d1;
--absolute: #000;
--contrast: #fff;
--text: rgba(0, 0, 0, 0.84);
--link: var(--primary);
--grey: #6f6f6f;
--bg: #fbfcfc;
--box: #f2f3f4;
--code: #f7f9f9;
--button: #eee;
--border: #e5e7e9;
--shadow: #eee;
--glow: transparent;
--scroll: #e9e9e9;
--hover: #dedede;
--code-lang: #555;
--hljs-html: #000080;
--hljs-attr: #008080;
--hljs-obj: #2c426b;
--hljs-string: #d14;
--hljs-builtin: #0086b3;
--hljs-keyword: rgba(0, 0, 0, 0.84);
--hljs-selector: #900;
--hljs-type: #458;
--hljs-regex: #009926;
--hljs-symbol: #990073;
--hljs-meta: #999;
--hljs-comment: #707070;
--hljs-deletion: #e8b9b8;
--hljs-deletion-text: #4c232d;
--hljs-addition: #b9e0d3;
--hljs-addition-text: #1e4839;
}
@media (prefers-color-scheme: dark) {
:root {
--absolute: #fff;
--contrast: #000;
--text: #ddd;
--link: #a6c3e8;
--grey: #52555c;
--bg: #0e0f0f;
--box: #050505;
--code: #000;
--button: #2d2d2d;
--border: #2d2e2f;
--shadow: #070707;
--glow: var(--primary);
--scroll: #202225;
--hover: #36393f;
--code-lang: #ccc;
--hljs-html: #46db8c;
--hljs-attr: #dd1111;
--hljs-obj: #c6cbda;
--hljs-string: #abcdef;
--hljs-builtin: #b8528d;
/* bd1a79, 926956 */
--hljs-keyword: #2d8b59;
--hljs-comment: #a0a0a0;
--hljs-deletion: #4c232d;
--hljs-deletion-text: #e8b9b8;
--hljs-addition: #1e4839;
--hljs-addition-text: #b9e0d3;
}
}
* {
box-sizing: border-box;
word-break: break-word;
text-decoration: none;
text-size-adjust: 100%;
}
html,
body {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
}
body {
color: var(--text);
background-color: var(--bg);
font-family: 'Nunito Sans', sans-serif;
}
::-webkit-scrollbar {
width: 2px;
height: 2px;
}
::-webkit-scrollbar-corner,
::-webkit-scrollbar-track {
background-color: transparent;
}
::-webkit-scrollbar-thumb {
background-color: var(--scroll);
border-radius: 5px;
}
::-webkit-scrollbar-thumb:hover {
background: var(--hover);
}
aside {
display: flex;
flex-direction: column;
background-color: var(--box);
overflow-x: auto;
}
aside .title {
display: flex;
flex-direction: row;
}
aside .title h1 {
font: 1.8em 'Source Code Pro', monospace;
margin: 0 0 1em 1.5rem;
padding: 1em 8px 2.5px 0;
letter-spacing: -2px;
border-bottom: 5px solid var(--primary);
color: var(--absolute);
}
aside .title .icon {
margin: auto 0.5em;
}
aside .title .icon img {
width: 2.5em;
margin: auto 0.5em;
}
aside > ul:first-child > li:first-child {
padding-top: 1em;
}
aside ul {
list-style-type: none;
padding-inline-start: 0;
margin: 0;
}
aside ul li p {
font-weight: bold;
letter-spacing: -0.5px;
margin-bottom: 0;
padding: 2px 1.3em;
font-size: 1.1em;
color: var(--hljs-comment);
}
aside ul li a {
color: var(--text);
padding-bottom: 0.1em 5em;
display: block;
padding: 2px 1.5em;
}
aside ul li a:hover,
aside ul li a:active {
background: var(--scroll);
}
aside ul li a.active {
color: var(--link);
font-weight: bold;
text-shadow: 0 0 0.75em var(--glow);
}
aside ul li.entry > a {
text-decoration: underline var(--border);
}
aside ul li.level-1 > a {
padding-left: 1.75em;
}
aside ul li.level-2 > a {
padding-left: calc(1.5em + calc(0.75em * 1));
}
aside ul li.level-3 > a {
padding-left: calc(1.5em + calc(0.75em * 2));
}
aside ul li.level-4 > a {
padding-left: calc(1.5em + calc(0.75em * 3));
}
aside ul li.level-5 > a {
padding-left: calc(1.5em + calc(0.75em * 4));
}
aside ul li.level-6 > a {
padding-left: calc(1.5em + calc(0.75em * 5));
}
aside .mark {
text-align: right;
margin-top: auto;
padding: 1.5em 1.5em 2px 1.5em;
font-size: 0.8em;
}
aside .mark a {
color: var(--grey);
}
.wrapper {
height: 100%;
width: 100%;
overflow-y: hidden;
}
.wrapper .documentative {
height: 100%;
overflow-y: auto;
padding: 0 1.5em;
padding-bottom: 4em;
display: flex;
flex-direction: column;
}
.wrapper .documentative .block {
margin: 1.5em;
word-wrap: break-word;
}
.wrapper .documentative .block:first-child {
margin: 0 1.5em 1.5em 1.5em;
}
.wrapper .documentative .example {
margin-top: 1em;
padding: 1em;
background-color: var(--box);
box-shadow: 0.4em 0.4em 1em var(--shadow);
}
.wrapper .documentative .example p:first-child {
margin-top: 0;
}
.wrapper .documentative .example p:last-child {
margin-bottom: 0;
}
.wrapper .documentative nav {
width: 75%;
position: fixed;
bottom: 1em;
right: 0;
pointer-events: none;
}
.wrapper .documentative nav .prev {
float: left;
padding-right: 0.13em;
}
.wrapper .documentative nav .next {
float: right;
padding-left: 0.13em;
}
.wrapper .documentative nav .prev,
.wrapper .documentative nav .next {
opacity: 1;
transition: opacity 200ms ease;
pointer-events: all;
border-radius: 50%;
width: 1.75em;
height: 1.75em;
margin: 0 1em;
font: 1.5em 'Source Code Pro', monospace;
line-height: 1.75em;
text-align: center;
color: var(--text);
text-shadow: none !important;
background-color: var(--button);
}
.wrapper .documentative .footer {
text-align: right;
color: var(--grey);
margin: auto 1.5em 0;
}
.wrapper .documentative .footer hr {
border-color: var(--grey);
}
.wrapper .documentative .footer a {
color: var(--grey);
font-weight: bold;
text-shadow: none;
text-decoration: dotted underline;
}
.wrapper .documentative h1,
.wrapper .documentative h2,
.wrapper .documentative h3,
.wrapper .documentative h4,
.wrapper .documentative h5,
.wrapper .documentative h6 {
margin: 0;
padding-top: 1em;
}
.wrapper .documentative h1 a,
.wrapper .documentative h2 a,
.wrapper .documentative h3 a,
.wrapper .documentative h4 a,
.wrapper .documentative h5 a,
.wrapper .documentative h6 a {
color: var(--text);
text-shadow: none;
}
.wrapper .documentative h1 {
padding-top: 1.5em;
}
.wrapper .documentative a {
color: var(--link);
text-shadow: 0 0 0.75em var(--glow);
}
.wrapper .documentative blockquote {
margin-left: 0;
padding-left: 1em;
border-left: 0.25em solid var(--border);
}
.wrapper .documentative h1 + table,
.wrapper .documentative h2 + table,
.wrapper .documentative h3 + table,
.wrapper .documentative h4 + table,
.wrapper .documentative h5 + table,
.wrapper .documentative h6 + table {
margin-top: 1em;
}
.wrapper .documentative table {
width: 100%;
border-collapse: collapse;
}
.wrapper .documentative table,
.wrapper .documentative th,
.wrapper .documentative td {
padding: 0.2em 0.7em;
border: 1px solid var(--border);
}
.wrapper .documentative code {
font-size: 0.8em;
background-color: var(--code);
overflow-x: auto;
position: relative;
display: block;
font-family: 'Source Code Pro', monospace;
}
.wrapper .documentative *:not(pre) > code {
padding: 0.275em 0.35em;
border-radius: 2px;
display: inline;
}
.wrapper .documentative pre {
position: relative;
}
.wrapper .documentative pre code {
padding: 1.8em;
border-radius: 5px;
position: static;
}
.wrapper .documentative pre code::before {
position: absolute;
right: 0;
top: 0;
color: var(--code-lang);
font-size: 0.65em;
padding: 0.5em 0.8em;
}
@media (min-width: 769px) {
body {
display: grid;
grid-template-columns: 25% 75%;
}
aside::-webkit-scrollbar-corner,
aside::-webkit-scrollbar-track {
background-color: var(--bg);
}
.toggle {
display: none;
}
}
@media (max-width: 768px) {
aside {
z-index: 1;
height: 100%;
display: flex;
position: fixed;
top: 0;
left: calc(4.5em - 100%);
width: calc(100% - 4.5em);
transition: left 300ms ease;
}
.wrapper {
display: flex;
flex-direction: column;
position: fixed;
top: 0;
left: 0;
transition: left 300ms ease;
}
.wrapper .documentative {
flex-shrink: 1;
}
.wrapper .documentative nav {
width: 100%;
}
.wrapper .toggle {
display: flex;
flex-direction: row;
flex-shrink: 0;
padding: 0.8em 0;
background-color: var(--box);
}
.wrapper .toggle h1 {
letter-spacing: -2px;
font-size: 1.8em;
padding-top: 1.5px;
margin: auto 1.5rem auto 0;
}
.wrapper .toggle button {
font-size: 1.8em;
width: 2.5em;
margin: auto 0.5em;
color: var(--absolute);
border: none;
background: none;
text-align: center;
transition: transform 150ms ease;
-webkit-appearance: none;
-moz-appearance: none;
}
.wrapper .toggle button:hover,
.wrapper .toggle button:focus {
color: var(--text);
}
.wrapper .toggle button:active {
transform: scale(0.95);
}
.mobilemenu aside {
left: 0;
}
.mobilemenu .wrapper {
left: calc(100% - 4.75em);
}
.mobilemenu .wrapper .prev,
.mobilemenu .wrapper .next {
opacity: 0 !important;
pointer-events: none !important;
}
}
.hljs-subst {
color: var(--text);
}
.hljs-comment,
.hljs-quote {
color: var(--hljs-comment);
font-style: italic;
}
.hljs-keyword,
.hljs-selector-tag {
color: var(--hljs-keyword);
font-weight: bold;
}
.hljs-attr {
color: var(--hljs-obj);
}
.hljs-number,
.hljs-literal,
.hljs-variable,
.hljs-template-variable,
.hljs-tag .hljs-attr {
color: var(--hljs-attr);
}
.hljs-string,
.hljs-doctag {
color: var(--hljs-string);
}
.hljs-name,
.hljs-attribute {
color: var(--hljs-html);
}
.hljs-built_in,
.hljs-builtin-name {
color: var(--hljs-builtin);
}
.hljs-title,
.hljs-section,
.hljs-selector-id {
color: var(--hljs-selector);
font-weight: bold;
}
.hljs-type,
.hljs-class .hljs-title {
color: var(--hljs-type);
font-weight: bold;
}
.hljs-regexp,
.hljs-link {
color: var(--hljs-regex);
}
.hljs-symbol,
.hljs-bullet {
color: var(--hljs-symbol);
}
.hljs-meta {
color: var(--hljs-meta);
font-weight: bold;
}
.hljs-deletion {
background: var(--hljs-deletion);
color: var(--hljs-deletion-text);
}
.hljs-addition {
background: var(--hljs-addition);
color: var(--hljs-addition-text);
}
.hljs-emphasis {
font-style: italic;
}
.hljs-strong {
font-weight: bold;
}
.documentative pre .lang-css::before { content: 'CSS'; }

View File

@ -1,193 +0,0 @@
/*
* Documentative
* (c) 2020 dragonwocky <thedragonring.bod@gmail.com>
* (https://dragonwocky.me/) under the MIT license
*/
class Scrollnav {
constructor(menu, content, options) {
if (!(menu instanceof HTMLElement))
throw Error('scrollnav: invalid <menu> element provided');
if (!(content instanceof HTMLElement))
throw Error('scrollnav: invalid <content> element provided');
if (typeof options !== 'object') options = {};
if (Scrollnav.prototype.INITIATED)
throw Error('scrollnav: only 1 instance per page allowed!');
Scrollnav.prototype.INITIATED = true;
this.ID;
this.ticking = [];
this._menu = menu;
this._content = content;
this._links = [];
this._sections = [...this._menu.querySelectorAll('ul li a')].reduce(
(list, link) => {
if (!link.getAttribute('href').startsWith('#')) return list;
let section = this._content.querySelector(link.getAttribute('href'));
if (!section) return list;
this._links.push(link);
link.onclick = async ev => {
ev.preventDefault();
const ID = link.getAttribute('href');
this.highlightHeading(ID);
this.scrollContent(ID);
this.setHash(ID);
};
return [...list, section];
},
[]
);
this._topheading = `#${this._sections[0].id}`;
window.onhashchange = this.watchHash.bind(this);
this._content.addEventListener('scroll', ev => {
if (!this.ticking.length) {
this.ticking.push(1);
requestAnimationFrame(() => {
this.watchScroll(ev);
this.ticking.pop();
});
}
});
this.set(null, false);
return this;
}
set(ID, smooth) {
this.highlightHeading(ID);
this.scrollMenu(ID, smooth);
this.scrollContent(ID, smooth);
this.setHash(ID);
}
parseID(ID) {
if (!ID || typeof ID !== 'string') ID = location.hash || this._topheading;
if (!ID.startsWith('#')) ID = `#${ID}`;
if (!this._links.find(el => el.getAttribute('href') === ID))
ID = this._topheading;
this.ID = ID;
return ID;
}
highlightHeading(ID) {
this.parseID(ID);
this._links.forEach(el =>
el.getAttribute('href') === this.ID
? el.classList.add('active')
: el.classList.remove('active')
);
return true;
}
watchHash(ev) {
ev.preventDefault();
if (ev.newURL !== ev.oldURL) {
this.set();
}
}
setHash(ID) {
if (!history.replaceState) return false;
this.parseID(ID);
history.replaceState(null, null, ID === this._topheading ? '#' : this.ID);
return true;
}
scrollContent(ID, smooth = true) {
this.ticking.push(1);
this.parseID(ID);
let offset = this._sections.find(el => `#${el.id}` === this.ID).offsetTop;
if (offset < this._content.clientHeight / 2) offset = 0;
this._content.scroll({
top: offset,
behavior: smooth ? 'smooth' : 'auto'
});
setTimeout(() => this.ticking.pop(), 1000);
return true;
}
scrollMenu(ID, smooth = true) {
this.parseID(ID);
let offset = this._links.find(el => el.getAttribute('href') === this.ID)
.offsetTop;
if (offset < this._menu.clientHeight / 2) offset = 0;
this._menu.scroll({
top: offset,
behavior: smooth ? 'smooth' : 'auto'
});
return true;
}
watchScroll(ev) {
const viewport = this._content.clientHeight,
ID = this._sections.reduce(
(carry, el) => {
const rect = el.getBoundingClientRect(),
height = rect.bottom - rect.top,
visible = {
top: rect.top >= 0 && rect.top < viewport,
bottom: rect.bottom > 0 && rect.top < viewport
};
let pixels = 0;
if (visible.top && visible.bottom) {
pixels = height; // whole el
} else if (visible.top) {
pixels = viewport - rect.top;
} else if (visible.bottom) {
pixels = rect.bottom;
} else if (height > viewport && rect.top < 0) {
const absolute = Math.abs(rect.top);
if (absolute < height) pixels = height - absolute; // part of el
}
pixels = (pixels / height) * 100;
return pixels > carry[0] ? [pixels, el] : carry;
},
[0, null]
)[1].id;
this.ID = ID;
this.scrollMenu(this.ID);
clearTimeout(this.afterScroll);
this.afterScroll = setTimeout(
() => void (this.highlightHeading(this.ID) && this.setHash(this.ID)),
100
);
}
}
let constructed = false;
const construct = () => {
if (document.readyState !== 'complete' || constructed) return false;
constructed = true;
if (
location.pathname.endsWith('index.html') &&
window.location.protocol === 'https:'
)
location.replace('./' + location.hash);
new Scrollnav(
document.querySelector('aside'),
document.querySelector('.documentative')
);
document.querySelector('.toggle button').onclick = () =>
document.body.classList.toggle('mobilemenu');
if (window.matchMedia) {
let prev;
const links = [...document.head.querySelectorAll('link[rel*="icon"]')],
pointer = document.createElement('link');
pointer.setAttribute('rel', 'icon');
document.head.appendChild(pointer);
setInterval(() => {
const match = links.find(link => window.matchMedia(link.media).matches);
if (!match || match.media === prev) return;
prev = match.media;
pointer.setAttribute('href', match.getAttribute('href'));
}, 500);
links.forEach(link => document.head.removeChild(link));
}
};
construct();
document.addEventListener('readystatechange', construct);

View File

@ -1,282 +0,0 @@
<!DOCTYPE html><!-- Documentative--><!-- (c) 2020 dragonwocky <thedragonring.bod@gmail.com>--><!-- (https://dragonwocky.me/) under the MIT license--><html prefix="og: http://ogp.me/ns#"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>notion enhancer</title><link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Source+Code+Pro|Nunito+Sans"><link rel="stylesheet" href="docs.css"><script src="docs.js"></script><link rel="icon" href="web-logo.png" media="(prefers-color-scheme: dark)"><link rel="icon" href="web-logo.png"><meta name="title" content="notion enhancer"><meta name="description" content="an enhancer/customiser for the all-in-one productivity workspace notion.so"><meta name="theme-color" content="rgb(75, 133, 209)"><meta property="og:type" content="article"><meta property="og:url" content="https://dragonwocky.me/notion-enhancer/index.html"><meta property="og:title" content="notion enhancer"><meta property="og:site_name" content="notion enhancer"><meta property="og:description" content="an enhancer/customiser for the all-in-one productivity workspace notion.so"><meta property="og:image" content="https://dragonwocky.me/notion-enhancer/web-logo.png"><meta property="twitter:card" content="summary"></head><body><aside class="menu"><div><div class="title"><h1>notion enhancer</h1><picture class="icon"><source srcset="web-logo.png" media="(prefers-color-scheme: dark)"><img src="web-logo.png"></picture></div></div><ul class="nav"><li class="entry"><a href="#notion-enhancer">notion enhancer</a><ul><li class="level-2"><a href="#installation">installation</a></li><li class="level-2"><a href="#this-is-a-fork">this is a fork</a></li><li class="level-2"><a href="#features">features</a></li><li class="level-3"><a href="#titlebar">titlebar</a></li><li class="level-3"><a href="#nicer-scrollbars">nicer scrollbars</a></li><li class="level-3"><a href="#hotkeys">hotkeys</a></li><li class="level-3"><a href="#tray">tray</a></li><li class="level-3"><a href="#styling">styling</a></li><li class="level-4"><a href="#wider-page-view">wider page view</a></li><li class="level-4"><a href="#thinner-cover-image">thinner cover image</a></li><li class="level-4"><a href="#table-columns-below-100px">table columns below 100px</a></li><li class="level-4"><a href="#hide--new-table-row">hide '+ new' table row</a></li><li class="level-4"><a href="#hide-calculations-table-row">hide calculations table row</a></li><li class="level-4"><a href="#hide--new-board-row">hide '+ new' board row</a></li><li class="level-4"><a href="#hide-board-view-hidden-columns">hide board view hidden columns</a></li><li class="level-4"><a href="#hide-board-view-add-a-group">hide board view 'add a group'</a></li><li class="level-4"><a href="#centre-align-table-column-headers">centre-align table column headers</a></li><li class="level-4"><a href="#smaller-table-column-header-icons">smaller table column header icons</a></li><li class="level-4"><a href="#remove-icons-from-table-column-headers">remove icons from table column headers</a></li><li class="level-4"><a href="#removingdecreasing-side-padding-for-tables">removing/decreasing side padding for tables</a></li><li class="level-4"><a href="#removingdecreasing-side-padding-for-boards">removing/decreasing side padding for boards</a></li><li class="level-2"><a href="#other-details">other details</a></li></ul></li><li class="entry"><p>resources</p></li><li class="entry"><a href="changelog.html">changelog</a></li><li class="entry"><a href="https://github.com/dragonwocky/notion-enhancer/blob/master/LICENSE">license</a></li><li class="entry"><a href="https://github.com/dragonwocky/notion-enhancer/">github</a></li><li class="entry"><a href="https://dragonwocky.me/">me (dragonwocky)</a></li></ul><p class="mark"><a href="https://dragonwocky.me/documentative">docs by documentative</a></p></aside><div class="wrapper"><div class="toggle"><button></button><h1>notion enhancer</h1></div><article class="documentative"><div class="content">
<section class="block" id="notion-enhancer">
<h1>
<a href="#notion-enhancer">notion enhancer</a>
</h1>
<p>an enhancer/customiser for the all-in-one productivity workspace <a href="https://www.notion.so/">notion.so</a></p>
</section>
<section class="block" id="installation">
<h2>
<a href="#installation">installation</a>
</h2>
<p>currently, only win10 is supported. it is possible to run this script via the wsl to modify the win10 notion app.</p>
<p>(the <a href="#styling">styles</a> should also work for the web version.
these can be installed via an extension like <a href="https://chrome.google.com/webstore/detail/stylus/clngdbkpkpeebahjckkjfobafhncgmne?hl=en">stylus</a>
or a built-in feature like <a href="https://www.userchrome.org/">userChrome.css</a>.)</p>
<ol>
<li>install <a href="https://nodejs.org/en/">node.js</a> (if using the wsl, it is recommended to install via <a href="https://github.com/nvm-sh/nvm#install--update-script">nvm</a>).</li>
<li>install <a href="https://www.python.org/">python</a> (if using the wsl, follow <a href="https://docs.python-guide.org/starting/install3/linux/">this guide</a>).</li>
<li>reboot.</li>
<li>in cmd (on windows) or bash (with wsl), run <code>npm install -g asar</code> (check installation by running <code>asar</code>).</li>
<li><a href="https://github.com/dragonwocky/notion-enhancer/archive/master.zip">download this enhancer</a> &amp; extract to a location it can safely remain (this must be in the windows filesystem,
even if you are running the script from the wsl).</li>
<li>ensure notion is closed.</li>
<li>optional: to remove previous versions of notion enhancer, run <code>cleaner.py</code></li>
<li>optional: modify the <code>resources/user.css</code> files to your liking.</li>
<li>run <code>customiser.py</code> to build changes.</li>
</ol>
<p>done: run notion and enjoy.</p>
<p><strong>oh no, now my app won&#39;t open!</strong></p>
<ol>
<li>kill any notion tasks in the task manager (<code>ctrl+shift+esc</code>).</li>
<li>run <code>cleaner.py</code>.</li>
<li>reboot.</li>
<li>follow instructions above (ensuring notion <em>isn&#39;t</em> running! again, check task manager).</li>
</ol>
</section>
<section class="block" id="this-is-a-fork">
<h2>
<a href="#this-is-a-fork">this is a fork</a>
</h2>
<p>credit where credit is due, this was originally made by Uzver (github: <a href="https://github.com/TarasokUA">@TarasokUA</a>,
telegram: <a href="https://t.me/UserFromUkraine">UserFromUkraine</a>, discord: Uzver#8760).</p>
<p>he has approved my go-ahead with this fork, as he himself no longer wishes to continue development on the project.</p>
</section>
<section class="block" id="features">
<h2>
<a href="#features">features</a>
</h2>
</section>
<section class="block" id="titlebar">
<h3>
<a href="#titlebar">titlebar</a>
</h3>
<p>default windows titlebar/frame has been replaced by one more fitting to the theme of the app.</p>
<p>this includes the addition of an extra button, &quot;always on top&quot;
symbolised with an arrow (4th from the right). when toggled to point up,
notion will remain the top visible window even if not focused.</p>
<p>to customise which characters are used for these buttons, open in the <code>resources/preload.js</code> file,
find the relevant button (read the comments) and replace its icon with your chosen unicode character (e.g.
replacing <code>element.innerHTML = &#39;&#39;;</code> with <code>element.innerHTML = &#39;🙄&#39;;</code>).</p>
</section>
<section class="block" id="nicer-scrollbars">
<h3>
<a href="#nicer-scrollbars">nicer scrollbars</a>
</h3>
<p>i mean, yeah. get rid of those ugly default scrollbars and use nice inconspicuous
ones that actually look as if they&#39;re part of notion.</p>
<p>to add these to the web version, copy lines 44 - 75 from <code>user.css</code> into your css customiser.</p>
</section>
<section class="block" id="hotkeys">
<h3>
<a href="#hotkeys">hotkeys</a>
</h3>
<ul>
<li><strong>reload window</strong>: in addition to the built-in <code>CmdOrCtrl+R</code> reload,
you can now reload a window with <code>F5</code>.</li>
<li><strong>toggle all notion windows to/from the tray</strong>: <code>CmdOrCtrl+Shift+A</code> by default.</li>
</ul>
<p>to set your own toggle hotkey, open <code>customiser.py</code> and change line 16 (<code>hotkey = &#39;CmdOrCtrl+Shift+A&#39;</code>)
to your preference. you will need to run or re-run <code>customiser.py</code> afterwards.</p>
</section>
<section class="block" id="tray">
<h3>
<a href="#tray">tray</a>
</h3>
<ul>
<li>single-click to toggle app visibility. right click to open menu.</li>
<li>settings will be saved in <code>%localappdata%/Programs/Notion/resources/app/user-preferences.json</code></li>
<li><strong>run on startup</strong>: run notion on boot/startup. (default: true)</li>
<li><strong>hide on open</strong>: hide the launch of notion to the tray. (default: false)</li>
<li><strong>open maximised</strong>: maximise the app on open. (default: false)</li>
<li><strong>close to tray</strong>: close window to tray rather than closing outright
on click of <code></code>. does not apply if multiple notion windows are open. (default: false)</li>
</ul>
</section>
<section class="block" id="styling">
<h3>
<a href="#styling">styling</a>
</h3>
<p>due to <code>customiser.py</code> setting up a direct link to <code>resources/user.css</code>,
changes will be applied instantly on notion reload
(no need to re-run <code>customiser.py</code> every time you want to change some styles).</p>
<p>these should also work for the web version, if copied into your css customiser.</p>
<p>css below will work for every instance of the element, but if you wish to hide only a specific element
(e.g. the &#39;+ new&#39; table row) it is recommended that you prepend each selector with <code>[data-block-id=&#39;ID&#39;]</code> (<a href="https://www.youtube.com/watch?v=6V7eqShm_4w">video tutorial on fetching IDs</a>).</p>
</section>
<section class="block" id="wider-page-view">
<h4>
<a href="#wider-page-view">wider page view</a>
</h4>
<pre><code class="lang-css"><span class="hljs-selector-class">.notion-peek-renderer</span> &gt; <span class="hljs-selector-tag">div</span><span class="hljs-selector-pseudo">:nth-child(2)</span> {
<span class="hljs-attribute">max-width</span>: <span class="hljs-number">85vw</span> <span class="hljs-meta">!important</span>;
}</code></pre>
</section>
<section class="block" id="thinner-cover-image">
<h4>
<a href="#thinner-cover-image">thinner cover image</a>
</h4>
<pre><code class="lang-css"><span class="hljs-selector-attr">[style^=<span class="hljs-string">'position: relative; width: 100%; display: flex; flex-direction: column; align-items: center; height: 30vh;'</span>]</span> {
<span class="hljs-attribute">height</span>: <span class="hljs-number">12vh</span> <span class="hljs-meta">!important</span>;
}
<span class="hljs-selector-attr">[style^=<span class="hljs-string">'position: relative; width: 100%; display: flex; flex-direction: column; align-items: center; height: 30vh;'</span>]</span>
<span class="hljs-selector-tag">img</span> {
<span class="hljs-attribute">height</span>: <span class="hljs-number">20vh</span> <span class="hljs-meta">!important</span>;
}</code></pre>
</section>
<section class="block" id="table-columns-below-100px">
<h4>
<a href="#table-columns-below-100px">table columns below 100px</a>
</h4>
<p><strong>not recommended!</strong> this is unreliable and will cause bugs.
coincidentally, this is also what the youtube video linked above shows how to do.
as it is a per-table-column style, unlike all others here, it must be prepended with the block ID.</p>
<pre><code class="lang-css"><span class="hljs-selector-attr">[data-block-id^=<span class="hljs-string">'ID'</span>]</span>
&gt; <span class="hljs-selector-attr">[style^=<span class="hljs-string">'display: flex; position: absolute; background: rgb(47, 52, 55); z-index: 82; height: 33px; color: rgba(255, 255, 255, 0.6);'</span>]</span>
&gt; <span class="hljs-selector-tag">div</span><span class="hljs-selector-pseudo">:nth-child(1)</span>
&gt; <span class="hljs-selector-tag">div</span><span class="hljs-selector-pseudo">:nth-child(10)</span>
&gt; <span class="hljs-selector-tag">div</span><span class="hljs-selector-pseudo">:nth-child(1)</span>,
<span class="hljs-selector-attr">[data-block-id^=<span class="hljs-string">'ID'</span>]</span>
&gt; <span class="hljs-selector-attr">[style^=<span class="hljs-string">'position: relative; min-width: calc(100% - 192px);'</span>]</span>
&gt; <span class="hljs-selector-attr">[data-block-id]</span>
&gt; <span class="hljs-selector-tag">div</span><span class="hljs-selector-pseudo">:nth-child(10)</span>,
<span class="hljs-selector-attr">[data-block-id^=<span class="hljs-string">'ID'</span>]</span> &gt; <span class="hljs-selector-tag">div</span><span class="hljs-selector-pseudo">:nth-child(5)</span> &gt; <span class="hljs-selector-tag">div</span><span class="hljs-selector-pseudo">:nth-child(10)</span> {
<span class="hljs-attribute">width</span>: <span class="hljs-number">45px</span> <span class="hljs-meta">!important</span>;
}
<span class="hljs-selector-attr">[data-block-id^=<span class="hljs-string">'ID'</span>]</span>
<span class="hljs-selector-attr">[style^=<span class="hljs-string">'position: absolute; top: 0px; left: 0px; pointer-events: none;'</span>]</span><span class="hljs-selector-pseudo">:not(.notion-presence-container)</span> {
<span class="hljs-attribute">display</span>: none;
}</code></pre>
</section>
<section class="block" id="hide--new-table-row">
<h4>
<a href="#hide--new-table-row">hide '+ new' table row</a>
</h4>
<pre><code class="lang-css"><span class="hljs-selector-class">.notion-table-view-add-row</span> {
<span class="hljs-attribute">display</span>: none <span class="hljs-meta">!important</span>;
}</code></pre>
</section>
<section class="block" id="hide-calculations-table-row">
<h4>
<a href="#hide-calculations-table-row">hide calculations table row</a>
</h4>
<pre><code class="lang-css"><span class="hljs-selector-class">.notion-table-view-add-row</span> + <span class="hljs-selector-tag">div</span> {
<span class="hljs-attribute">display</span>: none <span class="hljs-meta">!important</span>;
}</code></pre>
</section>
<section class="block" id="hide--new-board-row">
<h4>
<a href="#hide--new-board-row">hide '+ new' board row</a>
</h4>
<pre><code class="lang-css"><span class="hljs-selector-class">.notion-board-group</span>
<span class="hljs-selector-attr">[style=<span class="hljs-string">'user-select: none; transition: background 120ms ease-in 0s; cursor: pointer; display: inline-flex; align-items: center; flex-shrink: 0; white-space: nowrap; height: 32px; border-radius: 3px; font-size: 14px; line-height: 1.2; min-width: 0px; padding-left: 6px; padding-right: 8px; color: rgba(255, 255, 255, 0.4); width: 100%;'</span>]</span> {
<span class="hljs-attribute">display</span>: none <span class="hljs-meta">!important</span>;
}</code></pre>
</section>
<section class="block" id="hide-board-view-hidden-columns">
<h4>
<a href="#hide-board-view-hidden-columns">hide board view hidden columns</a>
</h4>
<pre><code class="lang-css"><span class="hljs-selector-class">.notion-board-view</span> &gt; <span class="hljs-selector-attr">[data-block-id]</span> &gt; <span class="hljs-selector-tag">div</span><span class="hljs-selector-pseudo">:nth-last-child(2)</span>,
<span class="hljs-selector-class">.notion-board-view</span> &gt; <span class="hljs-selector-attr">[data-block-id]</span> &gt; <span class="hljs-selector-tag">div</span><span class="hljs-selector-pseudo">:first-child</span> &gt; <span class="hljs-selector-tag">div</span><span class="hljs-selector-pseudo">:nth-last-child(2)</span> {
<span class="hljs-attribute">display</span>: none <span class="hljs-meta">!important</span>;
}</code></pre>
</section>
<section class="block" id="hide-board-view-add-a-group">
<h4>
<a href="#hide-board-view-add-a-group">hide board view 'add a group'</a>
</h4>
<pre><code class="lang-css"><span class="hljs-selector-class">.notion-board-view</span> &gt; <span class="hljs-selector-attr">[data-block-id]</span> &gt; <span class="hljs-selector-tag">div</span><span class="hljs-selector-pseudo">:last-child</span>,
<span class="hljs-selector-class">.notion-board-view</span> &gt; <span class="hljs-selector-attr">[data-block-id]</span> &gt; <span class="hljs-selector-tag">div</span><span class="hljs-selector-pseudo">:first-child</span> &gt; <span class="hljs-selector-tag">div</span><span class="hljs-selector-pseudo">:last-child</span> {
<span class="hljs-attribute">display</span>: none <span class="hljs-meta">!important</span>;
}</code></pre>
</section>
<section class="block" id="centre-align-table-column-headers">
<h4>
<a href="#centre-align-table-column-headers">centre-align table column headers</a>
</h4>
<pre><code class="lang-css"><span class="hljs-selector-class">.notion-table-view-header-cell</span> &gt; <span class="hljs-selector-tag">div</span> &gt; <span class="hljs-selector-tag">div</span> {
<span class="hljs-attribute">margin</span>: <span class="hljs-number">0px</span> auto;
}</code></pre>
</section>
<section class="block" id="smaller-table-column-header-icons">
<h4>
<a href="#smaller-table-column-header-icons">smaller table column header icons</a>
</h4>
<pre><code class="lang-css"><span class="hljs-selector-attr">[style^=<span class="hljs-string">'display: flex; position: absolute; background: rgb(47, 52, 55); z-index: 82; height: 33px; color: rgba(255, 255, 255, 0.6);'</span>]</span>
<span class="hljs-selector-tag">div</span><span class="hljs-selector-pseudo">:nth-child(1)</span>
<span class="hljs-selector-tag">svg</span> {
<span class="hljs-attribute">height</span>: <span class="hljs-number">10px</span> <span class="hljs-meta">!important</span>;
<span class="hljs-attribute">width</span>: <span class="hljs-number">10px</span> <span class="hljs-meta">!important</span>;
<span class="hljs-attribute">margin-right</span>: -<span class="hljs-number">4px</span>;
}</code></pre>
</section>
<section class="block" id="remove-icons-from-table-column-headers">
<h4>
<a href="#remove-icons-from-table-column-headers">remove icons from table column headers</a>
</h4>
<pre><code class="lang-css"><span class="hljs-selector-class">.notion-table-view-header-cell</span> <span class="hljs-selector-attr">[style^=<span class="hljs-string">'margin-right: 6px;'</span>]</span> {
<span class="hljs-attribute">display</span>: none <span class="hljs-meta">!important</span>;
}</code></pre>
</section>
<section class="block" id="removingdecreasing-side-padding-for-tables">
<h4>
<a href="#removingdecreasing-side-padding-for-tables">removing/decreasing side padding for tables</a>
</h4>
<pre><code class="lang-css"><span class="hljs-selector-attr">[style^=<span class="hljs-string">'flex-shrink: 0; flex-grow: 1; width: 100%; max-width: 100%; display: flex; align-items: center; flex-direction: column; font-size: 16px; color: rgba(255, 255, 255, 0.9); padding: 0px 96px 30vh;'</span>]</span>
<span class="hljs-selector-class">.notion-table-view</span>,
<span class="hljs-selector-attr">[class=<span class="hljs-string">'notion-scroller'</span>]</span> &gt; <span class="hljs-selector-class">.notion-table-view</span> {
<span class="hljs-attribute">padding-left</span>: <span class="hljs-number">35px</span> <span class="hljs-meta">!important</span>;
<span class="hljs-attribute">padding-right</span>: <span class="hljs-number">15px</span> <span class="hljs-meta">!important</span>;
<span class="hljs-attribute">min-width</span>: <span class="hljs-number">0%</span> <span class="hljs-meta">!important</span>;
}
<span class="hljs-selector-attr">[style^=<span class="hljs-string">'flex-shrink: 0; flex-grow: 1; width: 100%; max-width: 100%; display: flex; align-items: center; flex-direction: column; font-size: 16px; color: rgba(255, 255, 255, 0.9); padding: 0px 96px 30vh;'</span>]</span>
<span class="hljs-selector-class">.notion-selectable</span>
<span class="hljs-selector-class">.notion-scroller</span><span class="hljs-selector-class">.horizontal</span><span class="hljs-selector-pseudo">::-webkit-scrollbar-track</span> {
<span class="hljs-attribute">margin-left</span>: <span class="hljs-number">10px</span>;
<span class="hljs-attribute">margin-right</span>: <span class="hljs-number">10px</span>;
}</code></pre>
</section>
<section class="block" id="removingdecreasing-side-padding-for-boards">
<h4>
<a href="#removingdecreasing-side-padding-for-boards">removing/decreasing side padding for boards</a>
</h4>
<pre><code class="lang-css"><span class="hljs-selector-class">.notion-board-view</span> {
<span class="hljs-attribute">padding-left</span>: <span class="hljs-number">10px</span> <span class="hljs-meta">!important</span>;
<span class="hljs-attribute">padding-right</span>: <span class="hljs-number">10px</span> <span class="hljs-meta">!important</span>;
}</code></pre>
</section>
<section class="block" id="other-details">
<h2>
<a href="#other-details">other details</a>
</h2>
<p>i have an unhealthy habit of avoiding capital letters. nothing enforces this, i just do it.</p>
<p>the notion logo belongs entirely to the notion team, and was sourced from their
<a href="https://www.notion.so/Media-Kit-205535b1d9c4440497a3d7a2ac096286">media kit</a>.</p>
<p>if you have any questions, check <a href="https://dragonwocky.me/">my website</a> for contact details.</p>
</section></div><footer class="footer"><hr><p><a href="https://github.com/dragonwocky/notion-enhancer/blob/master/README.md">Edit on GitHub</a> // © 2020 dragonwocky &amp; Uzver, under the <a href="https://choosealicense.com/licenses/mit/">MIT license</a>.</p>
</footer><nav><a class="next" href="changelog.html"></a></nav></article></div></body></html>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 15 KiB

155
package-lock.json generated Normal file
View File

@ -0,0 +1,155 @@
{
"name": "notion-enhancer",
"version": "0.11.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "notion-enhancer",
"version": "0.11.1",
"license": "MIT",
"dependencies": {
"@electron/asar": "^3.2.9",
"arg": "^5.0.2",
"chalk-template": "^1.1.0"
},
"bin": {
"notion-enhancer": "bin.mjs"
},
"engines": {
"node": ">=18.x.x"
},
"funding": {
"url": "https://github.com/sponsors/dragonwocky"
}
},
"node_modules/@electron/asar": {
"version": "3.2.9",
"resolved": "https://registry.npmjs.org/@electron/asar/-/asar-3.2.9.tgz",
"integrity": "sha512-Vu2P3X2gcZ3MY9W7yH72X9+AMXwUQZEJBrsPIbX0JsdllLtoh62/Q8Wg370/DawIEVKOyfD6KtTLo645ezqxUA==",
"dependencies": {
"commander": "^5.0.0",
"glob": "^7.1.6",
"minimatch": "^3.0.4"
},
"bin": {
"asar": "bin/asar.js"
},
"engines": {
"node": ">=10.12.0"
}
},
"node_modules/arg": {
"version": "5.0.2",
"license": "MIT"
},
"node_modules/balanced-match": {
"version": "1.0.2",
"license": "MIT"
},
"node_modules/brace-expansion": {
"version": "1.1.11",
"license": "MIT",
"dependencies": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
}
},
"node_modules/chalk": {
"version": "5.2.0",
"license": "MIT",
"engines": {
"node": "^12.17.0 || ^14.13 || >=16.0.0"
},
"funding": {
"url": "https://github.com/chalk/chalk?sponsor=1"
}
},
"node_modules/chalk-template": {
"version": "1.1.0",
"license": "MIT",
"dependencies": {
"chalk": "^5.2.0"
},
"engines": {
"node": ">=14.16"
},
"funding": {
"url": "https://github.com/chalk/chalk-template?sponsor=1"
}
},
"node_modules/commander": {
"version": "5.1.0",
"license": "MIT",
"engines": {
"node": ">= 6"
}
},
"node_modules/concat-map": {
"version": "0.0.1",
"license": "MIT"
},
"node_modules/fs.realpath": {
"version": "1.0.0",
"license": "ISC"
},
"node_modules/glob": {
"version": "7.2.3",
"license": "ISC",
"dependencies": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
"inherits": "2",
"minimatch": "^3.1.1",
"once": "^1.3.0",
"path-is-absolute": "^1.0.0"
},
"engines": {
"node": "*"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/inflight": {
"version": "1.0.6",
"license": "ISC",
"dependencies": {
"once": "^1.3.0",
"wrappy": "1"
}
},
"node_modules/inherits": {
"version": "2.0.4",
"license": "ISC"
},
"node_modules/minimatch": {
"version": "3.1.2",
"license": "ISC",
"dependencies": {
"brace-expansion": "^1.1.7"
},
"engines": {
"node": "*"
}
},
"node_modules/once": {
"version": "1.4.0",
"license": "ISC",
"dependencies": {
"wrappy": "1"
}
},
"node_modules/path-is-absolute": {
"version": "1.0.1",
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/wrappy": {
"version": "1.0.2",
"license": "ISC"
}
}
}

42
package.json Normal file
View File

@ -0,0 +1,42 @@
{
"name": "notion-enhancer",
"version": "0.11.1",
"author": "dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)",
"description": "Customise the all-in-one productivity workspace Notion.",
"homepage": "https://notion-enhancer.github.io",
"repository": "github:notion-enhancer/desktop",
"bugs": "https://github.com/notion-enhancer/desktop/issues",
"funding": "https://github.com/sponsors/dragonwocky",
"license": "MIT",
"bin": "bin.mjs",
"type": "module",
"scripts": {
"build": "./scripts/build-browser-extension.sh",
"vendor": "node ./scripts/vendor-dependencies.mjs"
},
"engines": {
"node": ">=18.x.x"
},
"keywords": [
"windows",
"macos",
"linux",
"productivity",
"hack",
"extensions",
"themes",
"integrations",
"addons",
"mod",
"mods",
"mod-loader",
"enhancer",
"notion",
"notion-enhancer"
],
"dependencies": {
"@electron/asar": "^3.2.9",
"chalk-template": "^1.1.0",
"arg": "^5.0.2"
}
}

View File

@ -1,109 +0,0 @@
/* === INJECTION MARKER === */
/*
* Notion Enhancer
* (c) 2020 dragonwocky <thedragonring.bod@gmail.com>
* (c) 2020 TarasokUA
* (https://dragonwocky.me/) under the MIT license
*/
// adds: tray support (inc. context menu with settings), window toggle hotkey
// DO NOT REMOVE THE INJECTION MARKER ABOVE.
// DO NOT CHANGE THE NAME OF THE 'enhancements()' FUNCTION.
let tray;
function enhancements() {
const { Tray, Menu } = require('electron'),
path = require('path'),
store = new (require(path.join(__dirname, '..', 'store.js')))({
config: 'user-preferences',
defaults: {
openhidden: false,
maximised: false,
tray: false
}
}),
states = {
startup: electron_1.app.getLoginItemSettings().openAtLogin,
openhidden: store.get('openhidden'),
maximised: store.get('maximised'),
tray: store.get('tray')
};
tray = new Tray(path.join(__dirname, './notion.ico'));
const contextMenu = Menu.buildFromTemplate([
{
id: 'startup',
label: 'run on startup',
type: 'checkbox',
checked: states.startup,
click: () =>
contextMenu.getMenuItemById('startup').checked
? electron_1.app.setLoginItemSettings({ openAtLogin: true })
: electron_1.app.setLoginItemSettings({ openAtLogin: false })
},
{
id: 'openhidden',
label: 'hide on open',
type: 'checkbox',
checked: states.openhidden,
click: () =>
contextMenu.getMenuItemById('openhidden').checked
? store.set('openhidden', true)
: store.set('openhidden', false)
},
{
id: 'maximised',
label: 'open maximised',
type: 'checkbox',
checked: states.maximised,
click: () =>
contextMenu.getMenuItemById('maximised').checked
? store.set('maximised', true)
: store.set('maximised', false)
},
{
id: 'tray',
label: 'close to tray',
type: 'checkbox',
checked: states.tray,
click: () =>
contextMenu.getMenuItemById('tray').checked
? store.set('tray', true)
: store.set('tray', false)
},
{
type: 'separator'
},
{
label: '(x) quit',
role: 'quit'
}
]);
tray.setContextMenu(contextMenu);
tray.on('click', function () {
const win = electron_1.BrowserWindow.getAllWindows()[0];
if (win.isVisible()) {
if (win.isMinimized()) {
win.show();
} else win.hide();
} else {
if (contextMenu.getMenuItemById('maximised').checked) {
win.maximize();
} else win.show();
}
});
const hotkey = '___hotkey___'; // will be set by python script
electron_1.globalShortcut.register(hotkey, () => {
const windows = electron_1.BrowserWindow.getAllWindows();
if (windows.some(win => !win.isVisible())) {
if (contextMenu.getMenuItemById('maximised').checked) {
windows.forEach(win => win.maximize());
} else windows.forEach(win => win.show());
} else windows.forEach(win => win.hide());
});
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 110 KiB

View File

@ -1,94 +0,0 @@
/* === INJECTION MARKER === */
/*
* Notion Enhancer
* (c) 2020 dragonwocky <thedragonring.bod@gmail.com>
* (c) 2020 TarasokUA
* (https://dragonwocky.me/) under the MIT license
*/
// adds: custom styles, nicer window control buttons
// DO NOT REMOVE THE INJECTION MARKER ABOVE
require('electron').remote.getGlobal('setTimeout')(() => {
/* style injection */
const fs = require('fs'),
css = fs.readFileSync('___user.css___'), // will be set by python script
style = document.createElement('style'),
head = document.getElementsByTagName('head')[0];
if (!head) return;
style.type = 'text/css';
style.innerHTML = css;
head.appendChild(style);
const intervalID = setInterval(injection, 100);
function injection() {
if (document.querySelector('div.notion-topbar > div') == undefined) return;
const appwindow = require('electron').remote.getCurrentWindow();
/* window control buttons */
let node = document.querySelector('div.notion-topbar > div'),
element = document.createElement('div');
element.id = 'window-buttons-area';
node.appendChild(element);
node = document.querySelector('#window-buttons-area');
// always-on-top
element = document.createElement('button');
element.classList.add('window-buttons');
element.innerHTML = '🠛';
element.onclick = function () {
const state = appwindow.isAlwaysOnTop();
appwindow.setAlwaysOnTop(!state);
this.innerHTML = state ? '🠛' : '🠙';
};
node.appendChild(element);
// minimise
element = document.createElement('button');
element.classList.add('window-buttons');
element.innerHTML = '⚊';
element.onclick = () => appwindow.minimize();
node.appendChild(element);
// maximise
element = document.createElement('button');
element.classList.add('window-buttons');
element.innerHTML = '▢';
element.onclick = () =>
appwindow.isMaximized() ? appwindow.unmaximize() : appwindow.maximize();
node.appendChild(element);
// close
const path = require('path');
element = document.createElement('button');
element.classList.add('window-buttons');
element.innerHTML = '⨉';
element.onclick = () => {
const store = new (require(path.join(__dirname, '..', 'store.js')))({
config: 'user-preferences',
defaults: {
tray: false
}
});
if (
store.get('tray') &&
require('electron').remote.BrowserWindow.getAllWindows().length === 1
) {
appwindow.hide();
} else appwindow.close();
};
node.appendChild(element);
clearInterval(intervalID);
/* reload window */
document.defaultView.addEventListener(
'keyup',
ev => void (ev.code === 'F5' ? appwindow.reload() : 0),
true
);
}
}, 100);

View File

@ -1,35 +0,0 @@
/*
* Notion Enhancer
* (c) 2020 dragonwocky <thedragonring.bod@gmail.com>
* (c) 2020 TarasokUA
* (https://dragonwocky.me/) under the MIT license
*/
// a wrapper for accessing data stored in a JSON file
const path = require('path'),
fs = require('fs');
class Store {
constructor(opts) {
this.path = path.join(__dirname, opts.config + '.json');
this.data = parseDataFile(this.path, opts.defaults);
}
get(key) {
return this.data[key];
}
set(key, val) {
this.data[key] = val;
fs.writeFileSync(this.path, JSON.stringify(this.data));
}
}
function parseDataFile(path, defaults) {
try {
return JSON.parse(fs.readFileSync(path));
} catch (error) {
return defaults;
}
}
module.exports = Store;

View File

@ -1,75 +0,0 @@
/*
* Notion Enhancer
* (c) 2020 dragonwocky <thedragonring.bod@gmail.com>
* (c) 2020 TarasokUA
* (https://dragonwocky.me/) under the MIT license
*/
/* window control buttons: block */
#window-buttons-area {
padding-left: 14px;
user-select: none;
}
/* window control buttons: light theme */
.notion-light-theme .window-buttons {
background: rgb(255, 255, 255);
color: black;
border: 0;
margin: 0px 0px 0px 9px;
width: 32px;
line-height: 26px;
border-radius: 4px;
font-size: 16px;
transition-duration: 0.2s;
font-weight: bold;
}
.notion-light-theme .window-buttons:hover {
background: rgb(239, 239, 239);
}
/* window control buttons: dark theme */
.notion-dark-theme .window-buttons {
background: rgb(47, 52, 55);
border: 0;
margin: 0px 0px 0px 9px;
width: 32px;
line-height: 26px;
border-radius: 4px;
font-size: 16px;
transition-duration: 0.2s;
}
.notion-dark-theme .window-buttons:hover {
background: rgb(71, 76, 80);
}
/* scrollbar: pointer */
.notion-scroller {
cursor: auto;
}
/* scrollbar: size */
::-webkit-scrollbar {
width: 8px; /* for vertical */
height: 8px; /* for horizontal */
}
/* scrollbar: light theme */
.notion-light-theme ::-webkit-scrollbar-corner {
background-color: transparent; /* for overlap */
}
.notion-light-theme ::-webkit-scrollbar-thumb {
border-radius: 5px;
background-color: #d9d8d6;
border: 1px solid #cacac8;
}
.notion-light-theme ::-webkit-scrollbar-thumb:hover {
background: #cacac8;
}
/* scrollbar: dark theme */
.notion-dark-theme ::-webkit-scrollbar-corner {
background-color: transparent; /* for overlap */
}
.notion-dark-theme ::-webkit-scrollbar-thumb {
border-radius: 5px;
background-color: #505457;
}
.notion-dark-theme ::-webkit-scrollbar-thumb:hover {
background: #696d6f;
}

View File

@ -0,0 +1,8 @@
#!/usr/bin/env bash
version=$(node -p "require('./package.json').version")
cd src
mkdir -p ../dist
rm -f "../dist/notion-enhancer-$version.zip"
zip -r9 "../dist/notion-enhancer-$version.zip" .

160
scripts/enhance-desktop-app.mjs Executable file
View File

@ -0,0 +1,160 @@
/**
* notion-enhancer
* (c) 2024 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
* (https://notion-enhancer.github.io/) under the MIT license
*/
import os from "node:os";
import fsp from "node:fs/promises";
import { resolve } from "node:path";
import { existsSync } from "node:fs";
import { execSync } from "node:child_process";
import { createRequire } from "node:module";
import { fileURLToPath } from "node:url";
import asar from "@electron/asar";
import patch from "./patch-desktop-app.mjs";
const nodeRequire = createRequire(import.meta.url),
platform =
process.platform === "linux" &&
os.release().toLowerCase().includes("microsoft")
? "wsl"
: process.platform,
getEnv = (name) => {
if (platform !== "wsl" || process.env[name]) return process.env[name];
// read windows environment variables and convert
// windows paths to paths mounted in the wsl fs
const pipe = { encoding: "utf8", stdio: "pipe" },
value = execSync(`cmd.exe /c echo %${name}%`, pipe).trim(),
isAbsolutePath = /^[a-zA-Z]:[\\\/]/.test(value),
isSystemPath = /^[\\\/]/.test(value);
if (isAbsolutePath) {
// e.g. C:\Program Files
const drive = value[0].toLowerCase(),
path = value.slice(2).replace(/\\/g, "/");
process.env[name] = `/mnt/${drive}${path}`;
} else if (isSystemPath) {
// e.g. \Program Files
const drive = getEnv("SYSTEMDRIVE")[0].toLowerCase(),
path = value.replace(/\\/g, "/");
process.env[name] = `/mnt/${drive}${path}`;
} else process.env[name] = value;
return process.env[name];
};
let __notionResources;
const setNotionPath = (path) => {
// sets notion resource path to user provided value
// e.g. with the --path cli option
__notionResources = path;
},
getResourcePath = (...paths) => {
if (__notionResources) return resolve(__notionResources, ...paths);
// prettier-ignore
for (const [platforms, notionResources] of [
[['win32', 'wsl'], resolve(`${getEnv("LOCALAPPDATA")}/Programs/Notion/resources`)],
[['win32', 'wsl'], resolve(`${getEnv("PROGRAMW6432")}/Notion/resources`)],
[['darwin'], `/Users/${getEnv("USER")}/Applications/Notion.app/Contents/Resources`],
[['darwin'], "/Applications/Notion.app/Contents/Resources"],
[['linux'], "/opt/notion-app"],
]) {
if (!platforms.includes(platform)) continue;
if (!existsSync(notionResources)) continue;
__notionResources = notionResources;
return resolve(__notionResources, ...paths);
}
},
extractFile = (path) => {
const archive = getResourcePath("app.asar");
return asar.extractFile(archive, path);
};
const getInsertPath = (...paths) => {
return "node_modules/notion-enhancer/" + paths.join("/");
},
getInsertVersion = () => {
try {
const manifest = extractFile(getInsertPath("package.json")).toString();
return JSON.parse(manifest).version;
} catch {
return null;
}
};
const backupApp = async () => {
const archive = getResourcePath("app.asar");
if (!existsSync(archive)) return false;
await fsp.cp(archive, archive + ".bak");
return true;
},
restoreApp = async () => {
const archive = getResourcePath("app.asar");
if (!existsSync(archive + ".bak")) return false;
await fsp.rename(archive + ".bak", archive);
return true;
},
enhanceApp = async (debug = false, directoryMode = false) => {
const app = getResourcePath("app"),
archive = getResourcePath("app.asar");
// directory mode acts on pre-extracted sources
// as part of the notion-repackaged build process
if (directoryMode) {
if (!existsSync(app)) return false;
for (let file of await fsp.readdir(app, { recursive: true })) {
file = file.replace(/^\//g, "");
const appPath = resolve(app, file),
stat = await fsp.stat(appPath);
if (stat.isFile()) {
const content = await fsp.readFile(appPath);
await fsp.writeFile(appPath, patch(file, content));
}
}
} else {
if (!existsSync(archive)) return false;
if (existsSync(app)) await fsp.rm(app, { recursive: true, force: true });
await fsp.mkdir(app);
// extract archive to folder and apply patches
for (let file of asar.listPackage(archive)) {
file = file.replace(/^\//g, "");
const stat = asar.statFile(archive, file),
isFolder = !!stat.files,
isSymlink = !!stat.link,
isExecutable = stat.executable,
appPath = resolve(app, file);
if (isFolder) {
await fsp.mkdir(appPath);
} else if (isSymlink) {
await fsp.symlink(appPath, resolve(app, link));
} else {
await fsp.writeFile(appPath, patch(file, extractFile(file)));
if (isExecutable) await fsp.chmod(appPath, "755");
}
}
}
// insert the notion-enhancer/src folder into notion's node_modules
const insertSrc = fileURLToPath(new URL("../src", import.meta.url)),
insertDest = resolve(app, getInsertPath());
await fsp.cp(insertSrc, insertDest, { recursive: true });
// create package.json with cli-specific fields removed
const insertManifest = resolve(insertDest, "package.json"),
manifest = { ...nodeRequire("../package.json"), main: "init.js" },
excludes = ["bin", "type", "scripts", "engines", "dependencies"];
for (const key of excludes) delete manifest[key];
await fsp.writeFile(insertManifest, JSON.stringify(manifest));
if (!directoryMode) {
// re-package enhanced sources into executable archive
await asar.createPackage(app, archive);
// cleanup extracted files unless in debug mode
if (!debug) await fsp.rm(app, { recursive: true });
}
return true;
};
export {
backupApp,
restoreApp,
enhanceApp,
getInsertVersion,
getResourcePath,
setNotionPath,
};

View File

@ -0,0 +1,893 @@
/**
* notion-enhancer
* (c) 2023 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
* (https://notion-enhancer.github.io/) under the MIT license
*/
// paste this in the devtools console at to generate theme css
// at https://www.notion.so/9390e51f458940a5a339dc4b8fdea2fb.
// to detect fonts, open the ... menu before running.
// repeat for both light and dark modes, pass the css through
// https://css-minifier.com/ and https://css.github.io/csso/csso.html
// and then save it to core/variables.css and core/theme.css
// todo: svg page & property icons
const darkMode = document.body.classList.contains("dark"),
modeSelector = darkMode ? ".dark" : ":not(.dark)",
bodySelector = `.notion-body${modeSelector}`;
let cssRoot = "",
cssBody = "",
cssRefs = {};
const getComputedPropertyValue = (el, prop) => {
const styles = window.getComputedStyle(el),
value = styles.getPropertyValue(prop);
return value;
},
cssVariable = ({ name, value, alias, splitValues = false }) => {
const values = splitValues ? value.split(", ") : [value],
rgbPattern = /^rgba?\(\d{1,3},\d{1,3},\d{1,3}(?:,\d{1,3})?\)$/,
isColor = rgbPattern.test(value.replace(/\s/g, ""));
if (isColor) {
values[0] = values[0].replace(/\s/g, "");
const hasOpaqueAlpha =
values[0].trim().startsWith("rgba(") &&
values[0].trim().endsWith(",1)");
if (hasOpaqueAlpha) values[0] = `rgb(${values[0].slice(5, -3)})`;
}
if (!cssRoot.includes(`--theme--${name}:`)) {
cssRoot += `--theme--${name}:${
alias ? `var(--theme--${alias})` : value
};`;
}
return {
name,
value,
ref: `var(--theme--${name},${values[0]})${
values.length > 1 ? ", " : ""
}${values.slice(1).join(", ")} !important`,
};
},
overrideStyle = ({
element,
selector = "",
property,
variable,
variableAliases = {},
valueAliases = [],
specificity = ["mode", "value"],
cssProps = {},
postProcessor = (selector, cssProps) => [selector, cssProps],
}) => {
if (selector) element ??= document.querySelector(selector);
const style = element?.getAttribute("style") ?? "",
pattern = String.raw`(?:^|(?:;\s*))${property}:\s*([^;]+);?`,
match = style.match(new RegExp(pattern));
if (typeof variable === "string") {
let value = match?.[1];
if (element) {
value ??= getComputedPropertyValue(
element,
property === "background" ? "background-color" : property
);
}
if (!value) throw new Error(`${property} not found for ${selector}`);
variable = cssVariable({
name: variable,
value: value,
alias: variableAliases[value],
splitValues: property === "font-family",
});
}
if (specificity.includes("value")) {
if (/(?<!rgb\()[^\s\d,]+,/g.test(selector) && !selector.includes(":is")) {
selector = `:is(${selector})`;
}
if (match?.[0]) selector += `[style*="${match[0].replace(/"/g, `\\"`)}"]`;
else {
const propSelector = [variable.value, ...valueAliases]
.map((value) =>
property === "color"
? `[style^="color: ${value}"],
[style^="color:${value}"],
[style*=";color: ${value}"],
[style*=";color:${value}"],
[style*=" color: ${value}"],
[style*=" color:${value}"],
[style*="fill: ${value}"],
[style*="fill:${value}"]`
: property === "background"
? `[style^="background: ${value}"],
[style^="background:${value}"],
[style*=";background: ${value}"],
[style*=";background:${value}"],
[style*=" background: ${value}"],
[style*=" background:${value}"],
[style*="background-color: ${value}"],
[style*="background-color:${value}"]`
: `[style*="${property}: ${value}"],
[style*="${property}:${value}"]`
)
.join(",");
selector += selector ? `:is(${propSelector})` : propSelector;
}
}
if (specificity.includes("mode")) {
selector =
/(?<!rgb\()[^\s\d,]+,/g.test(selector) && !selector.includes(":is")
? `${bodySelector} :is(${selector})`
: `${bodySelector} ${selector}`;
}
cssProps[property] = variable;
cssProps["fill"] ??= cssProps["color"];
[selector, cssProps] = postProcessor(selector, cssProps);
const body = Object.entries(cssProps)
.filter(([prop, val]) => prop && val)
.map(([prop, val]) => `${prop}:${val?.ref ?? val}`)
.join(";");
cssRefs[body] ??= [];
cssRefs[body].push(selector);
variableAliases[variable.value] ??= variable.name;
};
const styleText = () => {
const primary = cssVariable({
name: "fg-primary",
value: darkMode ? "rgba(255, 255, 255, 0.81)" : "rgb(55, 53, 47)",
}),
primaryAliases = darkMode
? [
"rgb(211, 211, 211)",
"rgb(255, 255, 255)",
"rgba(255, 255, 255, 0.8",
"rgba(255, 255, 255, 0.9",
"rgba(255, 255, 255, 1",
]
: [
"rgba(255, 255, 255, 0.9)",
"rgba(55, 53, 47, 0.8",
"rgba(55, 53, 47, 0.9",
"rgba(55, 53, 47, 1",
];
const secondary = cssVariable({
name: "fg-secondary",
value: darkMode ? "rgb(155, 155, 155)" : "rgba(25, 23, 17, 0.6)",
}),
secondaryAliases = darkMode
? [
"rgb(127, 127, 127)",
"rgba(255, 255, 255, 0.0",
"rgba(255, 255, 255, 0.1",
"rgba(255, 255, 255, 0.2",
"rgba(255, 255, 255, 0.3",
"rgba(255, 255, 255, 0.4",
"rgba(255, 255, 255, 0.5",
"rgba(255, 255, 255, 0.6",
"rgba(255, 255, 255, 0.7",
]
: [
"rgba(206, 205, 202, 0.6)",
"rgba(55, 53, 47, 0.0",
"rgba(55, 53, 47, 0.1",
"rgba(55, 53, 47, 0.2",
"rgba(55, 53, 47, 0.3",
"rgba(55, 53, 47, 0.4",
"rgba(55, 53, 47, 0.5",
"rgba(55, 53, 47, 0.6",
"rgba(55, 53, 47, 0.7",
];
overrideStyle({
property: "color",
variable: primary,
valueAliases: primaryAliases,
cssProps: {
"caret-color": primary,
"text-decoration-color": "currentColor",
fill: primary,
},
});
overrideStyle({
property: "color",
variable: secondary,
valueAliases: secondaryAliases,
cssProps: {
"caret-color": secondary,
"text-decoration-color": "currentColor",
fill: secondary,
},
postProcessor(selector, cssProps) {
return [
`${bodySelector} :is(.rdp-nav_icon, .rdp-head_cell,
.rdp-day.rdp-day_outside, ::placeholder), ${selector}`,
cssProps,
];
},
});
overrideStyle({
property: "caret-color",
variable: primary,
valueAliases: primaryAliases,
});
overrideStyle({
property: "caret-color",
variable: secondary,
valueAliases: secondaryAliases,
});
overrideStyle({
selector: `[style*="-webkit-text-fill-color:"]`,
property: "-webkit-text-fill-color",
variable: secondary,
specificity: ["mode"],
});
// light mode tags have coloured text,
// replace with primary text for inter-mode consistency
for (const tagSelector of [
`[style*="height: 20px; border-radius: 3px; padding-left: 6px;"][style*="background:"]`,
`.notion-collection_view-block [style*="height: 14px; border-radius: 3px; padding-left: 6px;"]`,
`.notion-timeline-item-properties [style*="height: 18px; border-radius: 3px; padding-left: 8px;"]`,
]) {
for (const el of document.querySelectorAll(tagSelector)) {
if (darkMode) continue;
overrideStyle({
element: el,
selector: tagSelector,
property: "color",
variable: "fg-primary",
});
}
}
};
const styleBorders = () => {
const border = cssVariable({
name: "fg-border",
value: darkMode ? "rgb(47, 47, 47)" : "rgb(233, 233, 231)",
}),
borderColors = darkMode
? [border.value.slice(4, -1), "37, 37, 37", "255, 255, 255"]
: [border.value.slice(4, -1), "238, 238, 237", "55, 53, 47"],
boxShadows = darkMode
? [
"; box-shadow: rgba(255, 255, 255, 0.094) 0px -1px 0px;",
"; box-shadow: rgba(15, 15, 15, 0.2) 0px 0px 0px 1px inset;",
"; box-shadow: rgb(25, 25, 25) -3px 0px 0px, rgb(47, 47, 47) 0px 1px 0px;",
]
: [
"; box-shadow: rgba(55, 53, 47, 0.09) 0px -1px 0px;",
"; box-shadow: rgba(15, 15, 15, 0.1) 0px 0px 0px 1px inset;",
"; box-shadow: white -3px 0px 0px, rgb(233, 233, 231) 0px 1px 0px;",
];
for (const el of document.querySelectorAll(`[style*="box-shadow:"]`)) {
const boxShadow = el
.getAttribute("style")
.match(/(?:^|(?:;\s*))box-shadow:\s*([^;]+);?/)?.[0];
if (borderColors.some((color) => boxShadow.includes(color))) {
boxShadows.push(boxShadow);
}
}
overrideStyle({
selector: `[style*="height: 1px;"][style*="background"]`,
property: "background",
variable: border,
specificity: ["mode"],
});
cssBody += `
${bodySelector} :is(${[...new Set(borderColors)]
.map(
(color) =>
`[style*="px solid rgb(${color}"], [style*="px solid rgba(${color}"]`
)
.join(", ")}):is([style*="border:"], [style*="border-top:"],
[style*="border-left:"], [style*="border-bottom:"],
[style*="border-right:"]) { border-color: ${border.ref}; }
${[...new Set(boxShadows)]
.map((shadow) => {
if (shadow.startsWith(";")) shadow = shadow.slice(1);
return `${bodySelector} [style*="${shadow}"] { ${shadow
.replace(
/rgba?\([^\)]+\)/g,
shadow.includes("-3px 0px 0px, ")
? "transparent"
: `var(--theme--fg-border, ${border.value})`
)
.slice(0, -1)} !important; }`;
})
.join("")}
`;
};
const styleColoredText = () => {
// inline text
for (const el of document.querySelectorAll(
'.notion-selectable .notion-enable-hover[style*="color:"][style*="fill:"]:not([style*="mono"])'
)) {
if (!el.innerText || /\s/.test(el.innerText)) continue;
overrideStyle({
element: el,
selector: `
.notion-selectable .notion-enable-hover,
.notion-code-block span.token
`,
property: "color",
variable: `fg-${el.innerText}`,
});
}
// block text
for (const el of document.querySelectorAll(
'.notion-text-block > [style*="color:"][style*="fill:"]'
)) {
if (!el.innerText || /\s/.test(el.innerText)) continue;
overrideStyle({
element: el,
selector: `.notion-text-block > [style*="color:"][style*="fill:"]`,
property: "color",
variable: `fg-${el.innerText}`,
});
}
// board text
for (const group of document.querySelectorAll(
".notion-board-view .notion-board-group"
)) {
// get color name from card
const card = group.querySelector('a[style*="background"]'),
innerText = card.innerText.replace("Drag image to reposition\n", "");
if (!innerText || /\s/.test(innerText)) continue;
const el = group.querySelector('[style*="height: 32px"]'),
groupStyle = group
.getAttribute("style")
.match(/background(?:-color)?:\s*([^;]+);?/)[1];
overrideStyle({
element: el,
selector: `.notion-board-view :is(
.notion-board-group[style*="${groupStyle}"] [style*="height: 32px"],
[style*="${groupStyle}"] > [style*="color"]:nth-child(2),
[style*="${groupStyle}"] > div > svg
)`,
property: "color",
// light_gray text doesn't exist
variable: `fg-${innerText === "light_gray" ? "secondary" : innerText}`,
specificity: ["mode"],
});
}
};
const styleBackgrounds = () => {
const primary = cssVariable({
name: "bg-primary",
value: darkMode ? "rgb(25, 25, 25)" : "white",
}),
secondary = cssVariable({
name: "bg-secondary",
value: darkMode ? "rgb(32, 32, 32)" : "rgb(251, 251, 250)",
});
overrideStyle({
property: "background",
variable: primary,
valueAliases: darkMode ? [] : ["rgb(255, 255, 255)", "rgb(247, 247, 247)"],
postProcessor(selector, cssProps) {
return [`${selector}:not(.notion-timeline-view)`, cssProps];
},
});
overrideStyle({
property: "background",
variable: secondary,
valueAliases: darkMode
? ["rgb(37, 37, 37)", "rgb(47, 47, 47)"]
: ["rgb(253, 253, 253)"],
});
// patch: remove overlay from settings sidebar
// to match notion-enhancer menu sidebar colour
cssBody += `.notion-overlay-container .notion-space-settings > div > div > [style*="height: 100%; background: rgba(255, 255, 255, 0.03);"] { background: transparent !important }`;
// cards
overrideStyle({
selector: `.notion-timeline-item,
.notion-calendar-view .notion-collection-item > a,
.notion-gallery-view .notion-collection-item > a`,
property: "background",
variable: secondary,
});
// popups
overrideStyle({
selector: `.notion-overlay-container [style*="border-radius: 4px;"
][style*="position: relative; max-width: calc(100vw - 24px); box-shadow:"],
[style*="font-size: 12px;"][style*="box-shadow:"][
style*="border-radius: 3px; max-width: calc(100% - 16px); min-height: 24px; overflow: hidden;"
][style*="position: absolute; right: 8px; bottom: 8px; z-index:"],
[style*="height: 32px;"][style*="font-size: 14px; line-height: 1.2; border-radius: 5px; box-shadow:"],
[style*="transition: background"][style*="cursor: pointer;"][
style*="border-radius: 3px; height: 24px; width: 24px;"][style*="box-shadow:"],
[style*="right: 6px; top: 4px;"][style*="border-radius: 4px;"][style*="gap: 1px;"][style*="box-shadow:"]`,
property: "background",
variable: secondary,
});
// modals
overrideStyle({
selector: `.notion-overlay-container [data-overlay] :is(
[style*="height: 100%; width: 275px;"][style*="flex-direction: column;"],
.notion-space-settings [style*="flex-grow: 1"] > [style*="background-color"])`,
property: "background",
variable: primary,
specificity: ["mode"],
});
overrideStyle({
selector: `.notion-overlay-container [data-overlay] :is(
[style*="height: 100%; width: 275px;"][style*="flex-direction: column;"] + [style*="width: 100%;"],
.notion-space-settings [style*="height: 100%; background:"][style*="max-width: 250px;"])`,
property: "background",
variable: secondary,
specificity: ["mode"],
});
// timeline fades
overrideStyle({
selector: `.notion-timeline-view`,
property: "background",
variable: primary,
specificity: ["mode"],
});
cssBody += `[style*="linear-gradient(to left, ${
darkMode ? primary.value : "white"
} 20%, rgba(${
darkMode ? primary.value.slice(4, -1) : "255, 255, 255"
}, 0) 100%)"] { background-image: linear-gradient(to left,
var(--theme--bg-primary, ${primary.value}) 20%, transparent
100%) !important; }
[style*="linear-gradient(to right, ${
darkMode ? primary.value : "white"
} 20%, rgba(${
darkMode ? primary.value.slice(4, -1) : "255, 255, 255"
}, 0) 100%)"] { background-image: linear-gradient(to right,
var(--theme--bg-primary, ${primary.value}) 20%, transparent
100%) !important; }
`;
// hovered elements, inputs and unchecked toggle backgrounds
overrideStyle({
property: "background",
variable: cssVariable({
name: "bg-hover",
value: darkMode ? "rgba(255, 255, 255, 0.055)" : "rgba(55, 53, 47, 0.08)",
}),
valueAliases: darkMode
? []
: [
"rgba(242, 241, 238, 0.6)",
"rgb(225, 225, 225)",
"rgb(239, 239, 238)",
],
postProcessor(selector, cssProps) {
selector += `, ${bodySelector} [style*="height: 14px; width: 26px; border-radius: 44px;"][style*="rgba"]`;
if (darkMode) {
selector += `, ${bodySelector} :is([style*="background: rgb(47, 47, 47)"],
[style*="background-color: rgb(47, 47, 47)"])[style*="transition: background"]:hover`;
}
return [selector, cssProps];
},
});
// modal shadow
overrideStyle({
selector: `.notion-overlay-container [data-overlay]
> div > [style*="position: absolute"]:first-child`,
property: "background",
variable: cssVariable({
name: "bg-overlay",
value: darkMode ? "rgba(15, 15, 15, 0.8)" : "rgba(15, 15, 15, 0.6)",
}),
specificity: ["mode"],
});
};
const styleColoredBackgrounds = () => {
for (const targetSelector of [
// database tags
`[style*="height: 20px; border-radius: 3px; padding-left: 6px;"]`,
`.notion-collection_view-block [style*="height: 14px; border-radius: 3px; padding-left: 6px;"]`,
`:is(.notion-timeline-item-properties [style*="height: 18px; border-radius: 3px; padding-left: 8px;"],
.notion-collection_view-block .notion-collection-item a > .notion-focusable)`,
// inline highlights
`.notion-selectable .notion-enable-hover[style*="background:"]`,
// block highlights and hovered board items
`:is(.notion-text-block > [style*="background:"],
.notion-collection_view-block .notion-collection-item a > .notion-focusable)`,
]) {
for (const el of document.querySelectorAll(targetSelector)) {
if (!el.innerText || /\s/.test(el.innerText)) continue;
overrideStyle({
element: el,
selector: targetSelector,
property: "background",
variable: `bg-${el.innerText}`,
});
}
}
// board cards
for (const group of document.querySelectorAll(
".notion-board-view .notion-board-group"
)) {
const card = group.querySelector('a[style*="background"]'),
innerText = card.innerText.replace("Drag image to reposition\n", "");
if (!innerText || /\s/.test(innerText)) continue;
const groupStyle = group
.getAttribute("style")
.match(/background(?:-color)?:\s*([^;]+);?/)[1];
// in light mode pages in board views all have bg "white"
// by default, must be styled based on parent
overrideStyle({
element: card,
selector: `.notion-board-view .notion-board-group[style*="${groupStyle}"] a`,
property: "background",
variable: `bg-${innerText}`,
specificity: ["mode"],
});
overrideStyle({
element: group,
selector: `.notion-board-view [style*="${groupStyle}"]:is(
.notion-board-group,
[style*="border-top-left-radius: 5px;"]
)`,
property: "background",
variable: `dim-${innerText}`,
specificity: ["mode"],
});
}
// use dim for callout blocks
for (const el of document.querySelectorAll(
'.notion-callout-block > div > [style*="background:"]'
)) {
if (!el.innerText || /\s/.test(el.innerText)) continue;
overrideStyle({
element: el,
selector: ".notion-callout-block > div > div",
property: "background",
variable: `dim-${el.innerText}`,
});
}
// use yellow for notification highlights
overrideStyle({
property: "background",
variable: cssVariable({
name: "bg-yellow",
value: "rgba(255, 212, 0, 0.14)",
}),
specificity: ["value"],
});
// use light gray for taglikes e.g. file property values
overrideStyle({
selector: `[style*="height: 18px; border-radius: 3px; background"]`,
property: "background",
variable: "bg-light_gray",
});
};
const styleTooltips = () => {
cssBody += `.notion-overlay-container [style*="border-radius: 3px; background:"
][style*="max-width: calc(100vw - 24px); box-shadow:"
][style*="padding: 4px 8px; font-size: 12px; line-height: 1.4; font-weight: 500;"] {
background: rgb(15, 15, 15) !important;
color: rgba(255, 255, 255, 0.9) !important;
}
.notion-overlay-container [style*="border-radius: 3px; background:"
][style*="max-width: calc(100vw - 24px); box-shadow:"
][style*="padding: 4px 8px; font-size: 12px; line-height: 1.4; font-weight: 500;"]
> [style*="color"] { color: rgb(127, 127, 127) !important; }`;
};
const styleAccents = () => {
const primary = cssVariable({
name: "accent-primary",
value: "rgb(35, 131, 226)",
}),
primaryHover = cssVariable({
name: "accent-primary_hover",
value: "rgb(0, 117, 211)",
}),
primaryContrast = cssVariable({
name: "accent-primary_contrast",
value: "rgb(255, 255, 255)",
}),
primaryTransparent = cssVariable({
name: "accent-primary_transparent",
value: "rgba(35, 131, 226, 0.14)",
});
overrideStyle({
property: "color",
variable: primary,
specificity: ["value"],
});
overrideStyle({
property: "background",
variable: primary,
specificity: ["value"],
cssProps: {
fill: primaryContrast,
color: primaryContrast,
},
});
overrideStyle({
property: "background",
variable: primaryHover,
specificity: ["value"],
cssProps: {
fill: primaryContrast,
color: primaryContrast,
},
});
overrideStyle({
selector: `.notion-table-selection-overlay [style*="border: 2px solid"]`,
property: "border-color",
variable: primary,
specificity: [],
});
overrideStyle({
selector: `
[style*="background: ${primary.value}"] svg[style*="fill"],
[style*="background-color: ${primary.value}"] svg[style*="fill"]
`,
property: "fill",
variable: primaryContrast,
specificity: [],
});
overrideStyle({
selector: `[style*="border-radius: 44px;"] > [style*="border-radius: 44px; background: white;"]`,
property: "background",
variable: primaryContrast,
specificity: [],
});
overrideStyle({
selector: `
*::selection,
.notion-selectable-halo,
#notion-app .rdp-day:not(.rdp-day_disabled):not(.rdp-day_selected
):not(.rdp-day_value):not(.rdp-day_start):not(.rdp-day_end):hover,
[style*="background: ${primaryTransparent.value.split(".")[0]}."],
[style*="background:${primaryTransparent.value.split(".")[0]}."],
[style*="background-color: ${primaryTransparent.value.split(".")[0]}."],
[style*="background-color:${primaryTransparent.value.split(".")[0]}."]
`,
property: "background",
variable: primaryTransparent,
specificity: [],
});
const secondary = cssVariable({
name: "accent-secondary",
value: "rgb(235, 87, 87)",
}),
secondaryAliases = [
"rgb(180, 65, 60)",
"rgb(211, 79, 67)",
"rgb(205, 73, 69)",
],
secondaryHover = cssVariable({
name: "accent-secondary_hover",
value: "rgba(235, 87, 87, 0.1)",
}),
secondaryContrast = cssVariable({
name: "accent-secondary_contrast",
value: "white",
});
overrideStyle({
property: "color",
variable: secondary,
valueAliases: secondaryAliases,
specificity: ["value"],
});
overrideStyle({
property: "background",
variable: secondary,
valueAliases: secondaryAliases,
specificity: ["value"],
cssProps: {
fill: secondaryContrast,
color: secondaryContrast,
},
postProcessor(selector, cssProps) {
return [
`#notion-app .rdp-day_today:not(.rdp-day_selected):not(.rdp-day_value
):not(.rdp-day_start):not(.rdp-day_end)::after, ${selector}`,
cssProps,
];
},
});
overrideStyle({
property: "background",
variable: secondary,
valueAliases: secondaryAliases,
specificity: ["value"],
cssProps: {
fill: secondaryContrast,
color: secondaryContrast,
},
postProcessor(selector, cssProps) {
delete cssProps["background"];
return [
`#notion-app .rdp-day_today:not(.rdp-day_selected):not(.rdp-day_value):not(.rdp-day_start
):not(.rdp-day_end), :is(${selector}) + :is([style*="fill: ${secondaryContrast.value};"],
[style*="color: ${secondaryContrast.value};"]), :is(${selector})
:is([style*="fill: ${secondaryContrast.value};"], [style*="color: ${secondaryContrast.value};"])`,
cssProps,
];
},
});
overrideStyle({
property: "background",
variable: secondaryHover,
specificity: ["value"],
});
// box-shadows are complicated, style manually
cssBody += `.notion-focusable-within:focus-within {
box-shadow:
var(--theme--accent-primary, ${primary.value}) 0px 0px 0px 1px inset,
var(--theme--accent-primary, ${primary.value}) 0px 0px 0px 2px
!important;
}
.notion-focusable:focus-visible {
box-shadow:
var(--theme--accent-primary, ${primary.value}) 0px 0px 0px 1px inset,
var(--theme--accent-primary, ${primary.value}) 0px 0px 0px 2px
!important;
}
${["box-shadow: rgb(35, 131, 226) 0px 0px 0px 2px inset"]
.map((shadow) => {
return `[style*="${shadow}"] { ${shadow.replace(
/rgba?\([^\)]+\)/g,
`var(--theme--accent-primary, ${primary.value})`
)} !important; }`;
})
.join("")}
${[
"border: 1px solid rgb(110, 54, 48)",
"border: 1px solid rgba(235, 87, 87, 0.5)",
"border: 2px solid rgb(110, 54, 48)",
"border: 2px solid rgb(227, 134, 118)",
"border-right: 1px solid rgb(180, 65, 60)",
"border-right: 1px solid rgb(211, 79, 67)",
]
.map((border) => `[style*="${border}"]`)
.join(", ")} { border-color: ${secondary.ref}; }`;
};
const styleScrollbars = () => {
const scrollbarTrack = cssVariable({
name: "scrollbar-track",
value: darkMode ? "rgba(202, 204, 206, 0.04)" : "#EDECE9",
});
overrideStyle({
selector: "::-webkit-scrollbar-track",
property: "background",
variable: scrollbarTrack,
specificity: ["mode"],
});
overrideStyle({
selector: "::-webkit-scrollbar-corner",
property: "background",
variable: scrollbarTrack,
specificity: ["mode"],
});
overrideStyle({
selector: "::-webkit-scrollbar-thumb",
property: "background",
variable: cssVariable({
name: "scrollbar-thumb",
value: darkMode ? "#474c50" : "#D3D1CB",
}),
specificity: ["mode"],
});
overrideStyle({
selector: "::-webkit-scrollbar-thumb:hover",
property: "background",
variable: cssVariable({
name: "scrollbar-thumb_hover",
value: darkMode ? "rgba(202, 204, 206, 0.3)" : "#AEACA6",
}),
specificity: ["mode"],
});
};
const styleCode = () => {
overrideStyle({
selector: `.notion-text-block .notion-enable-hover[style*="mono"]`,
property: "color",
variable: "code-inline_fg",
});
overrideStyle({
selector: `.notion-text-block .notion-enable-hover[style*="mono"]`,
property: "background",
variable: "code-inline_bg",
});
overrideStyle({
selector: `.notion-code-block > [style*="mono"]`,
property: "color",
variable: "code-block_fg",
});
overrideStyle({
selector: `.notion-code-block > div > [style*="background"]`,
property: "background",
variable: "code-block_bg",
});
const aliases = {},
code = document.querySelector(".notion-code-block .token");
for (const token of [
// standard tokens from https://prismjs.com/tokens.html
"keyword",
"builtin",
"class-name",
"function",
"boolean",
"number",
"string",
"char",
"symbol",
"regex",
"url",
"operator",
"variable",
"constant",
"property",
"punctuation",
"important",
"comment",
"tag",
"attr-name",
"attr-value",
"namespace",
"prolog",
"doctype",
"cdata",
"entity",
"atrule",
"selector",
"inserted",
"deleted",
]) {
code.className = `token ${token}`;
overrideStyle({
target: code,
selector: `.notion-code-block .token.${token}`,
property: "color",
variable: `code-${token.replace(/-/g, "_")}`,
variableAliases: aliases,
specificity: ["mode"],
});
}
// patch: remove individual backgrounds from prism tokens
cssBody += `.token:is(
.operator, .entity, .url,
:is(.language-css, .style) .string
) { background: transparent !important; }`;
};
styleText();
styleBorders();
styleColoredText();
styleBackgrounds();
styleColoredBackgrounds();
styleTooltips();
styleAccents();
styleScrollbars();
styleCode();
console.log(
`body${modeSelector} { ${cssRoot} } ${Object.entries(cssRefs)
.map(([body, selectors]) => `${[...new Set(selectors)].join(",")}{${body}}`)
.join("")} ${cssBody}`.replace(/\s+/g, " ")
);

76
scripts/patch-desktop-app.mjs Executable file
View File

@ -0,0 +1,76 @@
/**
* notion-enhancer
* (c) 2023 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
* (https://notion-enhancer.github.io/) under the MIT license
*/
// patch scripts within notion's sources to
// activate and respond to the notion-enhancer
const injectTriggerOnce = (file, content) =>
content +
(!/require\(['|"]notion-enhancer['|"]\)/.test(content)
? `\n\nrequire("notion-enhancer")("${file}",exports,(js)=>eval(js));`
: ""),
replaceIfNotFound = ({ string, mode = "replace" }, search, replacement) =>
string.includes(replacement)
? string
: string.replace(
search,
typeof replacement === "string" && mode === "append"
? `$&${replacement}`
: typeof replacement === "string" && mode === "prepend"
? `${replacement}$&`
: replacement
);
const patches = {
// prettier-ignore
".webpack/main/index.js": (file, content) => {
content = injectTriggerOnce(file, content);
const replace = (...args) =>
(content = replaceIfNotFound(
{ string: content, mode: "replace" },
...args
)),
prepend = (...args) =>
(content = replaceIfNotFound(
{ string: content, mode: "prepend" },
...args
)),
append = (...args) =>
(content = replaceIfNotFound(
{ string: content, mode: "append" },
...args
));
// https://github.com/notion-enhancer/notion-enhancer/issues/160:
// run the app in windows mode on linux (instead of macos mode)
const isWindows =
/(?:"win32"===process\.platform(?:(?=,isFullscreen)|(?=&&\w\.BrowserWindow)|(?=&&\(\w\.app\.requestSingleInstanceLock)))/g,
isWindowsOrLinux = '["win32","linux"].includes(process.platform)';
replace(isWindows, isWindowsOrLinux);
// restore node integration in the renderer process
// so the notion-enhancer can be require()-d into it
replace(/sandbox:!0/g, `sandbox:!1,nodeIntegration:!0,session:require('electron').session.fromPartition("persist:notion")`);
// expose the app's config + cache + preferences to the global namespace
// e.g. to enable development mode or check if keep in background is enabled
prepend(/\w\.exports=JSON\.parse\('\{"env":"production"/, "globalThis.__notionConfig=");
prepend(/\w\.updatePreferences=\w\.updatePreferences/, "globalThis.__updatePreferences=");
prepend(/\w\.Store=\(0,\w\.configureStore\)/, "globalThis.__notionStore=");
return content;
},
".webpack/renderer/tabs/preload.js": injectTriggerOnce,
".webpack/renderer/tab_browser_view/preload.js": injectTriggerOnce,
};
const decoder = new TextDecoder(),
encoder = new TextEncoder();
export default (file, content) => {
if (!patches[file]) return content;
content = decoder.decode(content);
content = patches[file](file, content);
return encoder.encode(content);
};

View File

@ -0,0 +1,55 @@
/**
* notion-enhancer
* (c) 2023 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
* (https://notion-enhancer.github.io/) under the MIT license
*/
import fsp from "node:fs/promises";
import { existsSync } from "node:fs";
import { resolve } from "node:path";
import { fileURLToPath } from "node:url";
const esmVersion = "135",
esTarget = "es2022",
esmBundle = ({ name, version, path = "", exports = [] }) => {
const scopedName = name;
if (name.startsWith("@")) name = name.split("/")[1];
path ||= `${name}.bundle.mjs`;
let bundleSrc = `https://esm.sh/v${esmVersion}/${scopedName}@${version}/${esTarget}/${path}`;
if (exports.length) bundleSrc += `?bundle&exports=${exports.join()}`;
return { [`${scopedName.replace(/\//g, "-")}.mjs`]: bundleSrc };
};
const uno = "0.59.4",
coloris = "https://cdn.jsdelivr.net/gh/mdbassit/coloris@v0.24.0/dist",
dependencies = {
...esmBundle({ name: "htm", version: "3.1.1" }),
...esmBundle({
name: "lucide",
version: "0.372.0",
path: "dist/umd/lucide.mjs",
}),
...esmBundle({
name: "@unocss/core",
version: uno,
exports: ["createGenerator", "expandVariantGroup"],
}),
...esmBundle({
name: "@unocss/preset-uno",
version: uno,
exports: ["presetUno"],
}),
"@unocss-preflight-tailwind.css": `https://esm.sh/@unocss/reset@${uno}/tailwind.css`,
"coloris.min.js": `${coloris}/coloris.min.js`,
"coloris.min.css": `${coloris}/coloris.min.css`,
};
const output = fileURLToPath(new URL("../src/vendor", import.meta.url)),
write = (file, data) => fsp.writeFile(resolve(`${output}/${file}`), data);
if (existsSync(output)) await fsp.rm(output, { recursive: true });
await fsp.mkdir(output);
for (const file in dependencies) {
const source = dependencies[file],
res = await (await fetch(source)).text();
await write(file, res);
}

307
src/api/interface.mjs Normal file
View File

@ -0,0 +1,307 @@
/**
* notion-enhancer
* (c) 2024 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
* (https://notion-enhancer.github.io/) under the MIT license
*/
import htm from "../vendor/htm.mjs";
import lucide from "../vendor/lucide.mjs";
import {
createGenerator,
expandVariantGroup,
} from "../vendor/@unocss-core.mjs";
import { presetUno } from "../vendor/@unocss-preset-uno.mjs";
import "../assets/icons.svg.js";
// prettier-ignore
// https://developer.mozilla.org/en-US/docs/Web/SVG/Element
// https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute
// https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes
const svgElements = ["animate","animateMotion","animateTransform","circle","clipPath","defs","desc","discard","ellipse","feBlend","feColorMatrix","feComponentTransfer","feComposite","feConvolveMatrix","feDiffuseLighting","feDisplacementMap","feDistantLight","feDropShadow","feFlood","feFuncA","feFuncB","feFuncG","feFuncR","feGaussianBlur","feImage","feMerge","feMergeNode","feMorphology","feOffset","fePointLight","feSpecularLighting","feSpotLight","feTile","feTurbulence","filter","foreignObject","g","hatch","hatchpath","image","line","linearGradient","marker","mask","metadata","mpath","path","pattern","polygon","polyline","radialGradient","rect","script","set","stop","style","svg","switch","symbol","text","textPath","title","tspan","use","view"],
htmlAttributes = ["accept","accept-charset","accesskey","action","align","allow","alt","async","autocapitalize","autocomplete","autofocus","autoplay","background","bgcolor","border","buffered","capture","challenge","charset","checked","cite","class","code","codebase","color","cols","colspan","content","contenteditable","contextmenu","controls","coords","crossorigin","csp","data","data-*","datetime","decoding","default","defer","dir","dirname","disabled","download","draggable","enctype","enterkeyhint","for","form","formaction","formenctype","formmethod","formnovalidate","formtarget","headers","height","hidden","high","href","hreflang","http-equiv","icon","id","importance","integrity","inputmode","ismap","itemprop","keytype","kind","label","lang","loading","list","loop","low","max","maxlength","minlength","media","method","min","multiple","muted","name","novalidate","open","optimum","pattern","ping","placeholder","playsinline","poster","preload","radiogroup","readonly","referrerpolicy","rel","required","reversed","role","rows","rowspan","sandbox","scope","selected","shape","size","sizes","slot","span","spellcheck","src","srcdoc","srclang","srcset","start","step","style","tabindex","target","title","translate","type","usemap","value","width","wrap","accent-height","accumulate","additive","alignment-baseline","alphabetic","amplitude","arabic-form","ascent","attributeName","attributeType","azimuth","baseFrequency","baseline-shift","baseProfile","bbox","begin","bias","by","calcMode","cap-height","clip","clipPathUnits","clip-path","clip-rule","color-interpolation","color-interpolation-filters","color-profile","color-rendering","contentScriptType","contentStyleType","cursor","cx","cy","d","decelerate","descent","diffuseConstant","direction","display","divisor","dominant-baseline","dur","dx","dy","edgeMode","elevation","enable-background","end","exponent","fill","fill-opacity","fill-rule","filter","filterRes","filterUnits","flood-color","flood-opacity","font-family","font-size","font-size-adjust","font-stretch","font-style","font-variant","font-weight","format","from","fr","fx","fy","g1","g2","glyph-name","glyph-orientation-horizontal","glyph-orientation-vertical","glyphRef","gradientTransform","gradientUnits","hanging","horiz-adv-x","horiz-origin-x","ideographic","image-rendering","in","in2","intercept","k","k1","k2","k3","k4","kernelMatrix","kernelUnitLength","kerning","keyPoints","keySplines","keyTimes","lengthAdjust","letter-spacing","lighting-color","limitingConeAngle","local","marker-end","marker-mid","marker-start","markerHeight","markerUnits","markerWidth","mask","maskContentUnits","maskUnits","mathematical","mode","numOctaves","offset","opacity","operator","order","orient","orientation","origin","overflow","overline-position","overline-thickness","panose-1","paint-order","path","pathLength","patternContentUnits","patternTransform","patternUnits","pointer-events","points","pointsAtX","pointsAtY","pointsAtZ","preserveAlpha","preserveAspectRatio","primitiveUnits","r","radius","referrerPolicy","refX","refY","rendering-intent","repeatCount","repeatDur","requiredExtensions","requiredFeatures","restart","result","rotate","rx","ry","scale","seed","shape-rendering","slope","spacing","specularConstant","specularExponent","speed","spreadMethod","startOffset","stdDeviation","stemh","stemv","stitchTiles","stop-color","stop-opacity","strikethrough-position","strikethrough-thickness","string","stroke","stroke-dasharray","stroke-dashoffset","stroke-linecap","stroke-linejoin","stroke-miterlimit","stroke-opacity","stroke-width","surfaceScale","systemLanguage","tableValues","targetX","targetY","text-anchor","text-decoration","text-rendering","textLength","to","transform","transform-origin","u1","u2","underline-position","underline-thickness","unicode","unicode-bidi","unicode-range","units-per-em","v-alphabetic","v-hanging","v-ideographic","v-mathematical","values","vector-effect","version","vert-adv-y","vert-origin-x","vert-origin-y","viewBox","viewTarget","visibility","widths","word-spacing","writing-mode","x","x-height","x1","x2","xChannelSelector","xlink:actuate","xlink:arcrole","xlink:href","xlink:role","xlink:show","xlink:title","xlink:type","xml:base","xml:lang","xml:space","y","y1","y2","yChannelSelector","z","zoomAndPan"];
// accelerators approximately match electron accelerators.
// logic used when recording hotkeys in menu matches logic used
// when triggering hotkeys => detection should be reliable.
// default hotkeys using "alt" may trigger an altcode or
// accented character on some keyboard layouts (not recommended).
let keyListeners = [];
const modifierAliases = [
["metaKey", ["meta", "os", "win", "cmd", "command"]],
["ctrlKey", ["ctrl", "control"]],
["shiftKey", ["shift"]],
["altKey", ["alt"]],
],
addKeyListener = (accelerator, callback, waitForKeyup = false) => {
if (typeof accelerator === "string") accelerator = accelerator.split("+");
accelerator = accelerator.map((key) => key.toLowerCase());
keyListeners.push([accelerator, callback, waitForKeyup]);
},
removeKeyListener = (callback) => {
keyListeners = keyListeners.filter(([, c]) => c !== callback);
},
handleKeypress = (event, keyListeners) => {
for (const [accelerator, callback] of keyListeners) {
const acceleratorModifiers = [],
combinationTriggered =
accelerator.every((key) => {
for (const [modifier, aliases] of modifierAliases) {
if (aliases.includes(key)) {
acceleratorModifiers.push(modifier);
return true;
}
}
if (key === "space") key = " ";
if (key === "plus") key = "equal";
if (key === "minus") key = "-";
if (key === "\\") key = "backslash";
if (key === ",") key = "comma";
if (key === ".") key = "period";
const keyPressed = [
event.key.toLowerCase(),
event.code.toLowerCase(),
].includes(key);
return keyPressed;
}) &&
modifierAliases.every(([modifier]) => {
// required && used -> matches accelerator
// !required && !used -> matches accelerator
// (required && !used) || (!required && used) -> no match
// differentiates e.g.ctrl + x from ctrl + shift + x
return acceleratorModifiers.includes(modifier) === event[modifier];
});
if (combinationTriggered) callback(event);
}
},
onKeyup = (event) => {
const keyupListeners = keyListeners //
.filter(([, , waitForKeyup]) => waitForKeyup);
handleKeypress(event, keyupListeners);
},
onKeydown = (event) => {
const keydownListeners = keyListeners //
.filter(([, , waitForKeyup]) => !waitForKeyup);
handleKeypress(event, keydownListeners);
};
document.removeEventListener("keyup", onKeyup);
document.removeEventListener("keydown", onKeydown);
document.addEventListener("keyup", onKeyup);
document.addEventListener("keydown", onKeydown);
// mutation listeners observe updates to the dom.
// by default, the criteria for matching a selector
// is very broad. custom opts can be passed when
// adding a listener to reduce handler calls
let documentObserver,
observerDefaults = {
// whether to observe attribute updates
attributes: true,
// whether to observe innerText updates
characterData: true,
// whether to observe added/removed nodes
childList: true,
// whether to observe nested nodes
subtree: true,
},
mutationListeners = [];
const _mutations = [],
addMutationListener = (selector, callback, opts) => {
opts = { ...observerDefaults, ...opts };
mutationListeners.push([selector, callback, opts]);
},
removeMutationListener = (callback) => {
mutationListeners = mutationListeners.filter(([, c]) => c !== callback);
},
selectorMutated = (mutation, selector, opts) => {
if (!opts.attributes && mutation.type === "attributes") return false;
if (!opts.characterData && mutation.type === "characterData") return false;
const target =
mutation.type === "characterData"
? mutation.target.parentElement
: mutation.target;
if (!target) return false;
const matchesTarget = target.matches(selector),
matchesParent = opts.subtree && target.matches(`${selector} *`),
matchesChild = opts.subtree && target.querySelector(selector),
matchesAdded =
opts.childList &&
[...(mutation.addedNodes || [])].some((node) => {
if (!(node instanceof HTMLElement)) node = node.parentElement;
return node?.querySelector(selector);
});
return matchesTarget || matchesParent || matchesChild || matchesAdded;
},
handleMutations = () => {
let mutation;
while ((mutation = _mutations.shift())) {
for (const [selector, callback, subtree] of mutationListeners)
if (selectorMutated(mutation, selector, subtree)) callback(mutation);
}
},
attachObserver = () => {
if (document.readyState !== "complete") return;
document.removeEventListener("readystatechange", attachObserver);
(documentObserver ??= new MutationObserver((mutations, _observer) => {
if (!_mutations.length) requestIdleCallback(handleMutations);
_mutations.push(...mutations);
})).disconnect();
documentObserver.observe(document.body, observerDefaults);
};
document.addEventListener("readystatechange", attachObserver);
attachObserver();
const kebabToPascalCase = (string) =>
string[0].toUpperCase() +
string.replace(/-[a-z]/g, (match) => match.slice(1).toUpperCase()).slice(1),
hToString = (type, props, ...children) =>
`<${type}${Object.entries(props)
.map(([attr, value]) => ` ${attr}="${value}"`)
.join("")}>${children
.map((child) => (Array.isArray(child) ? hToString(...child) : child))
.join("")}</${type}>`,
// combines instance-provided element props
// with a template of element props such that
// island/component/template props handlers
// and styles can be preserved and extended
// rather than overwritten
extendProps = (props, extend) => {
for (const key in extend) {
const { [key]: value } = props;
if (typeof extend[key] === "function") {
props[key] = (...args) => {
extend[key](...args);
if (typeof value === "function") value(...args);
};
} else if (key === "class") {
props[key] = value ? `${value} ${extend[key]}` : extend[key];
} else props[key] = extend[key] ?? value;
}
return props;
},
// enables use of the jsx-like htm syntax
// for building components and interfaces
// with tagged templates. instantiates dom
// elements directly, does not use a vdom.
// e.g. html`<div class=${className}></div>`
h = function (type, props, ...children) {
// disables element caching
this[0] = 3;
children = children.flat(Infinity);
if (typeof type === "function") {
// html`<${Component} attr="value">Click Me<//>`
return type(props ?? {}, ...children);
}
const elem = svgElements.includes(type)
? document.createElementNS("http://www.w3.org/2000/svg", type)
: document.createElement(type);
for (const prop in props ?? {}) {
if (typeof props[prop] === "undefined") continue;
if (["class", "className"].includes(prop)) {
// collapse multiline classes &
// expand utility variant class groups
props[prop] = props[prop].replace(/\s+/g, " ");
props[prop] = expandVariantGroup(props[prop]).trim();
elem.setAttribute("un-cloak", "");
}
if (htmlAttributes.includes(prop) || prop.includes("-")) {
if (typeof props[prop] === "boolean") {
if (!props[prop]) continue;
elem.setAttribute(prop, "");
} else elem.setAttribute(prop, props[prop]);
} else elem[prop] = props[prop];
}
if (type === "style") {
elem.append(children.join("").replace(/\s+/g, " "));
} else elem.append(...children);
return elem;
},
html = htm.bind(h);
const iconPattern = /^i-((?:\w|-)+)(?:\?(mask|bg|auto))?$/,
svgToUri = (svg) => {
// https://gist.github.com/jennyknuth/222825e315d45a738ed9d6e04c7a88d0
const xlmns = ~svg.indexOf("xmlns")
? "<svg"
: '<svg xmlns="http://www.w3.org/2000/svg"';
return `url("data:image/svg+xml;utf8,${svg
.replace("<svg", xlmns)
.replace(/"/g, "'")
.replace(/%/g, "%25")
.replace(/#/g, "%23")
.replace(/{/g, "%7B")
.replace(/}/g, "%7D")
.replace(/</g, "%3C")
.replace(/>/g, "%3E")
.replace(/\s+/g, " ")
.trim()}")`;
},
// prefer custom preset over @unocss/preset-icons:
// limits icons to single set, avoids loading over
// cdn (otherwise could cause issues when submitting
// to the chrome webstore). also makes custom icon
// handling straightforward
presetIcons = ([, icon, mode]) => {
let svg,
mask = mode === "mask";
if (icon === "notion-enhancer") {
const { iconColour, iconMonochrome } = globalThis.__enhancerApi;
svg = mask ? iconMonochrome : iconColour;
} else {
icon = kebabToPascalCase(icon);
if (!lucide[icon]) return;
const [type, props, children] = lucide[icon];
svg = hToString(type, props, ...children);
}
mask ||= mode !== "bg" && svg.includes("currentColor");
return {
// https://antfu.me/posts/icons-in-pure-css
display: "inline-block",
height: "1em",
width: "1em",
[mask ? "mask" : "background"]: `${svgToUri(svg)} no-repeat`,
[mask ? "mask-size" : "background-size"]: "100% 100%",
"background-color": mask ? "currentColor" : "transparent",
};
};
let _renderedTokens = -1;
const _tokens = new Set(),
_stylesheet = html`<style id="__unocss"></style>`,
preflight = `[un-cloak]{display:none!important}
.notion-emoji{display:inline-block!important}`,
uno = createGenerator({
presets: [presetUno()],
preflights: [{ getCSS: () => preflight }],
rules: [[iconPattern, presetIcons, { layer: "icons" }]],
layers: { preflights: -2, icons: -1, default: 1 },
}),
extractTokens = ($root) => {
if (!$root?.classList) return;
for (const t of $root.classList) _tokens.add(t);
for (const $ of $root.children) extractTokens($);
$root.removeAttribute("un-cloak");
},
renderStylesheet = async () => {
if (_renderedTokens === _tokens.size) return;
_renderedTokens = _tokens.size;
const res = await uno.generate(_tokens);
if (!document.contains(_stylesheet)) document.head.append(_stylesheet);
if (_stylesheet.innerHTML !== res.css) _stylesheet.innerHTML = res.css;
};
addMutationListener("*", (mutation) => {
if (mutation.type === "childList") {
for (const node of mutation.addedNodes) extractTokens(node);
} else if (mutation.type === "attributes") extractTokens(mutation.target);
else return;
renderStylesheet();
});
renderStylesheet();
Object.assign((globalThis.__enhancerApi ??= {}), {
html,
extendProps,
addKeyListener,
removeKeyListener,
addMutationListener,
removeMutationListener,
});

367
src/api/notion.js Normal file
View File

@ -0,0 +1,367 @@
/**
* notion-enhancer: api
* (c) 2021 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
* (https://notion-enhancer.github.io/) under the MIT license
*/
'use strict';
/**
* a basic wrapper around notion's content apis
* @namespace notion
*/
import { web, fs, fmt } from './index.mjs';
const standardiseUUID = (uuid) => {
if (uuid?.length === 32 && !uuid.includes('-')) {
uuid = uuid.replace(
/([\d\w]{8})([\d\w]{4})([\d\w]{4})([\d\w]{4})([\d\w]{12})/,
'$1-$2-$3-$4-$5'
);
}
return uuid;
};
/**
* unofficial content api: get a block by id
* (requires user to be signed in or content to be public).
* why not use the official api?
* 1. cors blocking prevents use on the client
* 2. the majority of blocks are still 'unsupported'
* @param {string} id - uuidv4 record id
* @param {string=} table - record type (default: 'block').
* may also be 'collection', 'collection_view', 'space', 'notion_user', 'discussion', or 'comment'
* @returns {Promise<object>} record data. type definitions can be found here:
* https://github.com/NotionX/react-notion-x/tree/master/packages/notion-types/src
*/
export const get = async (id, table = 'block') => {
id = standardiseUUID(id);
const json = await fs.getJSON('https://www.notion.so/api/v3/getRecordValues', {
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ requests: [{ table, id }] }),
method: 'POST',
});
return json?.results?.[0]?.value || json;
};
/**
* get the id of the current user (requires user to be signed in)
* @returns {string} uuidv4 user id
*/
export const getUserID = () =>
JSON.parse(localStorage['LRU:KeyValueStore2:current-user-id'] || {}).value;
/**
* get the id of the currently open page
* @returns {string} uuidv4 page id
*/
export const getPageID = () =>
standardiseUUID(
web.queryParams().get('p') || location.pathname.split(/(-|\/)/g).reverse()[0]
);
let _spaceID;
/**
* get the id of the currently open workspace (requires user to be signed in)
* @returns {string} uuidv4 space id
*/
export const getSpaceID = async () => {
if (!_spaceID) _spaceID = (await get(getPageID())).space_id;
return _spaceID;
};
/**
* unofficial content api: search all blocks in a space
* (requires user to be signed in or content to be public).
* why not use the official api?
* 1. cors blocking prevents use on the client
* 2. the majority of blocks are still 'unsupported'
* @param {string=} query - query to search blocks in the space for
* @param {number=} limit - the max number of results to return (default: 20)
* @param {string=} spaceID - uuidv4 workspace id
* @returns {object} the number of total results, the list of matches, and related record values.
* type definitions can be found here: https://github.com/NotionX/react-notion-x/blob/master/packages/notion-types/src/api.ts
*/
export const search = async (query = '', limit = 20, spaceID = getSpaceID()) => {
spaceID = standardiseUUID(await spaceID);
const json = await fs.getJSON('https://www.notion.so/api/v3/search', {
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
type: 'BlocksInSpace',
query,
spaceId: spaceID,
limit,
filters: {
isDeletedOnly: false,
excludeTemplates: false,
isNavigableOnly: false,
requireEditPermissions: false,
ancestors: [],
createdBy: [],
editedBy: [],
lastEditedTime: {},
createdTime: {},
},
sort: 'Relevance',
source: 'quick_find',
}),
method: 'POST',
});
return json;
};
/**
* unofficial content api: update a property/the content of an existing record
* (requires user to be signed in or content to be public).
* TEST THIS THOROUGHLY. misuse can corrupt a record, leading the notion client
* to be unable to parse and render content properly and throw errors.
* why not use the official api?
* 1. cors blocking prevents use on the client
* 2. the majority of blocks are still 'unsupported'
* @param {object} pointer - the record being updated
* @param {object} recordValue - the new raw data values to set to the record.
* for examples, use notion.get to fetch an existing block record.
* to use this to update content, set pointer.path to ['properties', 'title]
* and recordValue to an array of rich text segments. a segment is an array
* where the first value is the displayed text and the second value
* is an array of decorations. a decoration is an array where the first value
* is a modifier and the second value specifies it. e.g.
* [
* ['bold text', [['b']]],
* [' '],
* ['an italicised link', [['i'], ['a', 'https://github.com']]],
* [' '],
* ['highlighted text', [['h', 'pink_background']]],
* ]
* more examples can be creating a block with the desired content/formatting,
* then find the value of blockRecord.properties.title using notion.get.
* type definitions can be found here: https://github.com/NotionX/react-notion-x/blob/master/packages/notion-types/src/core.ts
* @param {string} pointer.recordID - uuidv4 record id
* @param {string=} pointer.recordTable - record type (default: 'block').
* may also be 'collection', 'collection_view', 'space', 'notion_user', 'discussion', or 'comment'
* @param {string=} pointer.property - the record property to update.
* for record content, it will be the default: 'title'.
* for page properties, it will be the property id (the key used in pageRecord.properties).
* other possible values are unknown/untested
* @param {string=} pointer.spaceID - uuidv4 workspace id
* @param {string=} pointer.path - the path to the key to be set within the record
* (default: [], the root of the record's values)
* @returns {boolean|object} true if success, else an error object
*/
export const set = async (
{ recordID, recordTable = 'block', spaceID = getSpaceID(), path = [] },
recordValue = {}
) => {
spaceID = standardiseUUID(await spaceID);
recordID = standardiseUUID(recordID);
const json = await fs.getJSON('https://www.notion.so/api/v3/saveTransactions', {
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
requestId: fmt.uuidv4(),
transactions: [
{
id: fmt.uuidv4(),
spaceId: spaceID,
operations: [
{
pointer: {
table: recordTable,
id: recordID,
spaceId: spaceID,
},
path,
command: path.length ? 'set' : 'update',
args: recordValue,
},
],
},
],
}),
method: 'POST',
});
return json.errorId ? json : true;
};
/**
* unofficial content api: create and add a new block to a page
* (requires user to be signed in or content to be public).
* TEST THIS THOROUGHLY. misuse can corrupt a record, leading the notion client
* to be unable to parse and render content properly and throw errors.
* why not use the official api?
* 1. cors blocking prevents use on the client
* 2. the majority of blocks are still 'unsupported'
* @param {object} insert - the new record.
* @param {object} pointer - where to insert the new block
* for examples, use notion.get to fetch an existing block record.
* type definitions can be found here: https://github.com/NotionX/react-notion-x/blob/master/packages/notion-types/src/block.ts
* may also be 'collection', 'collection_view', 'space', 'notion_user', 'discussion', or 'comment'
* @param {object=} insert.recordValue - the new raw data values to set to the record.
* @param {object=} insert.recordTable - record type (default: 'block').
* may also be 'collection', 'collection_view', 'space', 'notion_user', 'discussion', or 'comment'
* @param {string=} pointer.prepend - insert before pointer.siblingID. if false, will be appended after
* @param {string=} pointer.siblingID - uuidv4 sibling id. if unset, the record will be
* inserted at the end of the page start (or the start if pointer.prepend is true)
* @param {string=} pointer.parentID - uuidv4 parent id
* @param {string=} pointer.parentTable - parent record type (default: 'block').
* @param {string=} pointer.spaceID - uuidv4 space id
* @param {string=} pointer.userID - uuidv4 user id
* instead of the end
* @returns {string|object} error object or uuidv4 of the new record
*/
export const create = async (
{ recordValue = {}, recordTable = 'block' } = {},
{
prepend = false,
siblingID = undefined,
parentID = getPageID(),
parentTable = 'block',
spaceID = getSpaceID(),
userID = getUserID(),
} = {}
) => {
spaceID = standardiseUUID(await spaceID);
parentID = standardiseUUID(parentID);
siblingID = standardiseUUID(siblingID);
const recordID = standardiseUUID(recordValue?.id ?? fmt.uuidv4()),
path = [],
args = {
type: 'text',
id: recordID,
version: 0,
created_time: new Date().getTime(),
last_edited_time: new Date().getTime(),
parent_id: parentID,
parent_table: parentTable,
alive: true,
created_by_table: 'notion_user',
created_by_id: userID,
last_edited_by_table: 'notion_user',
last_edited_by_id: userID,
space_id: spaceID,
permissions: [{ type: 'user_permission', role: 'editor', user_id: userID }],
};
if (parentTable === 'space') {
parentID = spaceID;
args.parent_id = spaceID;
path.push('pages');
args.type = 'page';
} else if (parentTable === 'collection_view') {
path.push('page_sort');
args.type = 'page';
} else {
path.push('content');
}
const json = await fs.getJSON('https://www.notion.so/api/v3/saveTransactions', {
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
requestId: fmt.uuidv4(),
transactions: [
{
id: fmt.uuidv4(),
spaceId: spaceID,
operations: [
{
pointer: {
table: parentTable,
id: parentID,
spaceId: spaceID,
},
path,
command: prepend ? 'listBefore' : 'listAfter',
args: {
...(siblingID ? { after: siblingID } : {}),
id: recordID,
},
},
{
pointer: {
table: recordTable,
id: recordID,
spaceId: spaceID,
},
path: [],
command: 'set',
args: {
...args,
...recordValue,
},
},
],
},
],
}),
method: 'POST',
});
return json.errorId ? json : recordID;
};
/**
* unofficial content api: upload a file to notion's aws servers
* (requires user to be signed in or content to be public).
* TEST THIS THOROUGHLY. misuse can corrupt a record, leading the notion client
* to be unable to parse and render content properly and throw errors.
* why not use the official api?
* 1. cors blocking prevents use on the client
* 2. the majority of blocks are still 'unsupported'
* @param {File} file - the file to upload
* @param {object=} pointer - where the file should be accessible from
* @param {string=} pointer.pageID - uuidv4 page id
* @param {string=} pointer.spaceID - uuidv4 space id
* @returns {string|object} error object or the url of the uploaded file
*/
export const upload = async (file, { pageID = getPageID(), spaceID = getSpaceID() } = {}) => {
spaceID = standardiseUUID(await spaceID);
pageID = standardiseUUID(pageID);
const json = await fs.getJSON('https://www.notion.so/api/v3/getUploadFileUrl', {
method: 'POST',
headers: {
'content-type': 'application/json',
},
body: JSON.stringify({
bucket: 'secure',
name: file.name,
contentType: file.type,
record: {
table: 'block',
id: pageID,
spaceId: spaceID,
},
}),
});
if (json.errorId) return json;
fetch(json.signedPutUrl, {
method: 'PUT',
headers: { 'content-type': file.type },
body: file,
});
return json.url;
};
/**
* redirect through notion to a resource's signed aws url for display outside of notion
* (requires user to be signed in or content to be public)
* @param src source url for file
* @param {string} recordID uuidv4 record/block/file id
* @param {string=} recordTable record type (default: 'block').
* may also be 'collection', 'collection_view', 'space', 'notion_user', 'discussion', or 'comment'
* @returns {string} url signed if necessary, else string as-is
*/
export const sign = (src, recordID, recordTable = 'block') => {
if (src.startsWith('/')) src = `https://notion.so${src}`;
if (src.includes('secure.notion-static.com')) {
src = new URL(src);
src = `https://www.notion.so/signed/${encodeURIComponent(
src.origin + src.pathname
)}?table=${recordTable}&id=${recordID}`;
}
return src;
};

87
src/api/registry.js Normal file
View File

@ -0,0 +1,87 @@
/**
* notion-enhancer
* (c) 2024 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
* (https://notion-enhancer.github.io/) under the MIT license
*/
"use strict";
const _isManifestValid = (modManifest) => {
const { platform } = globalThis.__enhancerApi,
hasRequiredFields =
modManifest.id &&
modManifest.name &&
modManifest.version &&
modManifest.description &&
modManifest.authors,
meetsThemeRequirements =
!modManifest._src.startsWith("themes/") ||
((modManifest.tags?.includes("dark") ||
modManifest.tags?.includes("light")) &&
modManifest.thumbnail),
targetsCurrentPlatform =
!modManifest.platforms || //
modManifest.platforms.includes(platform);
return hasRequiredFields && meetsThemeRequirements && targetsCurrentPlatform;
};
let _mods;
const getMods = async (asyncFilter) => {
const { readJson } = globalThis.__enhancerApi;
// prettier-ignore
_mods ??= (await Promise.all((await readJson("registry.json")).map(async (_src) => {
const modManifest = { ...(await readJson(`${_src}/mod.json`)), _src };
return _isManifestValid(modManifest) ? modManifest : undefined;
}))).filter((mod) => mod);
// prettier-ignore
return (await Promise.all(_mods.map(async (mod) => {
return !asyncFilter || (await asyncFilter(mod)) ? mod : undefined;
}))).filter((mod) => mod);
},
getProfile = async () => {
const db = globalThis.__enhancerApi.initDatabase();
let activeProfile = await db.get("activeProfile");
activeProfile ??= (await db.get("profileIds"))?.[0];
return activeProfile ?? "default";
};
const isEnabled = async (id) => {
const { version, initDatabase } = globalThis.__enhancerApi,
mod = (await getMods()).find((mod) => mod.id === id);
if (mod._src === "core") return true;
const agreedToTerms = await initDatabase().get("agreedToTerms"),
enabledInProfile = await initDatabase([
await getProfile(),
"enabledMods",
]).get(id);
return agreedToTerms === version && enabledInProfile;
},
setEnabled = async (id, enabled) => {
return await globalThis.__enhancerApi
.initDatabase([await getProfile(), "enabledMods"])
.set(id, enabled);
};
const modDatabase = async (id) => {
const optionDefaults = (await getMods())
.find((mod) => mod.id === id)
?.options?.map?.((opt) => {
let value = opt.value;
value ??= opt.values?.[0]?.value;
value ??= opt.values?.[0];
return [opt.key, value];
})
?.filter?.(([, value]) => typeof value !== "undefined");
return globalThis.__enhancerApi.initDatabase(
[await getProfile(), id],
Object.fromEntries(optionDefaults ?? [])
);
};
Object.assign((globalThis.__enhancerApi ??= {}), {
getMods,
getProfile,
isEnabled,
setEnabled,
modDatabase,
});

70
src/api/state.js Normal file
View File

@ -0,0 +1,70 @@
/**
* notion-enhancer
* (c) 2023 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
* (https://notion-enhancer.github.io/) under the MIT license
*/
"use strict";
// batch event callbacks to avoid over-handling
// and any conflicts / perf.issues that may
// otherwise result. initial call is immediate,
// following calls are delayed. a wait time of
// ~200ms is recommended (the avg. human visual
// reaction time is ~180-200ms)
const sleep = async (ms) => {
return new Promise((res, rej) => setTimeout(res, ms));
},
debounce = (callback, ms = 200) => {
let delay, update;
const next = () =>
sleep(ms).then(() => {
if (!update) return (delay = undefined);
update(), (update = undefined);
delay = next();
});
return (...args) => {
if (delay) update = callback.bind(this, ...args);
return delay || ((delay = next()), callback(...args));
};
};
// provides basic key/value reactivity:
// this is shared between all active mods,
// i.e. mods can read and update other mods'
// reactive states. this enables interop
// between a mod's component islands and
// supports inter-mod communication if so
// required. caution should be used in
// naming keys to avoid conflicts
const _subscribers = [],
dumpState = () => (document.__enhancerState ??= {}),
getKeysFromState = (keys) => keys.map((key) => dumpState()[key]),
setState = (state) => {
Object.assign(dumpState(), state);
const updates = Object.keys(state);
_subscribers
.filter(([keys]) => updates.some((key) => keys.includes(key)))
.forEach(([keys, callback]) => callback(getKeysFromState(keys)));
},
// useState(["keyA", "keyB"]) => returns [valueA, valueB]
// useState(["keyA", "keyB"], callback) => registers callback
// to be triggered after each update to either keyA or keyB,
// with [valueA, valueB] passed to the callback's first arg
useState = (keys, callback) => {
const state = getKeysFromState(keys);
if (callback) {
callback = debounce(callback);
_subscribers.push([keys, callback]);
callback(state);
}
return state;
};
Object.assign((globalThis.__enhancerApi ??= {}), {
sleep,
debounce,
setState,
useState,
dumpState,
});

154
src/api/system.js Normal file
View File

@ -0,0 +1,154 @@
/**
* notion-enhancer
* (c) 2024 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
* (https://notion-enhancer.github.io/) under the MIT license
*/
"use strict";
const IS_ELECTRON = typeof module !== "undefined",
IS_RENDERER = IS_ELECTRON && process.type === "renderer";
// expected values: 'linux', 'win32', 'darwin' (== macos), 'firefox'
// and 'chromium' (inc. chromium-based browsers like edge and brave)
// other possible values: 'aix', 'freebsd', 'openbsd', 'sunos'
const platform = IS_ELECTRON
? process.platform
: navigator.userAgent.includes("Firefox")
? "firefox"
: "chromium",
// currently installed version of the notion-enhancer
version = IS_ELECTRON
? require("notion-enhancer/package.json").version
: chrome.runtime.getManifest().version,
// packages a url to access notion-enhancer assets and sources,
// proxies via api in desktop app to bypass service worker cache
enhancerUrl = (target = "") =>
IS_ELECTRON
? "https://www.notion.so/api/__notion-enhancer/" +
target.replace(/^\//, "")
: chrome.runtime.getURL(target),
// require a file from the root of notion's app/ folder,
// only available in an electron main process
notionRequire = (target) =>
IS_ELECTRON && !IS_RENDERER ? require(`../../../${target}`) : undefined;
let __port;
const connectToPort = () => {
if (__port) return;
__port = chrome.runtime.connect();
__port.onDisconnect.addListener(() => (__port = null));
},
onMessage = (channel, listener) => {
// from worker to client
if (IS_RENDERER) {
const { ipcRenderer } = require("electron");
ipcRenderer.on(channel, (event, message) => listener(message));
} else if (!IS_ELECTRON) {
const onMessage = (msg) => {
if (msg?.channel !== channel || msg?.invocation) return;
listener(msg.message);
};
connectToPort();
__port.onMessage.addListener(onMessage);
chrome.runtime.onMessage.addListener(onMessage);
}
},
sendMessage = (channel, message) => {
// to worker from client
if (IS_RENDERER) {
const { ipcRenderer } = require("electron");
ipcRenderer.send(channel, message);
} else if (!IS_ELECTRON) {
connectToPort();
__port.postMessage({ channel, message });
}
},
invokeInWorker = (channel, message) => {
// sends a payload to the worker/main
// process and waits for a response
if (IS_RENDERER) {
const { ipcRenderer } = require("electron");
return ipcRenderer.invoke(channel, message);
} else if (!IS_ELECTRON) {
// polyfills the electron.ipcRenderer.invoke method in
// the browser: uses a long-lived ipc connection to
// pass messages and handle responses asynchronously
let fulfilled;
connectToPort();
const id = crypto.randomUUID();
return new Promise((res, rej) => {
__port.onMessage.addListener((msg) => {
if (msg?.invocation !== id || fulfilled) return;
fulfilled = true;
res(msg.message);
});
__port.postMessage({ channel, message, invocation: id });
});
}
};
const readFile = (file) => {
if (IS_ELECTRON) {
// read directly from filesys if possible,
// treating notion-enhancer/src as fs root
if (!file.startsWith("http")) {
const fsp = require("fs/promises"),
{ resolve } = require("path");
return fsp.readFile(resolve(`${__dirname}/../${file}`), "utf-8");
}
} else file = file.startsWith("http") ? file : enhancerUrl(file);
return fetch(file).then((res) => res.text());
},
readJson = (file) => {
// as above, uses require instead of readFile
// and res.json() instead of res.text() to return
// json content of file in object form
if (IS_ELECTRON) {
if (!file.startsWith("http")) {
const { resolve } = require("path");
return require(resolve(`${__dirname}/../${file}`));
}
} else file = file.startsWith("http") ? file : enhancerUrl(file);
return fetch(file).then((res) => res.json());
};
const initDatabase = (namespace, fallbacks = {}) => {
// all db operations are performed via ipc:
// with nodeintegration disabled, sqlite cannot
// be require()-d from the renderer process
const query = (query, args = {}) =>
IS_ELECTRON && !IS_RENDERER
? globalThis.__enhancerApi.queryDatabase(namespace, query, args)
: invokeInWorker("notion-enhancer", {
action: "query-database",
data: { namespace, query, args },
});
return {
get: (key) => query("get", { key, fallbacks }),
set: (key, value) => query("set", { key, value }),
remove: (keys) => query("remove", { keys }),
export: () => query("export"),
import: (obj) => query("import", { obj }),
};
},
reloadApp = () => {
if (IS_ELECTRON && !IS_RENDERER) {
const { app } = require("electron");
app.relaunch(), app.exit();
} else sendMessage("notion-enhancer", "reload-app");
};
Object.assign((globalThis.__enhancerApi ??= {}), {
platform,
version,
enhancerUrl,
notionRequire,
onMessage,
sendMessage,
invokeInWorker,
readFile,
readJson,
initDatabase,
reloadApp,
});

BIN
src/assets/colour-x128.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

BIN
src/assets/colour-x16.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 595 B

BIN
src/assets/colour-x256.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.8 KiB

BIN
src/assets/colour-x32.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

BIN
src/assets/colour-x48.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

BIN
src/assets/colour-x512.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

BIN
src/assets/colour-x64.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

13
src/assets/icons.svg.js Normal file

File diff suppressed because one or more lines are too long

157
src/core/client.mjs Normal file
View File

@ -0,0 +1,157 @@
/**
* notion-enhancer
* (c) 2023 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
* (https://notion-enhancer.github.io/) under the MIT license
*/
// telemetry endpoint not ready, disabled for current release
import { checkForUpdate } from "./updateCheck.mjs";
// import { sendTelemetryPing } from "./sendTelemetry.mjs";
import { Modal, Frame } from "./islands/Modal.mjs";
import { MenuButton } from "./islands/MenuButton.mjs";
import { Tooltip } from "./islands/Tooltip.mjs";
import { Panel } from "./islands/Panel.mjs";
const shouldLoadThemeOverrides = async (api, db) => {
const { getMods, isEnabled } = api,
loadThemeOverrides = await db.get("loadThemeOverrides");
if (loadThemeOverrides === "Enabled") return true;
if (loadThemeOverrides === "Disabled") return false;
// prettier-ignore
// loadThemeOverrides === "Auto"
return (await getMods(async (mod) => {
if (!mod._src.startsWith("themes/")) return false;
return await isEnabled(mod.id);
})).length;
},
loadThemeOverrides = async (api, db) => {
const { html, enhancerUrl } = api;
if (!(await shouldLoadThemeOverrides(api, db))) return;
document.head.append(html`<link
rel="stylesheet"
href=${enhancerUrl("core/theme.css")}
/>`);
},
insertCustomStyles = async (api, db) => {
const { html } = api,
customStyles = (await db.get("customStyles"))?.content;
if (!customStyles) return;
const $customStyles = html`<style
id="__custom"
innerHTML=${customStyles}
></style>`;
return document.head.append($customStyles);
};
const insertMenu = async (api, db) => {
const inviteMember = `.notion-sidebar-container .notion-sidebar [role="button"]:has(.inviteMember)`,
{ html, addMutationListener, removeMutationListener } = api,
{ addKeyListener, platform, enhancerUrl, onMessage } = api,
menuButtonIconStyle = await db.get("menuButtonIconStyle"),
menuButtonLabel = await db.get("menuButtonLabel"),
openMenuHotkey = await db.get("openMenuHotkey"),
menuPing = {
channel: "notion-enhancer",
hotkey: openMenuHotkey,
icon: menuButtonIconStyle,
};
let _contentWindow;
const updateMenuTheme = () => {
const darkMode = document.body.classList.contains("dark"),
notionTheme = darkMode ? "dark" : "light";
menuPing.theme = notionTheme;
_contentWindow?.postMessage?.(menuPing, "*");
};
const $modal = html`<${Modal}>
<${Frame}
title="notion-enhancer menu"
src="${enhancerUrl("core/menu/index.html")}"
onload=${function () {
// pass notion-enhancer api to electron menu process
if (["linux", "win32", "darwin"].includes(platform)) {
const apiKey = "__enhancerApi";
this.contentWindow[apiKey] = { ...globalThis[apiKey] };
}
_contentWindow = this.contentWindow;
updateMenuTheme();
}}
/>
<//>`,
$button = html`<${MenuButton}
onclick=${$modal.open}
notifications=${(await checkForUpdate()) ? 1 : 0}
themeOverridesLoaded=${await shouldLoadThemeOverrides(api, db)}
icon="notion-enhancer${menuButtonIconStyle === "Monochrome"
? "?mask"
: " text-[16px]"}"
>${menuButtonLabel}
<//>`;
const appendToDom = () => {
if (!document.body.contains($modal)) document.body.append($modal);
else if (!document.body.contains($button)) {
document.querySelector(inviteMember)?.after($button);
} else removeMutationListener(appendToDom);
};
html`<${Tooltip}>
<b>Configure the notion-enhancer and its mods</b>
<//>`.attach($button, "right");
addMutationListener(inviteMember, appendToDom);
addMutationListener(".notion-app-inner", updateMenuTheme, { subtree: false });
appendToDom();
addKeyListener(openMenuHotkey, (event) => {
event.preventDefault();
event.stopPropagation();
$modal.open();
});
addEventListener("message", (event) => {
// from embedded menu
if (event.data?.channel !== "notion-enhancer") return;
if (event.data?.action === "close-menu") $modal.close();
if (event.data?.action === "open-menu") $modal.open();
});
onMessage("notion-enhancer", (message) => {
// from worker
if (message === "open-menu") $modal.open();
});
};
const insertPanel = async (api, db) => {
const notionFrame = ".notion-frame",
togglePanelHotkey = await db.get("togglePanelHotkey"),
{ html, setState, addMutationListener, removeMutationListener } = api;
const $panel = html`<${Panel}
hotkey="${togglePanelHotkey}"
...${Object.assign(
...["Width", "Open", "View"].map((key) => ({
[`_get${key}`]: () => db.get(`panel${key}`),
[`_set${key}`]: async (value) => {
await db.set(`panel${key}`, value);
setState({ rerender: true });
},
}))
)}
/>`,
appendToDom = () => {
const $frame = document.querySelector(notionFrame);
if (!$frame) return;
$frame.append($panel);
$frame.style.flexDirection = "row";
removeMutationListener(appendToDom);
};
addMutationListener(notionFrame, appendToDom);
appendToDom();
};
export default async (api, db) =>
Promise.all([
insertMenu(api, db),
insertPanel(api, db),
insertCustomStyles(api, db),
loadThemeOverrides(api, db),
// sendTelemetryPing(),
]).then(() => api.sendMessage("notion-enhancer", "load-complete"));

49
src/core/electron.cjs Normal file
View File

@ -0,0 +1,49 @@
/**
* notion-enhancer
* (c) 2024 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
* (https://notion-enhancer.github.io/) under the MIT license
*/
"use strict";
const getPreference = (key) => {
const { preferences = {} } = globalThis.__notionStore?.getState()?.app;
return preferences[key];
},
setPreference = (key, value) => {
const action = globalThis.__updatePreferences?.({ [key]: value });
globalThis.__notionStore?.dispatch?.(action);
};
module.exports = async ({}, db) => {
const toggleWindowHotkey = await db.get("toggleWindowHotkey"),
developerMode = await db.get("developerMode");
// enable developer mode, access extra debug tools
Object.assign((globalThis.__notionConfig ??= {}), {
env: developerMode ? "development" : "production",
});
// listen for the global window toggle hotkey
const { app, globalShortcut, BrowserWindow } = require("electron");
app.whenReady().then(() => {
globalShortcut.register(toggleWindowHotkey, () => {
const windows = BrowserWindow.getAllWindows()
// filter out quick search window
.filter((win) => win.fullScreenable),
focused = windows.some((win) => win.isFocused() && win.isVisible());
windows.forEach((win) =>
// check if notion is set to run in the background
getPreference("isHideLastWindowOnCloseEnabled")
? focused
? win.hide()
: win.show()
: focused
? win.minimize()
: win.isMinimized()
? win.restore()
: win.focus()
);
});
});
};

View File

@ -0,0 +1,54 @@
/**
* notion-enhancer
* (c) 2024 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
* (https://notion-enhancer.github.io/) under the MIT license
*/
let __$wrapper;
const setupWrapper = () => {
const notionAi = ".notion-help-button, .notion-ai-button",
{ html, addMutationListener } = globalThis.__enhancerApi,
{ removeMutationListener } = globalThis.__enhancerApi;
return (__$wrapper ??= new Promise((res) => {
const addToDom = () => {
const $notionAi = document.querySelector(notionAi);
if (!$notionAi) return;
const $wrapper = html`<div
class="notion-enhancer--floating-buttons z-50 gap-[12px]
flex absolute bottom-[calc(16px+env(safe-area-inset-bottom))]"
></div>`;
removeMutationListener(addToDom);
$notionAi.after($wrapper);
res($wrapper);
};
addMutationListener(notionAi, addToDom);
addToDom();
}));
},
addFloatingButton = async ($btn) => {
if (document.contains($btn)) return;
(await setupWrapper()).prepend($btn);
// button positioning is calculated by panel
},
removeFloatingButton = ($btn) => $btn.remove();
function FloatingButton({ icon, ...props }, ...children) {
const { html, extendProps } = globalThis.__enhancerApi;
extendProps(props, {
tabindex: 0,
class: `notion-enhancer--floating-button
size-[36px] flex items-center justify-center rounded-full
text-([20px] [color:var(--theme--fg-primary)]) select-none cursor-pointer
bg-[color:var(--theme--bg-secondary)] hover:bg-[color:var(--theme--bg-hover)]
shadow-[rgba(15,15,15,0.2)_0px_0px_0px_1px,rgba(15,15,15,0.2)_0px_2px_4px]`,
});
return html`<button ...${props}>${children}</button>`;
}
if (globalThis.document) setupWrapper();
Object.assign((globalThis.__enhancerApi ??= {}), {
addFloatingButton,
removeFloatingButton,
});
export { addFloatingButton, FloatingButton };

View File

@ -0,0 +1,43 @@
/**
* notion-enhancer
* (c) 2023 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
* (https://notion-enhancer.github.io/) under the MIT license
*/
function MenuButton(
{ icon, notifications, themeOverridesLoaded, ...props },
...children
) {
const { html, extendProps } = globalThis.__enhancerApi;
extendProps(props, {
tabindex: 0,
role: "button",
class: `notion-enhancer--menu-button flex select-none
cursor-pointer rounded-[6px] text-[14px] font-medium
transition hover:bg-[color:var(--theme--bg-hover)]
w-full h-[30px] px-[10px] py-[4px] items-center`,
});
return html`<div ...${props}>
<div class="flex items-center justify-center text-[18px] mr-[10px]">
<i class="i-${icon}"></i>
</div>
<div>${children}</div>
<div class="ml-auto my-auto${notifications > 0 ? "" : " hidden"}">
<!-- accents are squashed into one variable for theming:
use rgb to match notion if overrides not loaded -->
<div
class="flex justify-center size-[16px] font-semibold mb-[2px]
text-([10px] [color:var(--theme--accent-secondary\\_contrast)])
bg-[color:var(--theme--accent-secondary)] rounded-[3px]
dark:bg-[color:${themeOverridesLoaded
? "var(--theme--accent-secondary)"
: "rgb(180,65,60)"}]"
>
<span class="ml-[-0.5px]">${notifications}</span>
</div>
</div>
</div>`;
}
export { MenuButton };

View File

@ -0,0 +1,67 @@
/**
* notion-enhancer
* (c) 2023 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
* (https://notion-enhancer.github.io/) under the MIT license
*/
function Modal(props, ...children) {
const { html, extendProps, addKeyListener } = globalThis.__enhancerApi;
extendProps(props, {
class: `notion-enhancer--menu-modal z-[999]
fixed inset-0 w-screen h-screen group/modal
transition pointer-events-none opacity-0
open:(pointer-events-auto opacity-100)`,
});
const $modal = html`<div ...${props}>
<div
class="fixed inset-0 bg-[color:var(--theme--bg-overlay)]"
onclick=${() => $modal.close()}
></div>
<div
class="fixed inset-0 flex w-screen h-screen
items-center justify-center pointer-events-none"
>
${children}
</div>
</div>`;
let _openQueued;
$modal.open = async () => {
_openQueued = true;
while (!document.contains($modal)) {
if (!_openQueued) return;
// dont trigger open until menu is in dom,
// to ensure transition is shown when menu
// does initially open
await new Promise(requestAnimationFrame);
}
$modal.setAttribute("open", "");
setTimeout(() => $modal.onopen?.(), 200);
};
$modal.close = () => {
_openQueued = false;
$modal.removeAttribute("open");
if ($modal.contains(document.activeElement)) {
document.activeElement.blur();
}
setTimeout(() => $modal.onclose?.(), 200);
};
addKeyListener("Escape", () => {
if (document.activeElement?.nodeName === "INPUT") return;
$modal.close();
});
return $modal;
}
function Frame(props) {
const { html, extendProps } = globalThis.__enhancerApi;
extendProps(props, {
class: `rounded-[12px] w-[1150px] h-[calc(100vh-100px)] opacity-0
max-w-[calc(100vw-100px)] max-h-[715px] overflow-hidden scale-95
bg-[color:var(--theme--bg-primary)] drop-shadow-xl transition
group-open/modal:(pointer-events-auto opacity-100 scale-100)`,
});
return html`<iframe ...${props}></iframe>`;
}
export { Modal, Frame };

433
src/core/islands/Panel.mjs Normal file
View File

@ -0,0 +1,433 @@
/**
* notion-enhancer
* (c) 2023 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
* (c) 2021 CloudHill <rl.cloudhill@gmail.com> (https://github.com/CloudHill)
* (https://notion-enhancer.github.io/) under the MIT license
*/
import { Tooltip } from "./Tooltip.mjs";
import { TopbarButton } from "./TopbarButton.mjs";
import { Select } from "../menu/islands/Select.mjs";
const coreId = "0f0bf8b6-eae6-4273-b307-8fc43f2ee082",
topbarId = "e0700ce3-a9ae-45f5-92e5-610ded0e348d",
tweaksId = "5174a483-c88d-4bf8-a95f-35cd330b76e2";
// note: these islands are not reusable.
// panel views can be added via addPanelView,
// do not instantiate additional panels
let panelViews = [],
// "$icon" may either be an actual dom element,
// or an icon name from the lucide icons set
addPanelView = ({ title, $icon, $view }) => {
panelViews.push([{ title, $icon }, $view]);
panelViews.sort(([{ title: a }], [{ title: b }]) => a.localeCompare(b));
const { setState } = globalThis.__enhancerApi;
setState?.({ panelViews });
},
removePanelView = ($view) => {
panelViews = panelViews.filter(([, v]) => v !== $view);
const { setState } = globalThis.__enhancerApi;
setState?.({ panelViews });
};
function View({ _get }) {
const { html, useState } = globalThis.__enhancerApi,
$container = html`<div
class="overflow-(y-auto x-hidden)
h-[calc(100%-46px)] min-w-[var(--panel--width)]"
></div>`;
useState(["rerender"], async () => {
const openView = await _get?.(),
$view =
panelViews.find(([{ title }]) => {
return title === openView;
})?.[1] || panelViews[0]?.[1];
if (!$container.contains($view)) {
$container.innerHTML = "";
$container.append($view);
}
});
return $container;
}
function Switcher({ _get, _set, minWidth, maxWidth }) {
const { html, useState } = globalThis.__enhancerApi,
$select = html`<${Select}
popupMode="dropdown"
class="w-full text-left"
maxWidth=${maxWidth - 56}
minWidth=${minWidth - 56}
...${{ _get, _set }}
/>`;
useState(["panelViews"], ([panelViews = []]) => {
const values = panelViews.map(([{ title, $icon }]) => {
// panel switcher internally uses the select island,
// which expects an option value rather than a title
return { value: title, $icon };
});
$select.setValues(values);
});
return html`<div
class="relative flex items-center grow
font-medium p-[8.5px] ml-[4px] select-none"
>
${$select}
</div>`;
}
function Panel({
hotkey,
_getWidth,
_setWidth,
_getOpen,
_setOpen,
_getView,
_setView,
minWidth = 256,
maxWidth = 640,
transitionDuration = 300,
}) {
const { modDatabase, isEnabled } = globalThis.__enhancerApi,
{ html, useState, addKeyListener, MODS_LOADED } = globalThis.__enhancerApi,
{ addMutationListener, removeMutationListener } = globalThis.__enhancerApi,
$panelToggle = html`<button
aria-label="Toggle side panel"
class="select-none size-[24px] duration-[20ms]
transition inline-flex items-center justify-center mr-[10px]
rounded-[3px] hover:bg-[color:var(--theme--bg-hover)]"
>
<i
class="i-chevrons-left size-[20px]
text-[color:var(--theme--fg-secondary)] transition-transform
group-[&[data-pinned]]/panel:rotate-180 duration-[${transitionDuration}ms]"
/>
</button>`,
$panel = html`<div
class="notion-enhancer--panel group/panel order-2
shrink-0 [&[data-pinned]]:w-[var(--panel--width,0)]"
>
<style>
.notion-frame {
flex-direction: row !important;
}
/* prevent page load skeletons overlapping with panel */
.notion-frame [role="progressbar"] {
padding-right: var(--panel--width);
}
.notion-frame [role="progressbar"] > div {
overflow-x: clip;
}
</style>
<aside
class="border-(l-1 [color:var(--theme--fg-border)]) w-0
group-[&[data-pinned]]/panel:(w-[var(--panel--width,0)]) h-[calc(100vh-45px)] bottom-0)
absolute right-0 z-20 bg-[color:var(--theme--bg-primary)] group-[&[data-peeked]]/panel:(
w-[var(--panel--width,0)] h-[calc(100vh-120px)] bottom-[60px] rounded-l-[8px] border-(t-1 b-1))"
>
<div
class="flex justify-between items-center
border-(b [color:var(--theme--fg-border)])"
>
<${Switcher}
...${{ _get: _getView, _set: _setView, minWidth, maxWidth }}
/>
${$panelToggle}
</div>
<${View} ...${{ _get: _getView }} />
</aside>
</div>`;
const notionTopbar = ".notion-topbar",
topbarFavorite = ".notion-topbar-favorite-button",
$topbarToggle = html`<${TopbarButton}
aria-label="Toggle side panel"
icon="panel-right"
/>`,
addToTopbar = () => {
if (document.contains($topbarToggle)) return;
document.querySelector(topbarFavorite)?.after($topbarToggle);
};
$panelToggle.onclick = $topbarToggle.onclick = () => $panel.toggle();
addMutationListener(notionTopbar, addToTopbar, { subtree: false });
addToTopbar();
isEnabled(topbarId).then(async (topbarEnabled) => {
if (!topbarEnabled) return;
const topbarDatabase = await modDatabase(topbarId),
panelButton = await topbarDatabase.get("panelButton"),
panelIcon = await topbarDatabase.get("panelIcon");
if (panelButton === "Text") {
$topbarToggle.innerHTML = `<span>${$topbarToggle.ariaLabel}</span>`;
} else if (panelIcon?.content) $topbarToggle.innerHTML = panelIcon.content;
});
let preDragWidth, dragStartX, _animatedAt;
const getWidth = async (width) => {
if (width && !isNaN(width)) {
width = Math.max(width, minWidth);
width = Math.min(width, maxWidth);
} else width = await _getWidth?.();
if (isNaN(width)) width = minWidth;
return width;
},
setInteractive = (interactive) => {
$panel
.querySelectorAll("[tabindex]")
.forEach(($el) => ($el.tabIndex = interactive ? 1 : -1));
},
isAnimated = () => {
if (!_animatedAt) return false;
return Date.now() - _animatedAt <= transitionDuration;
},
isDragging = () => !isNaN(preDragWidth) && !isNaN(dragStartX),
isPinned = () => $panel.hasAttribute("data-pinned"),
isPeeked = () => $panel.hasAttribute("data-peeked"),
isClosed = () => !isPinned() && !isPeeked();
const closedWidth = { width: "0px" },
openWidth = { width: "var(--panel--width, 0px)" },
peekAnimation = {
height: "calc(100vh - 120px)",
bottom: "60px",
borderTopWidth: "1px",
borderBottomWidth: "1px",
borderTopLeftRadius: "8px",
borderBottomLeftRadius: "8px",
boxShadow: document.body.classList.contains("dark")
? "rgba(15, 15, 15, 0.1) 0px 0px 0px 1px, rgba(15, 15, 15, 0.2) 0px 3px 6px, rgba(15, 15, 15, 0.4) 0px 9px 24px"
: "rgba(15, 15, 15, 0.05) 0px 0px 0px 1px, rgba(15, 15, 15, 0.1) 0px 3px 6px, rgba(15, 15, 15, 0.2) 0px 9px 24px",
},
pinAnimation = {
height: "calc(100vh - 45px)",
bottom: "0px",
borderTopWidth: "0px",
borderBottomWidth: "0px",
borderTopLeftRadius: "0px",
borderBottomLeftRadius: "0px",
boxShadow: "none",
};
const animationState = { ...closedWidth },
animate = ($target, keyframes) => {
const opts = {
fill: "forwards",
duration: transitionDuration,
easing: "ease",
};
$target.animate(keyframes, opts);
},
animatePanel = (to) => {
_animatedAt = Date.now();
animate($panel.lastElementChild, [animationState, to]);
Object.assign(animationState, to);
};
isEnabled(tweaksId).then(async (tweaksEnabled) => {
if (!tweaksEnabled) return;
const tweaksDatabase = await modDatabase(tweaksId),
snappyTransitions = await tweaksDatabase.get("snappyTransitions");
if (snappyTransitions) transitionDuration = 0;
});
// dragging the resize handle horizontally will
// adjust the width of the panel correspondingly
const $resizeHandle = html`<div
class="absolute opacity-0 h-full w-[3px] left-[-2px]
active:cursor-text bg-[color:var(--theme--fg-border)] z-20
transition duration-300 hover:(cursor-col-resize opacity-100)
group-[&[data-peeked]]/panel:(w-[8px] left-[-1px] rounded-l-[7px])"
>
<div
class="ml-[2px] bg-[color:var(--theme--bg-primary)]
group-[&[data-peeked]]/panel:(my-px h-[calc(100%-2px)] rounded-l-[6px])"
></div>
</div>`,
startDrag = async (event) => {
dragStartX = event.clientX;
preDragWidth = await getWidth();
document.addEventListener("mousemove", onDrag);
document.addEventListener("mouseup", endDrag);
},
onDrag = (event) => {
event.preventDefault();
if (!isDragging()) return;
$panel.resize(preDragWidth + (dragStartX - event.clientX));
},
endDrag = (event) => {
document.removeEventListener("mousemove", onDrag);
document.removeEventListener("mouseup", endDrag);
if (!isDragging()) return;
$panel.resize(preDragWidth + (dragStartX - event.clientX));
// toggle panel if not resized
if (dragStartX - event.clientX === 0) $panel.toggle();
preDragWidth = dragStartX = undefined;
};
$resizeHandle.addEventListener("mousedown", startDrag);
$panel.lastElementChild.prepend($resizeHandle);
// add tooltips to panel pin/unpin toggles
const $resizeTooltipClick = html`<span></span>`,
$resizeTooltip = html`<${Tooltip}
onbeforeshow=${() => {
$resizeTooltipClick.innerText = isPinned() ? "close" : "lock open";
}}
><b>Drag</b> to resize<br />
<b>Click</b> to ${$resizeTooltipClick}
<//>`,
$toggleTooltipClick = html`<b></b>`,
$toggleTooltip = html`<${Tooltip}
onbeforeshow=${() => {
$toggleTooltipClick.innerText = isPinned()
? "Close sidebar"
: "Lock sidebar open";
}}
>${$toggleTooltipClick}<br />
${hotkey}
<//>`;
$resizeTooltip.attach($resizeHandle, "left");
$toggleTooltip.attach($topbarToggle, "bottom");
$toggleTooltip.attach($panelToggle, "bottom");
// hovering over the peek trigger will temporarily
// pop out an interactive preview of the panel
let _peekDebounce, _peekPanelOnHover;
const $peekTrigger = html`<div
class="absolute z-10 right-0 h-[calc(100vh-120px)] bottom-[60px] w-[96px]
group-[&[data-peeked]]/panel:(w-[calc(var(--panel--width,0)+8px)])
group-[&[data-pinned]]/panel:(w-[calc(var(--panel--width,0)+8px)])"
></div>`;
modDatabase(coreId).then(async (db) => {
_peekPanelOnHover = await db.get("peekPanelOnHover");
if (_peekPanelOnHover) $panel.prepend($peekTrigger);
});
$panel.addEventListener("mouseout", () => {
if (isDragging() || isAnimated() || isPinned()) return;
if (!$panel.matches(":hover")) $panel.close();
});
$panel.addEventListener("mouseover", () => {
_peekDebounce ??= setTimeout(() => {
if (isClosed() && $panel.matches(":hover")) $panel.peek();
_peekDebounce = undefined;
}, 100);
});
// moves ai/q&a button out of the way of open panel.
// normally would place outside of an island, but in
// this case is necessary for syncing up animations
const notionAi = ".notion-help-button, .notion-ai-button",
floatingButtons = ".notion-enhancer--floating-buttons",
repositionCorner = async (offset) => {
const $help = document.querySelector(notionAi),
$floating = document.querySelector(floatingButtons);
offset ??= await getWidth();
if (isNaN(offset)) offset = minWidth;
if (!isPinned()) offset = 0;
// offset help from panel edge
offset += 26;
for (const $btn of [$help, $floating]) {
if (!$btn) continue;
const computedStyles = getComputedStyle($btn),
visible = computedStyles.getPropertyValue("display") !== "none";
if (!visible) continue;
const width = computedStyles.getPropertyValue("width"),
from = computedStyles.getPropertyValue("right"),
to = offset + "px";
// offset floating buttons from help
offset += 12 + parseInt(width);
if (from === to) continue;
$btn.style.setProperty("right", to);
animate($btn, [({ right: from }, { right: to })]);
}
if ($help || $floating) removeMutationListener(repositionCorner);
};
const corner = `${notionAi}, ${floatingButtons}`;
addMutationListener(corner, repositionCorner, { subtree: false });
MODS_LOADED.then(() => repositionCorner());
$panel.pin = () => {
if (isPinned() || !panelViews.length) return;
if (isClosed()) Object.assign(animationState, pinAnimation);
animatePanel({ ...openWidth, ...pinAnimation });
animate($panel, [closedWidth, openWidth]);
$panel.removeAttribute("data-peeked");
$panel.dataset.pinned = true;
$topbarToggle.setAttribute("data-active", true);
setInteractive(true);
_setOpen(true);
$panel.resize();
};
$panel.peek = () => {
if (!_peekPanelOnHover) return;
if (isPeeked() || !panelViews.length) return;
if (isClosed()) Object.assign(animationState, peekAnimation);
animatePanel({ ...openWidth, ...peekAnimation });
// closing on mouseout is disabled mid-animation,
// queue close in case mouse is no longer peeking
// after the initial animation is complete
setTimeout(() => {
if (!isDragging() && !$panel.matches(":hover")) $panel.close();
}, transitionDuration);
$panel.removeAttribute("data-pinned");
$panel.dataset.peeked = true;
setInteractive(true);
$panel.resize();
};
$panel.close = async () => {
if (isClosed()) return;
if (panelViews.length) _setOpen(false);
$topbarToggle.removeAttribute("data-active");
const width = (animationState.width = `${await getWidth()}px`);
// only animate container close if it is actually taking up space,
// otherwise will unnaturally grow + retrigger peek on peek mouseout
if (isPinned()) animate($panel, [{ width }, closedWidth]);
if (!$panel.matches(":hover") || !_peekPanelOnHover) {
$panel.removeAttribute("data-pinned");
$panel.removeAttribute("data-peeked");
animatePanel(closedWidth);
setInteractive(false);
$panel.resize();
} else $panel.peek();
};
$panel.toggle = () => {
if (isPinned()) $panel.close();
else $panel.pin();
};
// resizing handles visual resizes (inc. setting width to 0
// if closed) and actual resizes on drag (inc. saving to db)
$panel.resize = async (width) => {
$resizeTooltip.hide();
width = await getWidth(width);
_setWidth?.(width);
// works in conjunction with animations, acts as fallback
// plus updates dependent styles e.g. page skeleton padding
if (isClosed()) width = 0;
const $parent = $panel.parentElement || $panel;
$parent.style.setProperty("--panel--width", `${width}px`);
if ($parent !== $panel) $panel.style.removeProperty("--panel--width");
repositionCorner(width);
};
useState(["panelViews"], async ([panelViews = []]) => {
$topbarToggle.style.display = panelViews.length ? "" : "none";
if (panelViews.length && (await _getOpen())) $panel.pin();
else $panel.close();
});
if (!hotkey) return $panel;
addKeyListener(hotkey, (event) => {
event.preventDefault();
event.stopPropagation();
$panel.toggle();
});
return $panel;
}
Object.assign((globalThis.__enhancerApi ??= {}), {
addPanelView,
removePanelView,
});
export { Panel };

View File

@ -0,0 +1,94 @@
/**
* notion-enhancer
* (c) 2023 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
* (https://notion-enhancer.github.io/) under the MIT license
*/
function Tooltip(props, ...children) {
const { html, extendProps } = globalThis.__enhancerApi;
extendProps(props, {
role: "dialog",
class: `absolute group/tooltip z-[999] text-center pointer-events-none`,
});
const notionApp = ".notion-app-inner",
$tooltip = html`<div ...${props}>
<div
class="bg-[color:var(--theme--bg-secondary)]
text-([color:var(--theme--fg-secondary)] [12px] nowrap)
leading-[1.4] font-medium py-[4px] px-[8px] rounded-[4px]
drop-shadow-md transition duration-100 opacity-0
group-open/tooltip:(pointer-events-auto opacity-100)
[&>b]:text-[color:var(--theme--fg-primary)]"
>
${children}
</div>
</div>`;
// can pass each coord as a number or a function
$tooltip.show = (x, y) => {
const $notionApp = document.querySelector(notionApp);
if (!document.contains($tooltip)) $notionApp?.append($tooltip);
if ($tooltip.hasAttribute("open")) return;
$tooltip.onbeforeshow?.();
const edgePadding = 12,
{ clientHeight, clientWidth } = document.documentElement;
requestAnimationFrame(() => {
if (typeof x === "function") x = x();
if (typeof y === "function") y = y();
if (x < edgePadding) x = $tooltip.clientWidth + edgePadding;
if (x + $tooltip.clientWidth > clientWidth - edgePadding)
x = clientWidth - $tooltip.clientWidth - edgePadding;
if (y < edgePadding) y = $tooltip.clientHeight + edgePadding;
if (y + $tooltip.clientHeight > clientHeight - edgePadding)
y = clientHeight - $tooltip.clientHeight - edgePadding;
$tooltip.style.left = `${x}px`;
$tooltip.style.top = `${y}px`;
$tooltip.setAttribute("open", true);
$tooltip.onshow?.();
});
};
$tooltip.hide = () => {
$tooltip.onbeforehide?.();
$tooltip.removeAttribute("open");
setTimeout(() => {
$tooltip.onhide?.();
}, 200);
};
$tooltip.attach = ($target, alignment = "") => {
$target.addEventListener("mouseover", (event) => {
setTimeout(() => {
if (!$target.matches(":hover")) return;
const x = () => {
const rect = $target.getBoundingClientRect();
if (["top", "bottom"].includes(alignment)) {
return rect.left + rect.width / 2 - $tooltip.clientWidth / 2;
} else if (alignment === "left") {
return rect.left - $tooltip.clientWidth - 6;
} else if (alignment === "right") {
return rect.right + 6;
} else return event.clientX;
},
y = () => {
const rect = $target.getBoundingClientRect();
if (["left", "right"].includes(alignment)) {
// match mouse alignment if hovering over large
// target e.g. panel resize handle, otherwise centre
return rect.height > $tooltip.clientHeight * 2
? event.clientY - $tooltip.clientHeight / 2
: rect.top + rect.height / 2 - $tooltip.clientHeight / 2;
} else if (alignment === "top") {
return rect.top - $tooltip.clientHeight - 6;
} else if (alignment === "bottom") {
return rect.bottom + 6;
} else return event.clientY;
};
$tooltip.show(x, y);
}, 200);
});
$target.addEventListener("mouseout", $tooltip.hide);
};
return $tooltip;
}
export { Tooltip };

View File

@ -0,0 +1,29 @@
/**
* notion-enhancer
* (c) 2024 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
* (https://notion-enhancer.github.io/) under the MIT license
*/
function TopbarButton({ icon, ...props }, ...children) {
const { html, extendProps } = globalThis.__enhancerApi;
extendProps(props, {
tabindex: 0,
class: `notion-enhancer--topbar-button
text-[color:var(--theme--fg-primary)] mr-[2px]
select-none h-[28px] w-[33px] duration-[20ms]
transition inline-flex items-center justify-center
rounded-[3px] hover:bg-[color:var(--theme--bg-hover)]
has-[span]:w-auto [&>span]:(text-[14px] leading-[1.2] px-[8px])
[&[data-active]]:bg-[color:var(--theme--bg-hover)]
[&>i]:size-[20px]`,
});
// [role="button"] == `-webkit-app-region: no-drag`
return html`<div role="button" ...${props}>
${props.innerHTML || children.length
? children
: html`<i class="i-${icon}" />`}
</div>`;
}
export { TopbarButton };

59
src/core/menu/index.html Normal file
View File

@ -0,0 +1,59 @@
<!DOCTYPE html>
<html lang="en">
<head>
<base target="_blank" />
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>notion-enhancer menu</title>
<link rel="stylesheet" href="./menu.css" />
<link rel="stylesheet" href="../../vendor/coloris.min.css" />
<script src="../../vendor/coloris.min.js" type="module"></script>
<script src="./menu.mjs" type="module" defer></script>
</head>
<body>
<div id="skeleton">
<div class="row row-group">
<div class="shimmer" style="width: 110px"></div>
</div>
<div class="row">
<div class="shimmer icon"></div>
<div class="shimmer" style="width: 60px"></div>
</div>
<div class="row">
<div class="shimmer icon"></div>
<div class="shimmer" style="width: 72px"></div>
</div>
<div class="row">
<div class="shimmer icon"></div>
<div class="shimmer" style="width: 78px"></div>
</div>
<div class="row">
<div class="shimmer icon"></div>
<div class="shimmer" style="width: 96px"></div>
</div>
<div class="row">
<div class="shimmer icon"></div>
<div class="shimmer" style="width: 79px"></div>
</div>
<div class="row row-group">
<div class="shimmer" style="width: 53px"></div>
</div>
<div class="row">
<div class="shimmer icon"></div>
<div class="shimmer" style="width: 30px"></div>
</div>
<div class="row">
<div class="shimmer icon"></div>
<div class="shimmer" style="width: 48px"></div>
</div>
<div class="row">
<div class="shimmer icon"></div>
<div class="shimmer" style="width: 65px"></div>
</div>
<div class="row">
<div class="shimmer icon"></div>
<div class="shimmer" style="width: 75px"></div>
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,192 @@
/**
* notion-enhancer
* (c) 2023 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
* (https://notion-enhancer.github.io/) under the MIT license
*/
import { Popup } from "./Popup.mjs";
import { Button } from "./Button.mjs";
import { Description } from "./Description.mjs";
const updateGuide =
"https://notion-enhancer.github.io/getting-started/updating/",
tsAndCs = "https://notion-enhancer.github.io/about/terms-and-conditions/";
const rectToStyle = (rect) =>
["width", "height", "top", "bottom", "left", "right"]
.filter((prop) => rect[prop])
.map((prop) => `${prop}: ${rect[prop]};`)
.join("");
function Star({ from, ...rect }) {
const { html } = globalThis.__enhancerApi;
return html`<svg
viewBox="0 0 24 24"
class="absolute fill-none skew-y-2${from
? ` hidden ${from}:inline-block`
: ""}"
xmlns="http://www.w3.org/2000/svg"
style=${rectToStyle(rect)}
>
<path
d="M11.3255 22.5826C11.3255 22.8897 11.5745 23.1387 11.8816 23.1387C12.1887 23.1387 12.4377 22.8897 12.4377 22.5826C12.4377 19.3351 13.2489 16.7277 14.8868 14.848C16.5218 12.9717 19.044 11.7477 22.6145 11.3906C22.9201 11.3601 23.1431 11.0875 23.1125 10.7819C23.082 10.4763 22.8094 10.2533 22.5038 10.2839C18.9253 10.6417 16.4423 9.91532 14.8501 8.40653C13.2524 6.89252 12.4377 4.48364 12.4377 1.22746C12.4377 0.920325 12.1887 0.67134 11.8816 0.67134C11.5745 0.67134 11.3255 0.920325 11.3255 1.22746C11.3255 4.47517 10.516 7.08239 8.87909 8.96186C7.24516 10.8379 4.72305 12.062 1.1487 12.4194C0.843091 12.45 0.620117 12.7225 0.650678 13.0281C0.681239 13.3337 0.953763 13.5567 1.25938 13.5261C4.84181 13.1679 7.32467 13.8944 8.91581 15.4031C10.5125 16.9171 11.3255 19.3261 11.3255 22.5826Z"
fill="#FDCC80"
/>
<path
d="M11.3255 22.5826C11.3255 22.8897 11.5745 23.1387 11.8816 23.1387C12.1887 23.1387 12.4377 22.8897 12.4377 22.5826C12.4377 19.3351 13.2489 16.7277 14.8868 14.848C16.5218 12.9717 19.044 11.7477 22.6145 11.3906C22.9201 11.3601 23.1431 11.0875 23.1125 10.7819C23.082 10.4763 22.8094 10.2533 22.5038 10.2839C18.9253 10.6417 16.4423 9.91532 14.8501 8.40653C13.2524 6.89252 12.4377 4.48364 12.4377 1.22746C12.4377 0.920325 12.1887 0.67134 11.8816 0.67134C11.5745 0.67134 11.3255 0.920325 11.3255 1.22746C11.3255 4.47517 10.516 7.08239 8.87909 8.96186C7.24516 10.8379 4.72305 12.062 1.1487 12.4194C0.843091 12.45 0.620117 12.7225 0.650678 13.0281C0.681239 13.3337 0.953763 13.5567 1.25938 13.5261C4.84181 13.1679 7.32467 13.8944 8.91581 15.4031C10.5125 16.9171 11.3255 19.3261 11.3255 22.5826Z"
fill="url(#paint0_linear_3_70)"
/>
<path
d="M11.3255 22.5826C11.3255 22.8897 11.5745 23.1387 11.8816 23.1387C12.1887 23.1387 12.4377 22.8897 12.4377 22.5826C12.4377 19.3351 13.2489 16.7277 14.8868 14.848C16.5218 12.9717 19.044 11.7477 22.6145 11.3906C22.9201 11.3601 23.1431 11.0875 23.1125 10.7819C23.082 10.4763 22.8094 10.2533 22.5038 10.2839C18.9253 10.6417 16.4423 9.91532 14.8501 8.40653C13.2524 6.89252 12.4377 4.48364 12.4377 1.22746C12.4377 0.920325 12.1887 0.67134 11.8816 0.67134C11.5745 0.67134 11.3255 0.920325 11.3255 1.22746C11.3255 4.47517 10.516 7.08239 8.87909 8.96186C7.24516 10.8379 4.72305 12.062 1.1487 12.4194C0.843091 12.45 0.620117 12.7225 0.650678 13.0281C0.681239 13.3337 0.953763 13.5567 1.25938 13.5261C4.84181 13.1679 7.32467 13.8944 8.91581 15.4031C10.5125 16.9171 11.3255 19.3261 11.3255 22.5826Z"
stroke="#FDCC80"
stroke-width="1.11225"
stroke-linejoin="round"
/>
<defs>
<linearGradient
id="paint0_linear_3_70"
x1="11.8816"
y1="1.22746"
x2="11.8816"
y2="22.5826"
gradientUnits="userSpaceOnUse"
>
<stop stop-color="#FFE171" />
<stop offset="1" stop-color="white" stop-opacity="0" />
</linearGradient>
</defs>
</svg>`;
}
function Circle(rect) {
const { html } = globalThis.__enhancerApi;
return html`<div
class="absolute rounded-full
border-(~ purple-500) bg-purple-400"
style=${rectToStyle(rect)}
></div>`;
}
function Banner({ updateAvailable, isDevelopmentBuild }) {
const { html, useState } = globalThis.__enhancerApi,
{ version, initDatabase } = globalThis.__enhancerApi,
$version = html`<button
class="text-[12px] py-[2px] px-[6px] mt-[2px]
font-medium leading-tight tracking-wide rounded-[3px]
relative bg-purple-500 from-white/[0.18] to-white/[0.16]
bg-[linear-gradient(225deg,var(--tw-gradient-stops))]"
>
<div
class="notion-enhancer--menu-update-indicator
absolute size-[12px] right-[-6px] top-[-6px]
${updateAvailable ? "" : "hidden"}"
>
<span
class="block rounded-full h-full w-full
absolute bg-purple-500/75 animate-ping"
></span>
<span
class="block rounded-full h-full w-full
relative bg-purple-500"
></span>
</div>
<span class="relative">v${version}</span>
</button>`,
$popup = html`<${Popup} trigger=${$version}>
<p
class="typography py-[2px] px-[8px] text-[14px]"
innerHTML=${updateAvailable
? `<b>v${updateAvailable}</b> is available! <a href="${updateGuide}">Update now.</a>`
: isDevelopmentBuild
? "This is a development build of the notion-enhancer. It may be unstable."
: "You're up to date!"}
/>
<//>`;
$version.append($popup);
if (updateAvailable) {
useState(["focus", "view"], ([, view = "welcome"]) => {
if (view !== "welcome") return;
// delayed appearance = movement attracts eye
setTimeout(() => $popup.open(), 400);
});
}
const $welcome = html`<div
class="relative flex overflow-hidden h-[192px] rounded-t-[4px]
border-(~ purple-400) bg-purple-500 from-white/20 to-transparent
text-white bg-[linear-gradient(225deg,var(--tw-gradient-stops))]"
>
<${Circle} width="128px" height="128px" bottom="-64px" left="-64px" />
<${Circle} width="144px" height="144px" top="-108px" left="80px" />
<${Circle} width="208px" height="208px" bottom="-64px" right="-16px" />
<${Circle} width="144px" height="144px" bottom="-72px" right="144px" />
<${Star} width="36px" height="36px" top="136px" left="190px" />
<${Star} width="48px" height="48px" top="32px" left="336px" />
<${Star} width="64px" height="64px" top="90px" left="448px" from="lg" />
<h1
class="z-10 px-[32px] md:px-[48px] lg:px-[64px]
font-bold leading-tight tracking-tight my-auto"
>
<a href="https://notion-enhancer.github.io/">
<span class="text-[26px]">Welcome to</span><br />
<span class="text-[28px]">the notion-enhancer</span>
</a>
</h1>
<div
class="absolute bottom-0 right-0 py-[24px]
px-[32px] md:px-[48px] lg:px-[64px]"
>
<div class="relative flex-(~ col)">
<i class="i-notion-enhancer text-[42px] mx-auto mb-[8px]"></i>
${$version}
</div>
</div>
</div>`,
$sponsorship = html`<div
class="py-[18px] px-[16px] rounded-b-[4px]
border-(~ [color:var(--theme--fg-border)]) bg-[color:var(--theme--bg-secondary)]"
>
<div class="flex items-center gap-[16px]">
<p class="text-[14px] font-semibold">
Enjoying the notion-enhancer?<br />
Support future development:
</p>
<${Button}
icon="coffee"
variant="brand"
class="grow justify-center"
href="https://www.buymeacoffee.com/dragonwocky"
>Buy me a coffee
<//>
<${Button}
icon="calendar-heart"
variant="brand"
class="grow justify-center"
href="https://github.com/sponsors/dragonwocky"
>Sponsor me
<//>
</div>
<!-- Disclaimer: draft of potential perks, to be confirmed before full release. -->
<${Description} class="mt-[6px]">
<!-- Sponsors help make open-source development sustainable and receive
access to priority support channels, private developer previews, and
role cosmetics on Discord. A one-time donation is equivalent to 1 month
of sponsor perks. To learn more about perks, read the
<a href=${tsAndCs} class="ml-[3px]">Terms & Conditions</a>. -->
<//>
</div>`;
initDatabase()
.get("agreedToTerms")
.then((agreedToTerms) => {
// only show sponsorship if already agree to terms
// and opening menu after having reloaded since agreeing
$welcome.style.borderRadius = agreedToTerms === version ? "" : "4px";
$sponsorship.style.display = agreedToTerms === version ? "" : "none";
});
return html`<section class="notion-enhancer--menu-banner">
${$welcome}${$sponsorship}
</section>`;
}
export { Banner };

View File

@ -0,0 +1,45 @@
/**
* notion-enhancer
* (c) 2023 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
* (https://notion-enhancer.github.io/) under the MIT license
*/
function Button({ icon, variant, tagName, ...props }, ...children) {
const { html, extendProps } = globalThis.__enhancerApi;
extendProps(props, {
class: `notion-enhancer--menu-button shrink-0
flex gap-[8px] items-center px-[12px] rounded-[4px]
h-[${variant === "sm" ? "28" : "32"}px] select-none
transition duration-[20ms] ${
variant === "primary"
? `text-[color:var(--theme--accent-primary\\_contrast)]
font-medium bg-[color:var(--theme--accent-primary)]
hover:bg-[color:var(--theme--accent-primary\\_hover)]`
: variant === "secondary"
? `text-[color:var(--theme--accent-secondary)]
border-(~ [color:var(--theme--accent-secondary)])
hover:bg-[color:var(--theme--accent-secondary\\_hover)]`
: variant === "brand"
? `text-white border-(~ purple-400)
bg-purple-500 hover:(from-white/20 to-transparent
bg-[linear-gradient(225deg,var(--tw-gradient-stops))])`
: `border-(~ [color:var(--theme--fg-border)])
not-disabled:hover:bg-[color:var(--theme--bg-hover)]
disabled:text-[color:var(--theme--fg-secondary)]`
}`,
});
tagName ??= props["href"] ? "a" : "button";
return html`<${tagName} ...${props}>
${icon
? html`<i
class="i-${icon}
text-[${variant === "sm" && children.length ? "13" : "17"}px]"
></i>`
: ""}
<span class="text-[${variant === "sm" ? "13" : "14"}px] empty:hidden">
${children}
</span>
<//>`;
}
export { Button };

View File

@ -0,0 +1,47 @@
/**
* notion-enhancer
* (c) 2023 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
* (https://notion-enhancer.github.io/) under the MIT license
*/
function Checkbox({ _get, _set, _requireReload = true, ...props }) {
let _initialValue;
const { html, extendProps, setState, useState } = globalThis.__enhancerApi,
$input = html`<input
type="checkbox"
class="hidden [&:checked+div]:(px-px bg-[color:var(--theme--accent-primary)])
[&:not(:checked)+div>i]:text-transparent [&:not(:checked)+div]:(border-(~
[color:var(--theme--fg-primary)]) hover:bg-[color:var(--theme--bg-hover)])"
...${props}
/>`;
extendProps($input, { onchange: () => _set?.($input.checked) });
useState(["rerender"], async () => {
const checked = (await _get?.()) ?? $input.checked;
$input.checked = checked;
if (_requireReload) {
_initialValue ??= checked;
if (checked !== _initialValue) setState({ databaseUpdated: true });
}
});
return html`<label
tabindex="0"
class="notion-enhancer--menu-checkbox cursor-pointer"
onkeydown=${(event) => {
if ([" ", "Enter"].includes(event.key)) {
event.preventDefault();
$input.click();
}
}}
>
${$input}
<div class="flex items-center h-[16px] transition duration-200">
<i
class="i-check size-[14px]
text-[color:var(--theme--accent-primary\\_contrast)]"
></i>
</div>
</label>`;
}
export { Checkbox };

View File

@ -0,0 +1,16 @@
/**
* notion-enhancer
* (c) 2023 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
* (https://notion-enhancer.github.io/) under the MIT license
*/
function Description(props, ...children) {
const { html, extendProps } = globalThis.__enhancerApi;
extendProps(props, {
class: `notion-enhancer--menu-description typography
leading-[16px] text-([12px] [color:var(--theme--fg-secondary)])`,
});
return html`<p ...${props}>${children}</p>`;
}
export { Description };

View File

@ -0,0 +1,59 @@
/**
* notion-enhancer
* (c) 2023 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
* (https://notion-enhancer.github.io/) under the MIT license
*/
import { Button } from "./Button.mjs";
function Footer({ categories, transitionDuration = 150 }) {
const { html, setState, useState, reloadApp } = globalThis.__enhancerApi,
$reload = html`<${Button}
class="ml-auto"
variant="primary"
icon="refresh-cw"
onclick=${reloadApp}
style="display: none"
>Reload & Apply Changes
<//>`,
$categories = categories.map(({ id, title, mods }) => {
return [
mods.map((mod) => mod.id),
html`<${Button}
icon="chevron-left"
onclick=${() => setState({ transition: "slide-to-left", view: id })}
>${title}
<//>`,
];
});
useState(["view"], ([view]) => {
let [footerOpen] = useState(["databaseUpdated"]);
footerOpen ||= $categories.some(([ids]) => ids.some((id) => id === view));
setState({ footerOpen });
});
useState(["databaseUpdated"], ([databaseUpdated]) => {
$reload.style.display = databaseUpdated ? "" : "none";
if (databaseUpdated) setState({ footerOpen: true });
});
useState(["footerOpen"], ([footerOpen]) => {
// only toggle buttons if footer is open,
// otherwise leave as is during transition
if (!footerOpen) return;
const [view] = useState(["view"]);
for (const [ids, $btn] of $categories) {
const viewInCategory = ids.some((id) => id === view);
$btn.style.display = viewInCategory ? "" : "none";
}
});
return html`<footer
class="notion-enhancer--menu-footer px-[60px] py-[16px]
flex w-full bg-[color:var(--theme--bg-primary)] h-[64px]
border-t-(~ [color:var(--theme--fg-border)])"
>
${$categories.map(([, $btn]) => $btn)}${$reload}
</footer>`;
}
export { Footer };

View File

@ -0,0 +1,17 @@
/**
* notion-enhancer
* (c) 2023 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
* (https://notion-enhancer.github.io/) under the MIT license
*/
function Heading(props, ...children) {
const { html, extendProps } = globalThis.__enhancerApi;
extendProps(props, {
class: `notion-enhancer--menu-heading text-[16px]
font-semibold mb-[16px] mt-[48px] first:mt-0 pb-[12px]
border-b-(~ [color:var(--theme--fg-border)])`,
});
return html`<h4 ...${props}>${children}</h4>`;
}
export { Heading };

View File

@ -0,0 +1,200 @@
/**
* notion-enhancer
* (c) 2023 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
* (https://notion-enhancer.github.io/) under the MIT license
*/
const updateHotkey = (event) => {
const keys = [];
for (const modifier of ["metaKey", "ctrlKey", "altKey", "shiftKey"]) {
if (!event[modifier]) continue;
const alias = modifier[0].toUpperCase() + modifier.slice(1, -3);
keys.push(alias);
}
// retain keyboard navigation of menu
if (["Tab", "Escape"].includes(event.key) && !keys.length) {
return;
} else event.preventDefault();
if (!keys.length && ["Backspace", "Delete"].includes(event.key)) {
event.target.value = "";
} else if (event.key) {
let key = event.key;
if (key === " ") key = "Space";
if (["+", "="].includes(key)) key = "Plus";
if (key === "-") key = "Minus";
if (key === "|") key = "\\";
if (event.code === "Comma") key = ",";
if (event.code === "Period") key = ".";
if (key === "Control") key = "Ctrl";
// avoid e.g. Shift+Shift, force inclusion of non-modifier
if (keys.includes(key)) return;
keys.push(key.length === 1 ? key.toUpperCase() : key);
event.target.value = keys.join("+");
}
event.target.dispatchEvent(new Event("input"));
event.target.dispatchEvent(new Event("change"));
},
updateContrast = ($input, $icon) => {
$input.style.background = $input.value;
const [r, g, b, a = 1] = $input.value
.replace(/^rgba?\(/, "")
.replace(/\)$/, "")
.split(",")
.map((n) => parseFloat(n));
if (a > 0.5) {
// pick a contrasting foreground for an rgb background
// using the percieved brightness constants from http://alienryderflex.com/hsp.html
const brightness = 0.299 * (r * r) + 0.587 * (g * g) + 0.114 * (b * b);
$input.style.color = Math.sqrt(brightness) > 165.75 ? "#000" : "#fff";
} else $input.style.color = "#000";
$icon.style.color = $input.style.color;
$icon.style.opacity = "0.7";
},
readUpload = async (event) => {
const file = event.target.files[0],
reader = new FileReader();
return new Promise((res) => {
reader.onload = async (progress) => {
const content = progress.currentTarget.result,
upload = { filename: file.name, content };
res(upload);
};
reader.readAsText(file);
});
};
function Input({
type,
icon,
variant,
extensions,
class: className,
_get,
_set,
_requireReload = true,
...props
}) {
let $filename, $clear;
const { html, extendProps, setState, useState } = globalThis.__enhancerApi;
Coloris({ format: "rgb" });
type ??= "text";
if (type === "text") icon ??= "text-cursor";
if (type === "number") icon ??= "hash";
if (type === "hotkey") icon ??= "command";
if (type === "color") icon ??= "pipette";
if (type === "file") {
icon ??= "file-up";
$filename = html`<span class="ml-[6px]">Upload a file</span>`;
$clear = html`<button
style="display: none"
class="h-[14px] transition duration-[20ms]
flex text-[color:var(--theme--fg-secondary)]
hover:text-[color:var(--theme--fg-primary)]"
onclick=${() => _set?.({ filename: "", content: "" })}
>
<i class="i-x size-[14px]"></i>
</button>`;
props.accept = extensions
?.map((ext) => (ext.startsWith(".") ? ext : `.${ext}`))
.join(",");
}
const $input = html`<input
type=${["hotkey", "color"].includes(type) ? "text" : type}
class="h-full w-full pb-px text-[14px] leading-[1.2]
${variant === "lg" ? "pl-[12px] pr-[40px]" : "pl-[8px] pr-[32px]"}
appearance-none bg-transparent ${type === "file" ? "hidden" : ""}
${type === "hotkey" ? "text-[color:var(--theme--fg-secondary)]" : ""}
${type === "color"
? "font-medium"
: "border-(~ [color:var(--theme--fg-border)])"}"
data-coloris=${type === "color"}
...${props}
/>`,
$icon = html`<span
class="${variant === "lg" ? "pr-[12px]" : "pr-[8px]"}
absolute flex items-center h-full pointer-events-none
text-[color:var(--theme--fg-secondary)] right-0 top-0"
><i class="i-${icon} size-[16px]"></i>
</span>`;
let _initialValue;
extendProps($input, {
onclick: () => {
// change text to "uploading..." until file has uploaded
// to reassure users experiencing latency while file is processed
if (type === "file") $filename.innerText = "Uploading...";
},
onchange: (event) => {
if (_set && type === "file") {
readUpload(event)
.then(_set)
// refocus iframe after file has uploaded,
// sometimes switching back after opening the
// native file upload menu causes a loss of focus
.then(() => window.focus());
} else _set?.($input.value);
},
onrerender: async () => {
const value = (await _get?.()) ?? $input.value ?? "";
if (type === "file") {
$filename.innerText = value?.filename || "Upload a file";
$clear.style.display = value?.filename ? "" : "none";
if (_requireReload) {
_initialValue ??= value?.content || "";
if ((value?.content || "") !== _initialValue) {
setState({ databaseUpdated: true });
}
}
} else {
if ($input.value !== value) $input.value = value;
if (_requireReload) {
_initialValue ??= value;
if (value !== _initialValue) setState({ databaseUpdated: true });
}
if (type === "color") updateContrast($input, $icon);
}
},
onkeydown: type === "hotkey" ? updateHotkey : undefined,
oninput: type === "color" ? () => _set?.($input.value) : undefined,
});
useState(["rerender"], () => $input.onrerender?.());
return type === "file"
? html`<div
class="notion-enhancer--menu-file-input shrink-0
flex items-center gap-[8px] ${className ?? ""}"
>
<label
tabindex="0"
class="flex items-center cursor-pointer select-none
px-[8px] bg-[color:var(--theme--bg-secondary)]
h-[28px] rounded-[4px] transition duration-[20ms]
text-([14px] [color:var(--theme--fg-secondary)])
border-(~ [color:var(--theme--fg-border)])
hover:bg-[color:var(--theme--bg-hover)]"
onkeydown=${(event) => {
if ([" ", "Enter"].includes(event.key)) {
event.preventDefault();
$input.click();
}
}}
>${$input}${$icon.children[0]}${$filename}
</label>
${$clear}
</div>`
: html`<label
class="notion-enhancer--menu-input
${variant === "lg" ? "h-[32px]" : "h-[28px]"}
relative overflow-hidden rounded-[4px] w-full inline-block
focus-within:ring-(~ [color:var(--theme--accent-primary)])
${className ?? ""} ${type === "color"
? "bg-([image:repeating-linear-gradient(45deg,#aaa_25%,transparent_25%,transparent_75%,#aaa_75%,#aaa),repeating-linear-gradient(45deg,#aaa_25%,#fff_25%,#fff_75%,#aaa_75%,#aaa)] [position:0_0,4px_4px] [size:8px_8px])"
: "bg-[color:var(--theme--bg-hover)]"}"
>${$input}${$icon}
</label>`;
}
export { Input };

View File

@ -0,0 +1,72 @@
/**
* notion-enhancer
* (c) 2023 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
* (https://notion-enhancer.github.io/) under the MIT license
*/
import { Description } from "./Description.mjs";
import { Input } from "./Input.mjs";
import { Mod } from "./Mod.mjs";
function Search({ items, itemType }) {
const { html, addKeyListener } = globalThis.__enhancerApi,
$search = html`<${Input}
type="text"
icon="search"
variant="lg"
placeholder="Search ${items.length} ${items.length === 1
? itemType.replace(/s$/, "")
: itemType} (Press '/' to focus)"
oninput=${(event) => {
const query = event.target.value.toLowerCase();
for (const $item of items) {
const matches = $item.innerText.toLowerCase().includes(query);
$item.style.display = matches ? "" : "none";
}
}}
/>`;
addKeyListener("/", (event) => {
if (document.activeElement?.nodeName === "INPUT") return;
// offsetParent == null if parent has "display: none;"
if ($search.offsetParent) {
event.preventDefault();
$search.focus();
}
});
return $search;
}
function List({ id, mods, description }) {
const { html, setState } = globalThis.__enhancerApi,
{ isEnabled, setEnabled } = globalThis.__enhancerApi,
$mods = mods.map((mod) => {
const _get = () => isEnabled(mod.id),
_set = async (enabled) => {
await setEnabled(mod.id, enabled);
// only one theme may be enabled per
// mode at a time => auto-disable other
// enabled themes of matching mode
if (enabled && id === "themes") {
const isDark = mod.tags.includes("dark"),
isLight = mod.tags.includes("light");
for (const other of mods) {
if (other.id === mod.id) continue;
const otherDark = other.tags.includes("dark"),
otherLight = other.tags.includes("light");
if ((isDark && otherDark) || (isLight && otherLight)) {
await setEnabled(other.id, false);
}
}
}
setState({ rerender: true });
};
return html`<${Mod} ...${{ ...mod, _get, _set }} />`;
});
return html`<div class="flex-(~ col) gap-y-[14px]">
<${Search} items=${$mods} itemType=${id} />
<${Description} innerHTML=${description} />
${$mods}
</div>`;
}
export { List };

View File

@ -0,0 +1,84 @@
/**
* notion-enhancer
* (c) 2023 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
* (https://notion-enhancer.github.io/) under the MIT license
*/
import { Description } from "./Description.mjs";
import { Toggle } from "./Toggle.mjs";
function Mod({
id,
name,
version,
description,
thumbnail,
tags = [],
authors,
options = [],
_get,
_set,
_src,
}) {
const { html, setState, enhancerUrl } = globalThis.__enhancerApi,
toggleId = Math.random().toString(36).slice(2, 5);
return html`<label
for=${toggleId}
class="notion-enhancer--menu-mod flex items-stretch rounded-[4px]
bg-[color:var(--theme--bg-secondary)] w-full py-[18px] px-[16px]
border border-[color:var(--theme--fg-border)] cursor-pointer
duration-[20ms] hover:bg-[color:var(--theme--bg-hover)]
transition [&+.notion-enhancer--menu-option]:mt-[24px]"
>
${thumbnail
? html`<img
src="${enhancerUrl(`${_src}/${thumbnail}`)}"
class="rounded-[4px] mr-[12px] h-[74px] aspect-video my-auto"
/>`
: ""}
<div class="flex-(~ col) w-full">
<div class="flex flex-wrap items-center gap-[8px] text-[14px] mb-[5px]">
<h3 class="my-0">${name}</h3>
${[`v${version}`, ...tags].map((tag) => {
return html`<span
class="text-([12px] [color:var(--theme--fg-secondary)]
nowrap) leading-tight tracking-wide py-[2px] px-[6px]
rounded-[3px] bg-[color:var(--theme--bg-hover)]"
>${tag}
</span>`;
})}
</div>
<${Description} class="mb-[6px] max-w-[80%]" innerHTML=${description} />
<div class="mt-auto flex gap-x-[8px] text-[12px] leading-[16px]">
${authors.map((author) => {
return html`<a href=${author.homepage} class="flex items-center">
<img src=${author.avatar} alt="" class="h-[12px] rounded-full" />
<span class="ml-[6px]">${author.name}</span>
</a>`;
})}
</div>
</div>
<div class="flex ml-auto">
${options.length
? html`<button
class="flex items-center p-[4px] rounded-[4px] transition
text-[color:var(--theme--fg-secondary)] my-auto mr-[8px]
duration-[20ms] hover:bg-[color:var(--theme--bg-hover)]
active:text-[color:var(--theme--fg-primary)]"
onclick=${() => {
setState({ transition: "slide-to-right", view: id });
}}
>
<i class="i-settings size-[18px]"></i>
</button>`
: ""}
<div class="my-auto scale-[1.15]">
<${Toggle} id=${toggleId} ...${{ _get, _set }} />
</div>
</div>
</label>`;
}
export { Mod };

View File

@ -0,0 +1,108 @@
/**
* notion-enhancer
* (c) 2023 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
* (https://notion-enhancer.github.io/) under the MIT license
*/
import { Heading } from "./Heading.mjs";
import { Description } from "./Description.mjs";
import { Checkbox } from "./Checkbox.mjs";
import { Button } from "./Button.mjs";
import { Tile } from "./Tile.mjs";
const privacyPolicy = "https://notion-enhancer.github.io/about/privacy-policy/",
tsAndCs = "https://notion-enhancer.github.io/about/terms-and-conditions/";
function Onboarding() {
const { html, setState, useState } = globalThis.__enhancerApi,
{ version, initDatabase } = globalThis.__enhancerApi,
$submitAgreement = html`<${Button}
icon="arrow-right"
class="ml-auto"
disabled
>Continue
<//>`,
$agreeToTerms = html`<div class="mt-[32px]">
<${Heading} class="mb-[8px]">
Thanks for installing the notion-enhancer!
<//>
<${Description}>
In order for the notion-enhancer to function, it may access, collect,
process and/or store data on your device (including workspace content,
device metadata, and notion-enhancer configuration) as described in its
privacy policy. Unless otherwise stated, the notion-enhancer will never
transmit personally identifiable information from your device.
<br /><br />
The notion-enhancer is free and open-source software distributed under
the <a href="${tsAndCs}#license">MIT License</a> without warranty of any
kind. In no event shall the authors be liable for any consequences of
the software's use. Before continuing, you must read and agree to the
notion-enhancer's privacy policy and terms & conditions.
<//>
<div class="flex items-center my-[14px] gap-[8px]">
<!-- _requireReload=${false} prevents the footer from
suggesting a reload of the app when the box is checked -->
<${Checkbox}
_set=${(checked) => ($submitAgreement.disabled = !checked)}
_requireReload=${false}
/>
<p class="typography text-[14px] mr-[16px]">
I have read and agree to the
<a class="mx-[4px]" href=${privacyPolicy}>Privacy Policy</a>
and <a href=${tsAndCs}>Terms & Conditions</a>.
</p>
${$submitAgreement}
</div>
</div>`;
$submitAgreement.onclick = async () => {
if ($submitAgreement.disabled) return;
await initDatabase().set("agreedToTerms", version);
setState({ rerender: true });
};
const $regularGreeting = html`<div
class="mt-[16px] grid-(~ cols-3) gap-[16px]"
>
<${Tile}
href="https://notion-enhancer.github.io/getting-started/basic-usage/"
icon="graduation-cap"
title="Stuck?"
>Check out the usage guide.
<//>
<${Tile}
href="https://notion-enhancer.github.io/documentation/mods/"
icon="package-plus"
title="Something missing?"
>Build your own extension.
<//>
<${Tile}
href="https://github.com/notion-enhancer/notion-enhancer/issues"
icon="bug"
title="Something broken?"
>Report a bug.
<//>
</div>`,
$featuredSponsors = html`
<div class="mt-[32px]">
<${Heading} class="mb-[8px]">Featured Sponsors<//>
<${Description}>
A few awesome companies out there have teamed up with me to provide
you with the notion-enhancer, free forever. Check them out!
<//>
<div class="mt-[16px] grid-(~ cols-1) gap-[16px]"></div>
<${Description} class="mt-[12px]">
<a href="mailto:thedragonring.bod@gmail.com">Join this list.</a>
<//>
</div>
`;
useState(["rerender"], async () => {
const agreedToTerms = await initDatabase().get("agreedToTerms");
$agreeToTerms.style.display = agreedToTerms === version ? "none" : "";
$regularGreeting.style.display = agreedToTerms === version ? "" : "none";
$featuredSponsors.style.display = agreedToTerms === version ? "" : "none";
});
return html`${$agreeToTerms}${$regularGreeting}`;
}
export { Onboarding };

View File

@ -0,0 +1,100 @@
/**
* notion-enhancer
* (c) 2023 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
* (https://notion-enhancer.github.io/) under the MIT license
*/
import { Heading } from "./Heading.mjs";
import { Description } from "./Description.mjs";
import { Input } from "./Input.mjs";
import { Select } from "./Select.mjs";
import { Toggle } from "./Toggle.mjs";
const camelToSentenceCase = (string) =>
string[0].toUpperCase() +
string.replace(/[A-Z]/g, (match) => ` ${match.toLowerCase()}`).slice(1),
filterOptionsForRender = (options) => {
const { platform } = globalThis.__enhancerApi;
options = options.reduce((options, opt) => {
// option must have key, headings may use label
if (!opt.key && (opt.type !== "heading" || !opt.label)) return options;
// ignore platform-specific options
if (opt.platforms && !opt.platforms.includes(platform)) return options;
// replace consective headings
opt._autoremoveIfSectionEmpty ??= true;
const prev = options[options.length - 1],
canReplacePrev =
prev?._autoremoveIfSectionEmpty && prev?.type === opt.type;
if (opt.type === "heading" && canReplacePrev) {
options[options.length - 1] = opt;
} else options.push(opt);
return options;
}, []);
// remove trailing heading
return options.at(-1)?.type === "heading" &&
options.at(-1)?._autoremoveIfSectionEmpty
? options.slice(0, -1)
: options;
};
function Option({ _get, _set, ...opt }) {
const { html } = globalThis.__enhancerApi;
return html`<${opt.type === "toggle" ? "label" : "div"}
class="notion-enhancer--menu-option flex items-center justify-between
mb-[18px] ${opt.type === "toggle" ? "cursor-pointer" : ""}"
>
<div class="flex-(~ col) ${opt.type === "text" ? "w-full" : "mr-[10%]"}">
<h5 class="text-[14px] mb-[2px] mt-0">${opt.label}</h5>
${opt.type === "text"
? html`<${Input}
type="text"
class="mt-[4px] mb-[8px]"
...${{ _get, _set }}
/>`
: ""}
${["string", "undefined"].includes(typeof opt.description)
? html`<${Description} innerHTML=${opt.description} />`
: html`<${Description}>${opt.description}<//>`}
</div>
${["number", "hotkey", "color"].includes(opt.type)
? html`<${Input}
type=${opt.type}
class="shrink-0 !w-[192px]"
...${{ _get, _set }}
/>`
: opt.type === "file"
? html`<${Input}
type="file"
extensions=${opt.extensions}
...${{ _get, _set }}
/>`
: opt.type === "select"
? html`<${Select} values=${opt.values} ...${{ _get, _set }} />`
: opt.type === "toggle"
? html`<${Toggle} ...${{ _get, _set }} />`
: ""}
<//>`;
}
function Options({ mod }) {
const { html, modDatabase, setState } = globalThis.__enhancerApi;
return filterOptionsForRender(mod.options).map((opt) => {
opt.label ??= camelToSentenceCase(opt.key);
if (opt.type === "heading") {
return typeof opt.description === "string"
? html`<div class="mb-[18px]">
<${Heading}>${opt.label}<//>
<${Description} innerHTML=${opt.description} />
</div>`
: html`<${Heading}>${opt.label}<//>`;
}
const _get = async () => (await modDatabase(mod.id)).get(opt.key),
_set = async (value) => {
await (await modDatabase(mod.id)).set(opt.key, value);
setState({ rerender: true });
};
return html`<${Option} ...${{ _get, _set, ...opt }} />`;
});
}
export { Options, Option };

View File

@ -0,0 +1,82 @@
/**
* notion-enhancer
* (c) 2023 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
* (https://notion-enhancer.github.io/) under the MIT license
*/
function Popup(
{ trigger, mode = "left", width = 250, maxWidth, ...props },
...children
) {
const { html, extendProps, setState, useState } = globalThis.__enhancerApi,
// known values for mode:
// dropdown => panel switcher
isDropdown = mode === "dropdown",
// left => menu option select
isLeft = mode === "left";
extendProps(props, {
class: `notion-enhancer--menu-popup group/popup
absolute top-0 left-0 z-20 text-left font-normal
flex-(~ col) justify-center pointer-events-none
items-end w-full ${isDropdown ? "" : "h-full"}`,
});
const $popup = html`<div ...${props}>
<div
class="relative ${isDropdown ? "w-full" : ""}
${isLeft ? "right-[calc(100%+8px)]" : ""}"
>
<div
class="bg-[color:var(--theme--bg-secondary)]
rounded-[4px] overflow-y-auto drop-shadow-xl max-h-[70vh]
${isDropdown ? "w-full" : "w-[250px] max-w-[calc(100vw-24px)]"}
transition duration-200 opacity-0 scale-95 py-[6px] px-[4px]
group-open/popup:( pointer-events-auto opacity-100 scale-100)"
>
${children}
</div>
</div>
</div>`;
$popup.open = () => {
$popup.setAttribute("open", true);
$popup.querySelectorAll("[tabindex]").forEach(($el) => ($el.tabIndex = 0));
setState({ popupOpen: true });
$popup.onopen?.();
};
$popup.close = () => {
$popup.onbeforeclose?.();
$popup.removeAttribute("open");
$popup.style.pointerEvents = "auto";
$popup.querySelectorAll("[tabindex]").forEach(($el) => ($el.tabIndex = -1));
setTimeout(() => {
$popup.style.pointerEvents = "";
setState({ popupOpen: false });
$popup.onclose?.();
}, 200);
};
$popup.querySelectorAll("[tabindex]").forEach(($el) => ($el.tabIndex = -1));
document.addEventListener("click", (event) => {
if (!$popup.hasAttribute("open")) return;
if ($popup.contains(event.target) || $popup === event.target) return;
if (trigger?.contains(event.target) || trigger === event.target) return;
$popup.close();
});
useState(["rerender"], () => {
if ($popup.hasAttribute("open")) $popup.close();
});
if (!trigger) return $popup;
extendProps(trigger, {
onclick: $popup.open,
onkeydown(event) {
if ([" ", "Enter"].includes(event.key)) {
event.preventDefault();
$popup.open();
}
},
});
return $popup;
}
export { Popup };

View File

@ -0,0 +1,237 @@
/**
* notion-enhancer
* (c) 2023 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
* (https://notion-enhancer.github.io/) under the MIT license
*/
import { Heading } from "./Heading.mjs";
import { Description } from "./Description.mjs";
import { Checkbox } from "./Checkbox.mjs";
import { Button } from "./Button.mjs";
import { Input } from "./Input.mjs";
import { Popup } from "./Popup.mjs";
function Profile({ id }) {
const { html, setState } = globalThis.__enhancerApi,
{ getProfile, initDatabase } = globalThis.__enhancerApi,
profile = initDatabase([id]),
db = initDatabase();
const getName = async () => {
let profileName = await profile.get("profileName");
if (id === "default") profileName ??= "default";
return profileName ?? "";
},
setName = async (name) => {
// name only has effect in menu
// doesn't need to trigger reload
await profile.set("profileName", name);
},
isActive = async () => {
return id === (await getProfile());
},
setActive = async () => {
if (await isActive()) return;
await db.set("activeProfile", id);
setState({ rerender: true });
};
const $successName = html`<span
class="py-[2px] px-[4px] rounded-[3px]
bg-[color:var(--theme--bg-hover)]"
></span>`,
$uploadSuccess = html`<${Popup}
onopen=${async () => ($successName.innerText = await getName())}
>
<p class="py-[2px] px-[8px] text-[14px]">
The profile ${$successName} has been updated successfully.
</p>
<//>`,
$uploadError = html`<${Popup}>
<p
class="py-[2px] px-[8px] text-[14px]
text-[color:var(--theme--accent-secondary)]"
>
An error was encountered attempting to parse the uploaded file.
</p>
<//>`,
uploadProfile = (event) => {
const file = event.target.files[0],
reader = new FileReader();
reader.onload = async (progress) => {
try {
let res = progress.currentTarget.result;
res = JSON.parse(res);
delete res["profileName"];
await profile.import(res);
setState({ rerender: true });
$uploadSuccess.open();
setTimeout(() => $uploadSuccess.close(), 2000);
} catch (err) {
$uploadError.open();
setTimeout(() => $uploadError.close(), 2000);
}
// clear input value to allow repeat uploads
event.target.value = "";
};
reader.readAsText(file);
},
downloadProfile = async () => {
const now = new Date(),
year = now.getFullYear().toString(),
month = (now.getMonth() + 1).toString().padStart(2, "0"),
day = now.getDate().toString().padStart(2, "0"),
hour = now.getHours().toString().padStart(2, "0"),
min = now.getMinutes().toString().padStart(2, "0"),
sec = now.getSeconds().toString().padStart(2, "0"),
date = year + month + day + hour + min + sec;
const $a = html`<a
class="hidden"
download="notion-enhancer_${await getName()}_${date}.json"
href="data:text/json;charset=utf-8,${encodeURIComponent(
JSON.stringify(await profile.export())
)}"
/>`;
document.body.append($a);
$a.click();
$a.remove();
},
$uploadInput = html`<input
type="file"
class="hidden"
accept=".json"
onchange=${uploadProfile}
/>`;
const deleteProfile = async () => {
let profileIds = await db.get("profileIds");
if (!profileIds?.length) profileIds = ["default"];
// clear profile data
const keys = Object.keys(await profile.export());
await profile.remove(keys);
// remove profile from list
const index = profileIds.indexOf(id);
if (index > -1) profileIds.splice(index, 1);
await db.set("profileIds", profileIds);
if (await isActive()) await db.remove("activeProfile");
setState({ rerender: true });
},
$delete = html`<button
class="h-[14px] transition duration-[20ms]
text-[color:var(--theme--fg-secondary)]
hover:text-[color:var(--theme--fg-primary)]"
>
<i class="i-x size-[14px]"></i>
</button>`,
$confirmName = $successName.cloneNode(true),
$confirm = html`<${Popup}
trigger=${$delete}
onopen=${async () => ($confirmName.innerText = await getName())}
>
<p class="text-[14px] py-[2px] px-[8px]">
Are you sure you want to delete the profile ${$confirmName} permanently?
</p>
<div class="flex-(~ col) gap-[8px] py-[6px] px-[8px]">
<${Button}
tabindex="0"
icon="trash"
class="justify-center"
variant="secondary"
onclick=${deleteProfile}
>
Delete
<//>
<${Button}
tabindex="0"
class="justify-center"
onclick=${() => $confirm.close()}
>
Cancel
<//>
</div>
<//>`;
return html`<li class="flex items-center my-[14px] gap-[8px]" id=${id}>
<${Checkbox}
...${{ _get: isActive, _set: setActive, _requireReload: false }}
onchange=${(event) => (event.target.checked = true)}
/>
<${Input}
icon="file-cog"
...${{ _get: getName, _set: setName, _requireReload: false }}
/>
<${Button}
icon="import"
variant="sm"
tagName="label"
class="relative"
onkeydown=${(event) => {
if ([" ", "Enter"].includes(event.key)) {
event.preventDefault();
$uploadInput.click();
}
}}
>${$uploadInput} Import ${$uploadSuccess}${$uploadError}
<//>
<${Button} variant="sm" icon="upload" onclick=${downloadProfile}>Export<//>
<div class="relative flex">${$delete}${$confirm}</div>
</li>`;
}
function Profiles() {
const { html, setState, useState, initDatabase } = globalThis.__enhancerApi,
$input = html`<${Input} icon="file-cog" />`,
$list = html`<ul></ul>`;
const db = initDatabase(),
refreshProfiles = async () => {
let profileIds = await db.get("profileIds");
if (!profileIds?.length) profileIds = ["default"];
const $profiles = profileIds.map((id) => {
return document.getElementById(id) || html`<${Profile} id=${id} />`;
});
// replace rows one-by-one to avoid layout shift
for (let i = 0; i < $profiles.length || i < $list.children.length; i++) {
if ($profiles[i] === $list.children[i]) continue;
if ($list.children[i]) {
if ($profiles[i]) {
$list.children[i].replaceWith($profiles[i]);
} else $list.children[i].remove();
} else $list.append($profiles[i]);
}
},
addProfile = async () => {
if (!$input.children[0].value) return;
const name = $input.children[0].value,
id = crypto.randomUUID();
let profileIds = await db.get("profileIds");
if (!profileIds?.length) profileIds = ["default"];
await db.set("profileIds", [...profileIds, id]);
await initDatabase([id]).set("profileName", name);
$input.children[0].value = "";
setState({ rerender: true });
};
useState(["rerender"], () => refreshProfiles());
$input.onkeydown = (event) => {
if (event.key === "Enter") addProfile();
};
return html`
<${Heading}>Profiles<//>
<${Description}>
Profiles can be used to preserve and switch between notion-enhancer
configurations.
<//>
<div>
${$list}
<div class="flex items-center my-[14px] gap-[8px]">
${$input}
<${Button} variant="sm" icon="plus" onclick=${addProfile}>
Add Profile
<//>
</div>
</div>
`;
}
export { Profiles };

View File

@ -0,0 +1,146 @@
/**
* notion-enhancer
* (c) 2023 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
* (https://notion-enhancer.github.io/) under the MIT license
*/
import { Popup } from "./Popup.mjs";
function Option({ $icon = "", value = "", _get, _set }) {
const { html, useState } = globalThis.__enhancerApi;
return html`<div
tabindex="0"
role="option"
class="select-none cursor-pointer rounded-[3px]
flex items-center w-full h-[28px] px-[12px] leading-[1.2]
transition duration-[20ms] focus:bg-[color:var(--theme--bg-hover)]"
onmouseover=${(event) => event.target.focus()}
onclick=${() => _set?.(value)}
onkeydown=${(event) => {
if (["Enter", " "].includes(event.key)) _set?.(value);
}}
>
<div
class="mr-[6px] inline-flex items-center gap-[6px]
text-[14px] text-ellipsis overflow-hidden"
>
${$icon}<span>${value}</span>
</div>
</div>`;
}
function Select({
_get,
_set,
_requireReload = true,
values = [],
popupMode = "left",
maxWidth = 256,
minWidth = 48,
...props
}) {
const { html, extendProps, setState, useState } = globalThis.__enhancerApi,
$selected = html`<i class="ml-auto i-check size-[16px]"></i>`,
// dir="rtl" overflows to the left during transition
$select = html`<div
dir="rtl"
role="button"
tabindex="0"
class="appearance-none bg-transparent rounded-[4px]
h-[28px] max-w-[${maxWidth}px] min-w-[${minWidth}px]
cursor-pointer text-[14px] overflow-hidden pr-[28px]
transition duration-[20ms] leading-[28px] pl-[8px]
hover:bg-[color:var(--theme--bg-hover)]"
></div>`,
$popup = html`<div></div>`,
onKeydown = (event) => {
const intercept = () => {
event.preventDefault();
event.stopPropagation();
};
if (event.key === "Escape") {
intercept(setState({ rerender: true }));
} else if (!options.length) return;
// prettier-ignore
const $next = options.find(({ $option }) => $option === event.target)
?.$option.nextElementSibling ?? options.at(0).$option,
$prev = options.find(({ $option }) => $option === event.target)
?.$option.previousElementSibling ?? options.at(-1).$option;
// overflow to opposite end of list from dir of travel
if (event.key === "ArrowUp") intercept($prev.focus());
if (event.key === "ArrowDown") intercept($next.focus());
// re-enable natural tab behaviour in notion interface
if (event.key === "Tab") event.stopPropagation();
};
let options = [];
const valueToOption = (opt) => {
if (["string", "number"].includes(typeof opt)) opt = { value: opt };
if (!(opt?.$icon instanceof Element)) {
if (typeof opt?.$icon === "string") {
opt.$icon = html`<i class="i-${opt.$icon} size-[16px]" />`;
} else delete opt.$icon;
}
const $icon = opt.$icon?.cloneNode(true);
return {
...opt,
$option: html`<${Option} ...${{ ...opt, _get, _set }} />`,
$value: html`<div class="inline-flex text-nowrap items-center gap-[6px]">
<!-- swap icon/value order for correct display when dir="rtl" -->
<span>${opt.label || opt.value}</span>${$icon ?? ""}
</div>`,
};
};
$select.setValues = (values) => {
options = values.map(valueToOption);
$popup.innerHTML = "";
$popup.append(...options.map(({ $option }) => $option));
};
$select.setValues(values);
let _initialValue;
const getSelected = async () => {
const value = (await _get?.()) ?? $select.innerText,
option = options.find((opt) => opt.value === value);
if (!option) _set?.(options[0].value);
return option || options[0];
};
useState(["rerender"], async () => {
if (!options.length) return;
const { value, $value, $option } = await getSelected();
$select.innerHTML = "";
$select.append($value);
$option.append($selected);
if (_requireReload) {
_initialValue ??= value;
if (value !== _initialValue) setState({ databaseUpdated: true });
}
});
extendProps(props, { class: "notion-enhancer--menu-select relative" });
return html`<div ...${props} setValues=${$select.setValues}>
${$select}<${Popup}
tabindex="0"
trigger=${$select}
mode=${popupMode}
onopen=${() => document.addEventListener("keydown", onKeydown, true)}
onbeforeclose=${() => {
document.removeEventListener("keydown", onKeydown, true);
$select.style.width = `${$select.offsetWidth}px`;
$select.style.background = "transparent";
}}
onclose=${() => {
$select.style.width = "";
$select.style.background = "";
}}
>${$popup}
<//>
<i
class="i-chevron-down pointer-events-none
absolute right-[6px] top-[6px] size-[16px]
text-[color:var(--theme--fg-secondary)]"
></i>
</div>`;
}
export { Select };

View File

@ -0,0 +1,110 @@
/**
* notion-enhancer
* (c) 2023 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
* (https://notion-enhancer.github.io/) under the MIT license
*/
import { Description } from "./Description.mjs";
function SidebarHeading({}, ...children) {
const { html } = globalThis.__enhancerApi;
return html`<h2
class="flex items-center font-semibold leading-none
text-([12px] [color:var(--theme--fg-secondary)])
h-[24px] px-[12px] mb-px mt-[18px] first:mt-[10px]"
>
${children}
</h2>`;
}
function SidebarButton({ id, icon, ...props }, ...children) {
const { html, extendProps, setState, useState } = globalThis.__enhancerApi,
$btn = html`<${props["href"] ? "a" : "button"}
class="flex items-center select-none cursor-pointer text-[14px]
transition hover:bg-[color:var(--theme--bg-hover)] disabled:hidden
min-h-[27px] w-full my-px last:mb-[12px] px-[12px] rounded-[4px]"
...${props}
>
${icon
? html`<i
class="i-${icon} ${icon.startsWith("notion-enhancer")
? "size-[17px] ml-[1.5px] mr-[9.5px]"
: "size-[18px] ml-px mr-[9px]"}"
></i>`
: ""}
<span class="leading-[20px]">${children}</span>
<//>`;
if (!props["href"]) {
extendProps($btn, {
onclick: () => setState({ transition: "fade", view: id }),
});
useState(["view"], ([view = "welcome"]) => {
const active = view.toLowerCase() === id.toLowerCase();
$btn.style.background = active ? "var(--theme--bg-hover)" : "";
$btn.style.fontWeight = active ? "600" : "";
});
}
return $btn;
}
function Sidebar({ items, categories }) {
const { html, useState } = globalThis.__enhancerApi,
{ version, initDatabase, isEnabled } = globalThis.__enhancerApi,
$agreeToUnlock = html`<span
class="pt-[2px] pb-[5px] px-[15px] text-[12px]
inline-block text-[color:var(--theme--fg-red)]"
>To unlock the notion-enhancer's full functionality, agree to the privacy
policy and terms & conditions on the welcome page.
</span>`,
$sidebar = html`<aside
class="notion-enhancer--menu-sidebar h-full
px-[4px] overflow-y-auto flex-(~ col) row-span-1
bg-[color:var(--theme--bg-secondary)]"
>
${items.map((item) => {
if (Array.isArray(item)) {
const [title, desc] = Array.isArray(item) ? item : [item];
return html`
<${SidebarHeading}>${title}<//>
<${Description}>${desc}<//>
`;
} else if (typeof item === "object") {
const { title, ...props } = item;
return html`<${SidebarButton} ...${props}>${title}<//>`;
} else return html`<${SidebarHeading}>${item}<//>`;
})}${$agreeToUnlock}
</aside>`;
useState(["rerender"], async () => {
const agreedToTerms = await initDatabase().get("agreedToTerms");
$agreeToUnlock.style.display = agreedToTerms === version ? "none" : "";
[...$sidebar.children].forEach(($btn) => {
if (!$btn.disableUntilAgreedToTerms) return;
$btn.disabled = agreedToTerms !== version;
});
});
for (const { title, mods } of categories) {
const $title = html`<${SidebarHeading}>${title}<//>`,
$mods = mods
.filter((mod) => mod.options?.length)
.map((mod) => [
mod.id,
html`<${SidebarButton} id=${mod.id}>${mod.name}<//>`,
]);
$sidebar.append($title, ...$mods.map(([, $btn]) => $btn));
useState(["rerender"], async () => {
let sectionVisible = false;
for (const [id, $btn] of $mods) {
$btn.disabled = !(await isEnabled(id));
sectionVisible ||= !$btn.disabled;
}
$title.style.display = sectionVisible ? "" : "none";
});
}
return $sidebar;
}
export { Sidebar };

View File

@ -0,0 +1,53 @@
/**
* notion-enhancer
* (c) 2023 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
* (https://notion-enhancer.github.io/) under the MIT license
*/
// telemetry endpoint not ready, disabled for current release
import { collectTelemetryData } from "../../sendTelemetry.mjs";
import { Option } from "./Options.mjs";
const privacyPolicy = "https://notion-enhancer.github.io/about/privacy-policy/";
function Telemetry() {
const { html, setState, useState, initDatabase } = globalThis.__enhancerApi,
_get = async () => {
// defaults to true, must be explicitly set to false to disable
return (await initDatabase().get("telemetryEnabled")) ?? true;
},
_set = async (value) => {
await initDatabase().set("telemetryEnabled", value);
setState({ rerender: true });
};
const $ = {
platform: html`<code></code>`,
version: html`<code></code>`,
timezone: html`<code></code>`,
enabled_mods: html`<code></code>`,
};
useState(["rerender"], async () => {
const telemetryData = await collectTelemetryData();
for (const key in telemetryData) {
$[key].innerText = JSON.stringify(telemetryData[key]);
}
});
return html`<${Option}
type="toggle"
label="Telemetry"
description=${html`If telemetry is enabled, usage data will be collected at
a regular interval from your device in order to better understand how and
where the notion-enhancer is used. This data is anonymous and includes
only your platform (${$.platform}), notion-enhancer version
(${$.version}), timezone (${$.timezone}), and enabled mods
(${$.enabled_mods}). You can opt in or out of telemetry at any time. This
setting syncs across configuration profiles. For more information, read
the notion-enhancer's
<a href=${privacyPolicy} class="ml-[3px]">privacy policy</a>.`}
...${{ _get, _set }}
/>`;
}
export { Telemetry };

View File

@ -0,0 +1,27 @@
/**
* notion-enhancer
* (c) 2023 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
* (https://notion-enhancer.github.io/) under the MIT license
*/
function Tile({ icon, title, tagName, ...props }, ...children) {
const { html, extendProps } = globalThis.__enhancerApi;
extendProps(props, {
class: `flex items-center gap-[12px] rounded-[4px]
border-(~ [color:var(--theme--fg-border)]) px-[16px]
bg-[color:var(--theme--bg-secondary)] py-[12px]
hover:bg-[color:var(--theme--bg-hover)]`,
});
tagName ??= props["href"] ? "a" : "button";
return html`<${tagName} ...${props}>
<i class="i-${icon} text-[28px]"></i>
<div>
<h3 class="text-[14px] font-semibold">${title}</h3>
<div class="text-([12px] [color:var(--theme--fg-secondary)])">
${children}
</div>
</div>
<//>`;
}
export { Tile };

View File

@ -0,0 +1,53 @@
/**
* notion-enhancer
* (c) 2023 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
* (https://notion-enhancer.github.io/) under the MIT license
*/
function Toggle({ _get, _set, _requireReload = true, ...props }) {
let _initialValue;
const { html, extendProps, setState, useState } = globalThis.__enhancerApi,
$input = html`<input
type="checkbox"
class="hidden [&:checked+div>div]:(
bg-[color:var(--theme--accent-primary)]
after:translate-x-[12px])"
...${props}
/>`;
extendProps($input, { onchange: () => _set?.($input.checked) });
useState(["rerender"], async () => {
const checked = (await _get?.()) ?? $input.checked;
$input.checked = checked;
if (_requireReload) {
_initialValue ??= checked;
if (checked !== _initialValue) setState({ databaseUpdated: true });
}
});
return html`<div class="notion-enhancer--menu-toggle shrink-0">
${$input}
<div
tabindex="0"
class="w-[30px] h-[18px] rounded-[44px] cursor-pointer
transition duration-200 bg-[color:var(--theme--bg-hover)]"
onkeydown=${(event) => {
if ([" ", "Enter"].includes(event.key)) {
event.preventDefault();
$input.click();
}
}}
>
<div
class="w-full h-full rounded-[44px] text-[12px]
p-[2px] hover:bg-[color:var(--theme--bg-hover)]
transition duration-200 after:(
inline-block size-[14px] rounded-[44px]
bg-[color:var(--theme--accent-primary\\_contrast)]
transition duration-200 content-empty
)"
></div>
</div>
</div>`;
}
export { Toggle };

View File

@ -0,0 +1,92 @@
/**
* notion-enhancer
* (c) 2023 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
* (https://notion-enhancer.github.io/) under the MIT license
*/
function View({ id }, ...children) {
const { html, setState, useState } = globalThis.__enhancerApi,
// set padding on last child to maintain pad on overflow
$view = html`<article
id=${id}
class="notion-enhancer--menu-view min-h-full w-full
absolute px-[60px] py-[36px] min-w-[580px]"
>
${children}
</article>`;
useState(["view"], ([view = "welcome"]) => {
const [transition] = useState(["transition"]),
isVisible = $view.style.display !== "none",
nowActive = view.toLowerCase() === id.toLowerCase();
switch (transition) {
case "fade": {
const duration = 100,
cssTransition = `opacity ${duration}ms`;
if (isVisible && !nowActive) {
$view.parentElement.style.overflow = "hidden";
requestAnimationFrame(() => {
$view.style.transition = cssTransition;
$view.style.opacity = "0";
setTimeout(() => ($view.style.display = "none"), duration);
});
} else if (!isVisible && nowActive) {
setTimeout(() => {
$view.style.opacity = "0";
$view.style.display = "";
requestAnimationFrame(() => {
$view.style.transition = cssTransition;
$view.style.opacity = "1";
$view.parentElement.style.overflow = "";
});
}, duration);
}
break;
}
case "slide-to-left":
case "slide-to-right": {
const duration = 200,
cssTransition = `opacity ${duration}ms, transform ${duration}ms`;
if (isVisible && !nowActive) {
$view.parentElement.style.overflow = "hidden";
requestAnimationFrame(() => {
$view.style.transition = cssTransition;
$view.style.transform = `translateX(${
transition === "slide-to-right" ? "-100%" : "100%"
})`;
$view.style.opacity = "0";
setTimeout(() => {
$view.style.display = "none";
$view.style.transform = "";
}, duration);
});
} else if (!isVisible && nowActive) {
$view.style.transform = `translateX(${
transition === "slide-to-right" ? "100%" : "-100%"
})`;
$view.style.opacity = "0";
$view.style.display = "";
requestAnimationFrame(() => {
$view.style.transition = cssTransition;
$view.style.transform = "";
$view.style.opacity = "1";
setTimeout(() => {
$view.parentElement.style.overflow = "";
}, duration);
});
}
break;
}
default:
$view.style.transition = "";
$view.style.opacity = nowActive ? "1" : "0";
$view.style.display = nowActive ? "" : "none";
}
});
return $view;
}
export { View };

140
src/core/menu/menu.css Normal file
View File

@ -0,0 +1,140 @@
/**
* notion-enhancer
* (c) 2023 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
* (https://notion-enhancer.github.io/) under the MIT license
*/
@keyframes skeleton-shimmer {
0% {
transform: translateX(-100%);
}
100% {
transform: translateX(100%);
}
}
::selection {
background: var(--theme--accent-primary_transparent);
}
*:focus-visible {
outline: 3px solid var(--theme--accent-primary);
}
*:focus-visible[role="option"] {
outline: none;
}
::-webkit-scrollbar {
width: 10px;
height: 10px;
background: transparent;
}
::-webkit-scrollbar-track,
::-webkit-scrollbar-corner {
background: var(--theme--scrollbar-track);
}
::-webkit-scrollbar-thumb {
background: var(--theme--scrollbar-thumb);
}
::-webkit-scrollbar-thumb:hover {
background: var(--theme--scrollbar-thumb_hover);
}
body {
display: grid;
grid-template-rows: 1fr;
grid-template-columns: 240px auto;
width: 100vw;
height: 100vh;
margin: 0;
color: var(--theme--fg-primary);
font-family: var(--font--sans);
overflow: hidden;
}
body > #skeleton {
background: rgba(86, 86, 86, 0.1);
}
body > #skeleton .row {
display: flex;
align-items: center;
padding: 0 15px;
margin: 2px 0;
height: 27px;
}
body > #skeleton .shimmer {
height: 14px;
overflow: hidden;
position: relative;
border-radius: 4px;
background: rgba(86, 86, 86, 0.1);
}
body > #skeleton .shimmer.icon {
margin-left: 1px;
margin-right: 9px;
height: 18px;
width: 18px;
}
body > #skeleton .shimmer::before {
content: "";
position: absolute;
height: 100%;
width: 100%;
z-index: 1;
animation: 1s linear infinite skeleton-shimmer;
background: linear-gradient(
90deg,
transparent 0,
rgba(86, 86, 86, 0.1) 50%,
transparent 100%
);
}
body > #skeleton .row-group {
height: 24px;
margin-top: 18px;
}
body > #skeleton .row-group:first-child {
margin-top: 10px;
}
body > #skeleton .row-group .shimmer {
height: 11px;
}
.typography mark {
padding: 0 4px;
border-radius: 3px;
background-color: var(--theme--bg-hover);
color: inherit;
}
.typography code {
padding: 0 4px;
border-radius: 3px;
background-color: var(--theme--code-inline_bg);
color: var(--theme--code-inline_fg);
}
.typography kbd {
padding: 2px 4px;
border-radius: 6px;
border: solid 1px var(--theme--fg-border);
box-shadow: inset 0 -1px 0 var(--theme--fg-border);
}
.typography a {
text-decoration: underline;
transition: 100ms ease-in;
}
.typography a:hover {
color: var(--theme--accent-secondary);
}
/* https://coloris.js.org/ */
.clr-picker {
background-color: var(--theme--bg-secondary) !important;
}
.clr-color {
background-color: var(--theme--bg-hover) !important;
border-color: var(--theme--fg-border) !important;
color: var(--theme--fg-primary) !important;
}
.clr-preview:after,
.clr-preview:before {
border-color: var(--theme--fg-border) !important;
}

277
src/core/menu/menu.mjs Normal file
View File

@ -0,0 +1,277 @@
/**
* notion-enhancer
* (c) 2023 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
* (https://notion-enhancer.github.io/) under the MIT license
*/
import { checkForUpdate, isDevelopmentBuild } from "../updateCheck.mjs";
import { Sidebar } from "./islands/Sidebar.mjs";
import { Footer } from "./islands/Footer.mjs";
import { Banner } from "./islands/Banner.mjs";
import { Onboarding } from "./islands/Onboarding.mjs";
import { View } from "./islands/View.mjs";
import { List } from "./islands/List.mjs";
import { Mod } from "./islands/Mod.mjs";
import { Options } from "./islands/Options.mjs";
import { Profiles } from "./islands/Profiles.mjs";
import { Description } from "./islands/Description.mjs";
let _apiImport, //
_renderStarted,
_stateHookedInto,
_hotkeyRegistered;
const categories = [
{
icon: "palette",
id: "themes",
title: "Themes",
description: `Themes override Notion's colour schemes. Dark themes require
Notion to be in dark mode and light themes require Notion to be in light
mode. To switch between dark mode and light mode, go to <mark>Settings &
members My notifications & settings My settings Appearance</mark>.`,
},
{
icon: "zap",
id: "extensions",
title: "Extensions",
description: `Extensions add to the functionality and layout of the Notion
client, interacting with and modifying existing interfaces.`,
},
// {
// icon: "plug",
// id: "integrations",
// title: "Integrations",
// description: `<span class="text-[color:var(--theme--fg-red)]">
// Integrations access and modify Notion content. They interact directly with
// <mark>https://www.notion.so/api/v3</mark>. Use at your own risk.</span>`,
// },
],
sidebar = [
"notion-enhancer",
{
id: "welcome",
title: "Welcome",
icon: "notion-enhancer",
},
{
icon: "message-circle",
title: "Community",
href: "https://discord.gg/sFWPXtA",
},
{
icon: "clock",
title: "Changelog",
href: "https://notion-enhancer.github.io/about/changelog/",
},
{
icon: "book",
title: "Documentation",
href: "https://notion-enhancer.github.io/",
},
{
icon: "github",
title: "Source Code",
href: "https://github.com/notion-enhancer",
},
"Settings",
{
id: "core",
title: "Core",
icon: "sliders-horizontal",
disableUntilAgreedToTerms: true,
},
...categories.map((c) => ({
id: c.id,
title: c.title,
icon: c.icon,
disableUntilAgreedToTerms: true,
})),
];
const renderMenu = async () => {
const { html, setState, useState } = globalThis.__enhancerApi,
{ getMods, isEnabled, setEnabled } = globalThis.__enhancerApi,
[theme, icon] = useState(["theme", "icon"]);
if (!theme || !icon || _renderStarted) return;
if (icon === "Monochrome") sidebar[1].icon += "?mask";
_renderStarted = true;
const mods = await getMods();
for (let i = 0; i < categories.length; i++) {
const { id } = categories[i];
categories[i].mods = mods.filter(({ _src }) => _src.startsWith(`${id}/`));
categories[i].view = html`<${View} id=${id}>
<${List} ...${categories[i]} />
<//>`;
}
for (let i = 0; i < mods.length; i++) {
const options = mods[i].options?.filter((opt) => opt.type !== "heading");
if (mods[i]._src === "core" || !options?.length) continue;
const _get = () => isEnabled(mods[i].id),
_set = async (enabled) => {
await setEnabled(mods[i].id, enabled);
setState({ rerender: true });
};
mods[i].view = html`<${View} id=${mods[i].id}>
<!-- passing an empty options array hides the settings button -->
<${Mod} ...${{ ...mods[i], options: [], _get, _set }} />
<${Options} mod=${mods[i]} />
<//>`;
}
const $sidebar = html`<${Sidebar}
items=${sidebar}
categories=${categories}
/>`,
$main = html`
<main
class="flex-(~ col) overflow-hidden transition-[height]"
style="height: calc(100% + 65px)"
>
<!-- wrappers necessary for transitions and breakpoints -->
<div class="grow overflow-auto">
<div class="relative h-full w-full">
<${View} id="welcome">
<${Banner}
updateAvailable=${await checkForUpdate()}
isDevelopmentBuild=${await isDevelopmentBuild()}
/>
<${Onboarding} />
<div
class="p-6 rounded-[4px] mt-[16px] text-[14px]
border border-[color:var(--theme--fg-red)]
bg-[color:var(--theme--dim-red)] typography"
>
Hi there! Before you go any further, <b>please note that this update is
not feature complete.</b> As part of an internal overhaul and the Chrome
extension's upgrade to manifest v3, all themes and extensions must be
ported manually across to the new version.
<br />
<br />
The following extensions have not been updated yet but will be
soon:
<ul class="list-disc pl-6">
<li>indentation lines</li>
<li>view scale</li>
<li>emoji sets</li>
<li>simpler databases</li>
<li>icon sets</li>
<li>quick note</li>
</ul>
<br />
The theming system is incomplete and only mostly recolours the
app's interface. The following themes have not been updated
yet but will be soon:
<ul class="list-disc pl-6">
<li>dark+</li>
<li>light+</li>
<li>nord</li>
<li>dracula</li>
<li>neutral</li>
<li>cherry cola</li>
<li>gruvbox dark</li>
<li>gruvbox light</li>
<li>pastel dark</li>
<li>pinky boom</li>
<li>playful purple</li>
</ul>
<br />
In the meantime, the styling for these themes can be
found <a href="https://github.com/notion-enhancer/repo"
>here</a> and
copy/pasted into your custom styles alongside the <a
href="https://github.com/notion-enhancer/repo/blob/dev/theming/theme.css"
>old theming system</a>, if you wish.
<br />
<br />
The following extensions have been deprecated as their feature
offerings are now available within Notion by default. Some
features that belonged to these extensions have been merged
into the notion-enhancer's core or into the tweaks extension:
<ul class="list-disc pl-6">
<li>integrated titlebar</li>
<li>collapsible properties</li>
<li>collapsible headers</li>
<li>tray</li>
<li>tabs</li>
<li>weekly view</li>
<li>truncated titles</li>
<li>global block links</li>
</ul>
<br />
A full changelog and updated documentation will be made
available on the website as soon as possible. This release is
being made available early in order to comply with Chrome's
deprecation of manifest v2.
</div>
<//>
<${View} id="core">
<${Options} mod=${mods.find(({ _src }) => _src === "core")} />
<${Profiles} />
<//>
${[...categories, ...mods]
.filter(({ view }) => view)
.map(({ view }) => view)}
</div>
</div>
<${Footer} categories=${categories} />
</main>
`;
useState(["footerOpen"], ([footerOpen]) => {
$main.style.height = footerOpen ? "100%" : "calc(100% + 65px)";
});
const $skeleton = document.querySelector("#skeleton");
$skeleton.replaceWith($sidebar, $main);
},
registerHotkey = ([hotkey]) => {
const { addKeyListener, setState, useState } = globalThis.__enhancerApi;
if (!hotkey || _hotkeyRegistered) return;
_hotkeyRegistered = true;
addKeyListener(hotkey, (event) => {
event.preventDefault();
const msg = { channel: "notion-enhancer", action: "open-menu" };
parent?.postMessage(msg, "*");
});
addKeyListener("Escape", () => {
const [popupOpen] = useState(["popupOpen"]);
if (document.activeElement?.tagName === "INPUT") {
document.activeElement.blur();
} else if (!popupOpen) {
const msg = { channel: "notion-enhancer", action: "close-menu" };
parent?.postMessage(msg, "*");
} else setState({ rerender: true });
});
},
updateTheme = ([theme]) => {
if (theme === "dark") document.body.classList.add("dark");
if (theme === "light") document.body.classList.remove("dark");
};
const importApi = () => {
return (_apiImport ??= (async () => {
const api = globalThis.__enhancerApi;
if (typeof api === "undefined") await import("../../api/system.js");
await import("../../load.mjs").then((i) => i.default);
})());
},
hookIntoState = () => {
if (_stateHookedInto) return;
_stateHookedInto = true;
const { useState } = globalThis.__enhancerApi;
useState(["theme"], updateTheme);
useState(["hotkey"], registerHotkey);
useState(["rerender"], renderMenu);
};
addEventListener("message", async (event) => {
if (event.data?.channel !== "notion-enhancer") return;
await importApi().then(hookIntoState);
const { setState, useState } = globalThis.__enhancerApi;
setState({
rerender: true,
hotkey: event.data?.hotkey ?? useState(["hotkey"])[0],
theme: event.data?.theme ?? useState(["theme"])[0],
icon: event.data?.icon ?? useState(["icon"])[0],
});
});

88
src/core/mod.json Normal file
View File

@ -0,0 +1,88 @@
{
"name": "notion-enhancer",
"version": "0.11.1",
"id": "0f0bf8b6-eae6-4273-b307-8fc43f2ee082",
"description": "Customise the all-in-one productivity workspace Notion.",
"tags": ["core"],
"authors": [
{
"name": "dragonwocky",
"homepage": "https://dragonwocky.me/",
"avatar": "https://dragonwocky.me/avatar.jpg"
}
],
"options": [
{ "type": "heading", "label": "Hotkeys" },
{
"type": "hotkey",
"key": "openMenuHotkey",
"description": "Opens the notion-enhancer menu from within Notion.",
"value": "Ctrl+Shift+,"
},
{
"type": "hotkey",
"key": "togglePanelHotkey",
"description": "Toggles the side panel used by some notion-enhancer extensions to display additional information and interfaces within the Notion app.",
"value": "Ctrl+Shift+\\"
},
{
"type": "hotkey",
"key": "toggleWindowHotkey",
"description": "Toggles focus of the Notion window anywhere, even when your Notion app isn't active.",
"value": "Ctrl+Shift+A"
},
{ "type": "heading", "label": "Appearance" },
{
"type": "file",
"key": "customStyles",
"description": "Adds the styles from an uploaded .css file to Notion. Use this if you would like to customise the current theme or <a href=\"https://notion-enhancer.github.io/advanced/tweaks\">otherwise tweak Notion's appearance</a>.",
"extensions": ["css"]
},
{
"type": "select",
"key": "loadThemeOverrides",
"description": "Loads the styling required for a theme to customise Notion's interface. Disabling this may increase client performance, but will also disable all themes.",
"values": ["Auto", "Enabled", "Disabled"]
},
{
"type": "text",
"key": "menuButtonLabel",
"description": "Sets the text to label the notion-enhancer button added to Notion's sidebar with.",
"value": "notion-enhancer"
},
{
"type": "select",
"key": "menuButtonIconStyle",
"description": "Sets whether the icon beside the notion-enhancer button added to Notion's sidebar should be coloured or monochrome. The latter style will match the theme's icon colour for users who would like the icon to be less noticeable.",
"values": ["Colour", "Monochrome"]
},
{
"type": "toggle",
"key": "peekPanelOnHover",
"description": "Pops the side panel out to preview its content when hovering near the right edge of the window, in the same way that Notion's left-hand sidebar will slide out on hover. Disable this if you prefer to view the panel only by pinning it.",
"value": true
},
{
"type": "heading",
"label": "Advanced",
"_autoremoveIfSectionEmpty": false
},
{
"type": "file",
"label": "Custom JavaScript",
"key": "customScript",
"description": "Executes the uploaded userscript within Notion. Requires <a href='https://developer.chrome.com/docs/extensions/reference/api/userScripts#developer_mode_for_extension_users'>developer mode</a> to be enabled in your browser's extension settings to run in Chromium-based browsers.",
"extensions": ["js"]
},
{
"type": "toggle",
"key": "developerMode",
"description": "Activates built-in debugging tools accessible through the application menu.",
"platforms": ["linux", "win32", "darwin"],
"value": false
}
],
"clientStyles": ["variables.css", "../vendor/@unocss-preflight-tailwind.css"],
"clientScripts": ["client.mjs"],
"electronScripts": [[".webpack/main/index.js", "electron.cjs"]]
}

View File

@ -0,0 +1,47 @@
/**
* notion-enhancer
* (c) 2023 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
* (https://notion-enhancer.github.io/) under the MIT license
*/
// telemetry endpoint not ready, disabled for current release
const pingEndpoint = "https://notion-enhancer.deno.dev/api/ping",
collectTelemetryData = async () => {
const { platform, version } = globalThis.__enhancerApi,
{ getMods, isEnabled } = globalThis.__enhancerApi,
timezone = Intl.DateTimeFormat().resolvedOptions().timeZone,
// prettier-ignore
enabled_mods = (await getMods(async (mod) => {
if (mod._src === "core") return false;
return await isEnabled(mod.id);
})).map(mod => mod.id);
return { platform, version, timezone, enabled_mods };
},
sendTelemetryPing = async () => {
// const db = __enhancerApi.initDatabase(),
// { version } = globalThis.__enhancerApi,
// agreedToTerms = await db.get("agreedToTerms"),
// telemetryEnabled = (await db.get("telemetryEnabled")) ?? true;
// if (!telemetryEnabled || agreedToTerms !== version) return;
// const lastTelemetryPing = await db.get("lastTelemetryPing");
// if (lastTelemetryPing) {
// const msSincePing = Date.now() - new Date(lastTelemetryPing);
// // send ping only once a week
// if (msSincePing / 8.64e7 < 7) return;
// }
// try {
// const telemetryData = await collectTelemetryData(),
// pingTimestamp = await fetch(pingEndpoint, {
// method: "POST",
// body: JSON.stringify(telemetryData),
// }).then((res) => res.text());
// await db.set("lastTelemetryPing", pingTimestamp);
// } catch (err) {
// console.error(err);
// }
};
export { collectTelemetryData, sendTelemetryPing };

1
src/core/theme.css Normal file

File diff suppressed because one or more lines are too long

49
src/core/updateCheck.mjs Normal file
View File

@ -0,0 +1,49 @@
/**
* notion-enhancer
* (c) 2023 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
* (https://notion-enhancer.github.io/) under the MIT license
*/
let _release;
const repo = "notion-enhancer/notion-enhancer",
endpoint = `https://api.github.com/repos/${repo}/releases/latest`,
getRelease = async () => {
const { version, readJson } = globalThis.__enhancerApi;
try {
_release ??= (await readJson(endpoint))?.tag_name.replace(/^v/, "");
} catch {}
_release ??= version;
return _release;
};
const parseVersion = (semver) => {
while (semver.split("-")[0].split(".").length < 3) semver = `0.${semver}`;
let [major, minor, patch, build] = semver.split("."),
prerelease = patch.split("-")[1]?.split(".")[0];
patch = patch.split("-")[0];
return [major, minor, patch, prerelease, build]
.map((v) => v ?? "")
.map((v) => (/^\d+$/.test(v) ? parseInt(v) : v));
},
// is a < b
greaterThan = (a, b) => {
if (a && !b) return true;
if (!a && b) return false;
a = parseVersion(a);
b = parseVersion(b);
for (let i = 0; i < a.length; i++) {
if (a[i] > b[i]) return true;
else if (a[i] < b[i]) return false;
}
};
const checkForUpdate = async () => {
const { version } = globalThis.__enhancerApi;
return greaterThan(await getRelease(), version) ? _release : false;
},
isDevelopmentBuild = async () => {
const { version } = globalThis.__enhancerApi;
return !(await checkForUpdate()) && version !== _release;
};
export { checkForUpdate, isDevelopmentBuild, greaterThan };

181
src/core/variables.css Normal file
View File

@ -0,0 +1,181 @@
/**
* notion-enhancer
* (c) 2023 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
* (https://notion-enhancer.github.io/) under the MIT license
*/
body.dark {
--theme--fg-primary: rgba(255, 255, 255, 0.81);
--theme--fg-secondary: rgb(155, 155, 155);
--theme--fg-border: rgb(47, 47, 47);
--theme--fg-gray: rgba(155, 155, 155, 1);
--theme--fg-brown: rgba(186, 133, 111, 1);
--theme--fg-orange: rgba(199, 125, 72, 1);
--theme--fg-yellow: rgba(202, 152, 73, 1);
--theme--fg-green: rgba(82, 158, 114, 1);
--theme--fg-blue: rgba(94, 135, 201, 1);
--theme--fg-purple: rgba(157, 104, 211, 1);
--theme--fg-pink: rgba(209, 87, 150, 1);
--theme--fg-red: rgba(223, 84, 82, 1);
--theme--bg-primary: rgb(25, 25, 25);
--theme--bg-secondary: rgb(32, 32, 32);
--theme--bg-hover: rgba(255, 255, 255, 0.055);
--theme--bg-overlay: rgba(15, 15, 15, 0.8);
--theme--bg-light_gray: rgb(55, 55, 55);
--theme--bg-gray: rgb(90, 90, 90);
--theme--bg-brown: rgb(96, 59, 44);
--theme--bg-orange: rgb(133, 76, 29);
--theme--bg-yellow: rgb(137, 99, 42);
--theme--bg-green: rgb(43, 89, 63);
--theme--bg-blue: rgb(40, 69, 108);
--theme--bg-purple: rgb(73, 47, 100);
--theme--bg-pink: rgb(105, 49, 76);
--theme--bg-red: rgb(110, 54, 48);
--theme--dim-light_gray: rgb(28, 28, 28);
--theme--dim-gray: rgb(32, 32, 32);
--theme--dim-brown: rgb(35, 30, 28);
--theme--dim-orange: rgb(37, 31, 27);
--theme--dim-yellow: rgb(35, 31, 26);
--theme--dim-green: rgb(29, 34, 32);
--theme--dim-blue: rgb(27, 31, 34);
--theme--dim-purple: rgb(31, 29, 33);
--theme--dim-pink: rgb(35, 28, 31);
--theme--dim-red: rgb(36, 30, 29);
--theme--accent-primary: rgb(35, 131, 226);
--theme--accent-primary_hover: rgb(0, 117, 211);
--theme--accent-primary_contrast: rgb(255, 255, 255);
--theme--accent-primary_transparent: rgba(35, 131, 226, 0.14);
--theme--accent-secondary: rgb(235, 87, 87);
--theme--accent-secondary_hover: rgba(235, 87, 87, 0.1);
--theme--accent-secondary_contrast: white;
--theme--scrollbar-track: rgba(202, 204, 206, 0.04);
--theme--scrollbar-thumb: #474c50;
--theme--scrollbar-thumb_hover: rgba(202, 204, 206, 0.3);
--theme--code-inline_fg: #eb5757;
--theme--code-inline_bg: rgba(135, 131, 120, 0.15);
--theme--code-block_fg: rgba(255, 255, 255, 0.81);
--theme--code-block_bg: rgba(255, 255, 255, 0.03);
--theme--code-keyword: rgb(209, 148, 158);
--theme--code-builtin: rgb(189, 224, 82);
--theme--code-class_name: rgba(255, 255, 255, 0.81);
--theme--code-function: var(--theme--code-class_name);
--theme--code-boolean: var(--theme--code-keyword);
--theme--code-number: var(--theme--code-keyword);
--theme--code-string: var(--theme--code-builtin);
--theme--code-char: var(--theme--code-builtin);
--theme--code-symbol: var(--theme--code-keyword);
--theme--code-regex: rgb(238, 153, 0);
--theme--code-url: rgb(245, 184, 61);
--theme--code-operator: var(--theme--code-url);
--theme--code-variable: var(--theme--code-url);
--theme--code-constant: var(--theme--code-keyword);
--theme--code-property: var(--theme--code-keyword);
--theme--code-punctuation: var(--theme--code-class_name);
--theme--code-important: var(--theme--code-regex);
--theme--code-comment: rgb(153, 128, 102);
--theme--code-tag: var(--theme--code-keyword);
--theme--code-attr_name: var(--theme--code-builtin);
--theme--code-attr_value: var(--theme--code-keyword);
--theme--code-namespace: var(--theme--code-class_name);
--theme--code-prolog: var(--theme--code-comment);
--theme--code-doctype: var(--theme--code-comment);
--theme--code-cdata: var(--theme--code-comment);
--theme--code-entity: var(--theme--code-url);
--theme--code-atrule: var(--theme--code-keyword);
--theme--code-selector: var(--theme--code-builtin);
--theme--code-inserted: var(--theme--code-builtin);
--theme--code-deleted: rgb(255, 0, 0);
}
body:not(.dark) {
--theme--fg-primary: rgb(55, 53, 47);
--theme--fg-secondary: rgba(25, 23, 17, 0.6);
--theme--fg-border: rgb(233, 233, 231);
--theme--fg-gray: rgba(120, 119, 116, 1);
--theme--fg-brown: rgba(159, 107, 83, 1);
--theme--fg-orange: rgba(217, 115, 13, 1);
--theme--fg-yellow: rgba(203, 145, 47, 1);
--theme--fg-green: rgba(68, 131, 97, 1);
--theme--fg-blue: rgba(51, 126, 169, 1);
--theme--fg-purple: rgba(144, 101, 176, 1);
--theme--fg-pink: rgba(193, 76, 138, 1);
--theme--fg-red: rgba(212, 76, 71, 1);
--theme--bg-primary: white;
--theme--bg-secondary: rgb(251, 251, 250);
--theme--bg-hover: rgba(55, 53, 47, 0.08);
--theme--bg-overlay: rgba(15, 15, 15, 0.6);
--theme--bg-light_gray: rgba(227, 226, 224, 0.5);
--theme--bg-gray: rgb(227, 226, 224);
--theme--bg-brown: rgb(238, 224, 218);
--theme--bg-orange: rgb(250, 222, 201);
--theme--bg-yellow: rgb(253, 236, 200);
--theme--bg-green: rgb(219, 237, 219);
--theme--bg-blue: rgb(211, 229, 239);
--theme--bg-purple: rgb(232, 222, 238);
--theme--bg-pink: rgb(245, 224, 233);
--theme--bg-red: rgb(255, 226, 221);
--theme--dim-light_gray: rgba(249, 249, 245, 0.5);
--theme--dim-gray: rgba(247, 247, 245, 0.7);
--theme--dim-brown: rgba(250, 246, 245, 0.7);
--theme--dim-orange: rgba(252, 245, 242, 0.7);
--theme--dim-yellow: rgba(250, 247, 237, 0.7);
--theme--dim-green: rgba(244, 248, 243, 0.7);
--theme--dim-blue: rgba(241, 248, 251, 0.7);
--theme--dim-purple: rgba(249, 246, 252, 0.7);
--theme--dim-pink: rgba(251, 245, 251, 0.7);
--theme--dim-red: rgba(253, 245, 243, 0.7);
--theme--accent-primary: rgb(35, 131, 226);
--theme--accent-primary_hover: rgb(0, 117, 211);
--theme--accent-primary_contrast: rgb(255, 255, 255);
--theme--accent-primary_transparent: rgba(35, 131, 226, 0.14);
--theme--accent-secondary: rgb(235, 87, 87);
--theme--accent-secondary_hover: rgba(235, 87, 87, 0.1);
--theme--accent-secondary_contrast: white;
--theme--scrollbar-track: #edece9;
--theme--scrollbar-thumb: #d3d1cb;
--theme--scrollbar-thumb_hover: #aeaca6;
--theme--code-inline_fg: #eb5757;
--theme--code-inline_bg: rgba(135, 131, 120, 0.15);
--theme--code-block_fg: rgb(55, 53, 47);
--theme--code-block_bg: rgb(247, 246, 243);
--theme--code-keyword: rgb(0, 119, 170);
--theme--code-builtin: rgb(102, 153, 0);
--theme--code-class_name: rgb(221, 74, 104);
--theme--code-function: var(--theme--code-class_name);
--theme--code-boolean: rgb(153, 0, 85);
--theme--code-number: var(--theme--code-boolean);
--theme--code-string: var(--theme--code-builtin);
--theme--code-char: var(--theme--code-builtin);
--theme--code-symbol: var(--theme--code-boolean);
--theme--code-regex: rgb(238, 153, 0);
--theme--code-url: rgb(154, 110, 58);
--theme--code-operator: var(--theme--code-url);
--theme--code-variable: var(--theme--code-regex);
--theme--code-constant: var(--theme--code-boolean);
--theme--code-property: var(--theme--code-boolean);
--theme--code-punctuation: rgb(153, 153, 153);
--theme--code-important: var(--theme--code-regex);
--theme--code-comment: rgb(112, 128, 144);
--theme--code-tag: var(--theme--code-boolean);
--theme--code-attr_name: var(--theme--code-builtin);
--theme--code-attr_value: var(--theme--code-keyword);
--theme--code-namespace: rgb(55, 53, 47);
--theme--code-prolog: var(--theme--code-comment);
--theme--code-doctype: var(--theme--code-comment);
--theme--code-cdata: var(--theme--code-comment);
--theme--code-entity: var(--theme--code-url);
--theme--code-atrule: var(--theme--code-keyword);
--theme--code-selector: var(--theme--code-builtin);
--theme--code-inserted: var(--theme--code-builtin);
--theme--code-deleted: var(--theme--code-boolean);
}

View File

@ -0,0 +1,10 @@
/**
* notion-enhancer: emoji sets
* (c) 2021 Arecsu
* (c) 2021 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
* (https://notion-enhancer.github.io/) under the MIT license
*/
[aria-label][role='image'][style*='Apple Color Emoji']:not([data-emoji-sets-unsupported]) {
margin-left: 2.5px !important;
}

View File

@ -0,0 +1,71 @@
/**
* notion-enhancer: emoji sets
* (c) 2021 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
* (https://notion-enhancer.github.io/) under the MIT license
*/
export default async function ({ web, env }, db) {
const style = await db.get(['style']),
// real emojis are used on macos instead of the twitter set
nativeEmojiSelector = `[aria-label][role="image"][style*="Apple Color Emoji"]:not([data-emoji-sets-unsupported])`,
imgEmojiSelector = '.notion-emoji:not([data-emoji-sets-unsupported])',
imgEmojiOverlaySelector = `${imgEmojiSelector} + [src*="notion-emojis"]`;
await Promise.any([web.whenReady([nativeEmojiSelector]), web.whenReady([imgEmojiSelector])]);
const nativeEmojis = document.querySelectorAll(nativeEmojiSelector).length,
imgEmojis = document.querySelectorAll(imgEmojiSelector).length;
const unsupportedEmojis = [],
emojiReqs = new Map(),
getEmoji = async (emoji) => {
emoji = encodeURIComponent(emoji);
if (unsupportedEmojis.includes(emoji)) return undefined;
try {
if (!emojiReqs.get(emoji)) {
emojiReqs.set(emoji, fetch(`https://emojicdn.elk.sh/${emoji}?style=${style}`));
}
const res = await emojiReqs.get(emoji);
if (!res.ok) throw new Error();
return `url("https://emojicdn.elk.sh/${emoji}?style=${style}") 100% 100% / 100%`;
} catch {
unsupportedEmojis.push(emoji);
return undefined;
}
};
if (nativeEmojis) {
const updateEmojis = async () => {
const $emojis = document.querySelectorAll(nativeEmojiSelector);
for (const $emoji of $emojis) {
const emojiSrc = await getEmoji($emoji.ariaLabel);
if (emojiSrc) {
$emoji.style.background = emojiSrc;
$emoji.style.width = '1em';
$emoji.style.height = '1em';
$emoji.style.display = 'inline-block';
$emoji.innerText = '';
} else $emoji.dataset.emojiSetsUnsupported = true;
}
};
web.addDocumentObserver(updateEmojis, [nativeEmojiSelector]);
}
if (style !== 'twitter' && imgEmojis) {
const updateEmojis = async () => {
const $emojis = document.querySelectorAll(imgEmojiSelector);
for (const $emoji of $emojis) {
const emojiSrc = await getEmoji($emoji.ariaLabel);
if (emojiSrc) {
$emoji.style.background = emojiSrc;
$emoji.style.opacity = 1;
if ($emoji.nextElementSibling?.matches?.(imgEmojiOverlaySelector)) {
$emoji.nextElementSibling.style.opacity = 0;
}
} else $emoji.dataset.emojiSetsUnsupported = true;
}
};
updateEmojis();
web.addDocumentObserver(updateEmojis, [imgEmojiSelector, imgEmojiOverlaySelector]);
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

View File

@ -0,0 +1,46 @@
{
"name": "emoji sets",
"id": "a2401ee1-93ba-4b8c-9781-7f570bf5d71e",
"version": "0.4.0",
"description": "pick from a variety of emoji styles to use.",
"preview": "emoji-sets.jpg",
"tags": ["extension", "customisation"],
"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": [
{
"type": "select",
"key": "style",
"label": "emoji style",
"tooltip": "**initial use may involve some lag and load-time for emojis until they have all been cached**",
"values": [
"twitter",
"apple",
"google",
"microsoft",
"samsung",
"whatsapp",
"facebook",
"messenger",
"joypixels",
"openmoji",
"emojidex",
"lg",
"htc",
"mozilla"
]
}
]
}

View File

@ -0,0 +1,25 @@
/**
* notion-enhancer: focus
* (c) 2020 Arecsu
* (c) 2024 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
* (https://notion-enhancer.github.io/) under the MIT license
*/
/* hide topbar and ai */
.notion-sidebar-container[aria-hidden] ~ div {
:is(.notion-topbar, .notion-help-button, .notion-ai-button) {
opacity: 0 !important;
transition: opacity 200ms ease-in-out !important;
}
.notion-topbar:hover {
opacity: 1 !important;
}
}
/* hide tabs */
body > #root.sidebar-collapsed {
transition: opacity 200ms ease-in-out;
&:not(:hover) {
opacity: 0;
}
}

View File

@ -0,0 +1,23 @@
/**
* notion-enhancer: focus
* (c) 2020 Arecsu
* (c) 2024 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
* (https://notion-enhancer.github.io/) under the MIT license
*/
export default async (api, db) => {
// tabs can only be hidden in the desktop app
const { platform, sendMessage, addMutationListener } = api;
if (!["linux", "win32", "darwin"].includes(platform)) return;
let _state;
const sidebar = ".notion-sidebar-container",
onUpdate = () => {
const $sidebar = document.querySelector(sidebar),
state = $sidebar.hasAttribute("aria-hidden") ? "collapsed" : "pinned";
if (state === _state) return;
sendMessage("notion-enhancer:focus", "sidebar-" + (_state = state));
};
addMutationListener(sidebar, onUpdate, { childList: false, subtree: false });
onUpdate();
};

View File

@ -0,0 +1,17 @@
/**
* notion-enhancer: focus
* (c) 2020 Arecsu
* (c) 2024 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
* (https://notion-enhancer.github.io/) under the MIT license
*/
"use strict";
module.exports = async (api, db) => {
const { ipcMain, BrowserWindow } = require("electron"),
channel = "notion-enhancer:focus";
ipcMain.on(channel, ({ sender }, message) => {
const views = BrowserWindow.fromWebContents(sender).getBrowserViews();
for (const view of views) view.webContents.send(channel, message);
});
};

View File

@ -0,0 +1,25 @@
{
"name": "Focus",
"version": "0.4.0",
"id": "5a08598d-bfac-4167-9ae8-2bd0e2ef141e",
"description": "Enter focus mode when the left sidebar is closed, hiding Notion's extraneous interface elements (e.g. the topbar) until they are hovered over.",
"tags": ["productivity", "focus-mode"],
"authors": [
{
"name": "dragonwocky",
"homepage": "https://dragonwocky.me/",
"avatar": "https://dragonwocky.me/avatar.jpg"
},
{
"name": "Arecsu",
"homepage": "https://github.com/Arecsu",
"avatar": "https://avatars.githubusercontent.com/u/12679098"
}
],
"clientStyles": ["client.css"],
"clientScripts": ["client.mjs"],
"electronScripts": [
[".webpack/main/index.js", "electron.cjs"],
[".webpack/renderer/tabs/preload.js", "tabs.cjs"]
]
}

View File

@ -0,0 +1,18 @@
/**
* notion-enhancer: focus
* (c) 2020 Arecsu
* (c) 2021 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
* (https://notion-enhancer.github.io/) under the MIT license
*/
"use strict";
module.exports = async (api, db) => {
const { onMessage } = api,
focusClass = "sidebar-collapsed";
onMessage("notion-enhancer:focus", (message) => {
const $root = document.querySelector("#root");
if (message === "sidebar-pinned") $root?.classList.remove(focusClass);
if (message === "sidebar-collapsed") $root?.classList.add(focusClass);
});
};

View File

@ -0,0 +1,52 @@
/**
* notion-enhancer: fonts
* (c) 2021 TorchAtlas (https://github.com/torchatlas/)
* (c) 2021 admiraldus (https://github.com/admiraldus)
* (c) 2024 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
* (https://notion-enhancer.github.io/) under the MIT license
*/
:root {
--font--sans: ui-sans-serif, -apple-system, BlinkMacSystemFont,
"Segoe UI", Helvetica, "Apple Color Emoji", Arial, sans-serif,
"Segoe UI Emoji", "Segoe UI Symbol";
--font--serif: Lyon-Text, Georgia, ui-serif, serif;
--font--mono: iawriter-mono, Nitti, Menlo, Courier, monospace;
--font--code: "SFMono-Regular", Menlo, Consolas, "PT Mono",
"Liberation Mono", Courier, monospace;
--font--math: KaTeX_Main, Times New Roman, serif;
--font--quotes: inherit;
--font--headings: inherit;
}
[style*="Segoe UI"] {
font-family: var(--font--sans) !important;
}
[style*="Georgia"] {
font-family: var(--font--serif) !important;
}
[style*="iawriter-mono"] {
font-family: var(--font--mono) !important;
}
[style*=SFMono-Regular] {
font-family: var(--font--code) !important;
}
[placeholder='Untitled'],
[placeholder='Heading 1'],
[placeholder='Heading 2'],
[placeholder='Heading 3'] {
font-family: var(--font--headings) !important;
}
.notion-quote-block {
font-family: var(--font--quotes) !important;
}
.katex,
.katex * {
font-family: var(--font--math) !important;
}

View File

@ -0,0 +1,23 @@
/**
* notion-enhancer: fonts
* (c) 2021 TorchAtlas (https://github.com/torchatlas/)
* (c) 2021 admiraldus (https://github.com/admiraldus
* (c) 2024 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
* (https://notion-enhancer.github.io/) under the MIT license
*/
export default async (api, db) => {
const $root = document.documentElement;
for (const style of [
"sans",
"serif",
"mono",
"code",
"math",
"quotes",
"headings",
]) {
const font = await db.get(style);
if (font) $root.style.setProperty(`--font--${style}`, font);
}
};

View File

@ -0,0 +1,66 @@
{
"name": "Fonts",
"version": "0.5.0",
"id": "e0d8d148-45e7-4d79-8313-e7b2ad8abe16",
"description": "Replace Notion's default fonts with any font installed on your system.",
"tags": ["customisation", "font-chooser"],
"authors": [
{
"name": "dragonwocky",
"homepage": "https://dragonwocky.me/",
"avatar": "https://dragonwocky.me/avatar.jpg"
},
{
"name": "TorchAtlas",
"homepage": "https://github.com/torchatlas/",
"avatar": "https://avatars.githubusercontent.com/u/12666855"
}
],
"options": [
{
"type": "text",
"key": "sans",
"label": "Sans serif",
"description": "Sets the font used across Notion's interface and as the default page font. Leave this blank to use Notion's default sans serif font.",
"value": "ui-sans-serif, -apple-system, BlinkMacSystemFont, \"Segoe UI\", Helvetica, \"Apple Color Emoji\", Arial, sans-serif, \"Segoe UI Emoji\", \"Segoe UI Symbol\""
},
{
"type": "text",
"key": "serif",
"description": "Sets the font used on serif-styled pages (configurable via the <i class='i-ellipsis -mb-px'></i> <i>Style, export and more...</i> menu). Leave this blank to use Notion's default serif font.",
"value": "Lyon-Text, Georgia, ui-serif, serif"
},
{
"type": "text",
"key": "mono",
"description": "Sets the font used on mono-styled pages (configurable via the <i class='i-ellipsis -mb-px'></i> <i>Style, export and more...</i> menu). Leave this blank to use Notion's default monospaced font.",
"value": "iawriter-mono, Nitti, Menlo, Courier, monospace"
},
{
"type": "text",
"key": "code",
"description": "Sets the font used for code blocks and inline code. Leave this blank to use Notion's default code font.",
"value": "\"SFMono-Regular\", Menlo, Consolas, \"PT Mono\", \"Liberation Mono\", Courier, monospace"
},
{
"type": "text",
"key": "math",
"description": "Sets the font used for math equations. Leave this blank to use Notion's default math font.",
"value": "KaTeX_Main, Times New Roman, serif"
},
{
"type": "text",
"key": "quotes",
"description": "Sets the font used for quote blocks. Leave this blank to inherit the page's font style.",
"value": ""
},
{
"type": "text",
"key": "headings",
"description": "Sets the font used for page headings. Leave this blank to inherit the page's font style.",
"value": ""
}
],
"clientStyles": ["client.css"],
"clientScripts": ["client.mjs"]
}

View File

@ -0,0 +1,84 @@
/**
* notion-enhancer: indent guides
* (c) 2020 Alexa Baldon <alnbaldon@gmail.com> (https://github.com/runargs)
* (c) 2024 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
* (https://notion-enhancer.github.io/) under the MIT license
*/
body {
--guide--style: solid;
--guide--color: var(--theme--fg-border);
--guide--opacity: 0;
}
/* add indent guides to nested blocks */
.notion-header-block,
.notion-sub_header-block,
.notion-sub_sub_header-block,
.notion-toggle-block,
.notion-to_do-block,
.notion-bulleted_list-block,
.notion-numbered_list-block {
--guide--offset: 32px;
--guide--indent: 14px;
position: relative;
&:before {
content: "";
position: absolute;
height: calc(100% - var(--guide--offset));
top: var(--guide--offset);
margin-inline-start: var(--guide--indent);
border-left: 1px var(--guide--style) var(--guide--color);
opacity: var(--guide--opacity);
}
}
.notion-header-block {
--guide--offset: 47px;
}
.notion-sub_header-block {
--guide--offset: 40px;
}
.notion-header-block,
.notion-sub_header-block,
.notion-sub_sub_header-block,
.notion-toggle-block {
--guide--indent: 13.4px;
}
/* add indent guides to toc blocks & the outliner */
.notion-table_of_contents-block
[contenteditable="false"]
a
> div:not([style*="margin-left: 0"]),
.notion-enhancer--outliner-heading:not(.pl-\[18px\]) {
position: relative;
--guide--indent: -16px;
&:before {
content: "";
top: 0;
position: absolute;
height: 100%;
margin-inline-start: var(--guide--indent);
border-left: 1px var(--guide--style) var(--guide--color);
opacity: var(--guide--opacity);
}
}
.notion-enhancer--outliner-heading:not(.pl-\[18px\]) {
--guide--indent: -12px;
}
/* add solid background to drag handles,
otherwise guides show through underneath */
[role="button"]:is([aria-label="Drag"], [aria-label^="Click to add below"]) {
position: relative;
&:before {
content: "";
z-index: -1;
position: absolute;
width: 100%;
height: 100%;
background: var(--theme--bg-primary);
}
}

View File

@ -0,0 +1,66 @@
/**
* notion-enhancer: indent guides
* (c) 2020 Alexa Baldon <alnbaldon@gmail.com> (https://github.com/runargs)
* (c) 2024 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
* (https://notion-enhancer.github.io/) under the MIT license
*/
export default async function (api, db) {
const { html } = api,
guideStyle = await db.get("guideStyle"),
rainbowMode = await db.get("rainbowMode");
document.body.style.setProperty("--guide--style", guideStyle.toLowerCase());
const nestedTargets = [],
outlineTargets = [];
for (const [listType, selectors] of [
["to-doList", [".notion-to_do-block"]],
["bulletedList", [".notion-bulleted_list-block"]],
["numberedList", [".notion-numbered_list-block"]],
["toggleList", [".notion-toggle-block"]],
[
"toggleHeadings",
[
".notion-header-block",
".notion-sub_header-block",
".notion-sub_sub_header-block",
],
],
]) {
if (await db.get(listType)) nestedTargets.push(...selectors);
}
if (await db.get("tableOfContents"))
outlineTargets.push(".notion-table_of_contents-block");
if (await db.get("outliner"))
outlineTargets.push(".notion-enhancer--outliner-heading");
let css = `${[...nestedTargets, ...outlineTargets].join(",")} {
--guide--opacity: 1;
}`;
if (rainbowMode) {
const opacity = `--guide--opacity: 0.5;`,
selector = `:is(${nestedTargets.join(",")})`,
colours = ["green", "blue", "purple", "pink", "red", "orange", "yellow"];
colours.push(...colours, ...colours, ...colours, "gray");
for (let i = 0; i < colours.length; i++) {
css += `${(selector + " ").repeat(i + 1)} {
--guide--color: var(--theme--fg-${colours[i]});
${opacity}
}`;
}
css += `
.notion-table_of_contents-block [contenteditable="false"] a
> div[style*="margin-left: 24px"],
.notion-enhancer--outliner-heading.pl-\\[36px\\] {
--guide--color: var(--theme--fg-${colours[0]});
${opacity}
}
.notion-table_of_contents-block [contenteditable="false"] a
> div[style*="margin-left: 48px"],
.notion-enhancer--outliner-heading.pl-\\[54px\\] {
--guide--color: var(--theme--fg-${colours[1]});
${opacity}
}`;
}
document.head.append(html`<style innerHTML=${css}></style>`);
}

View File

@ -0,0 +1,79 @@
{
"name": "Indent Guides",
"id": "35815b3b-3916-4dc6-8769-c9c2448f8b57",
"version": "0.3.0",
"description": "Marks list indentation with vertical lines to make it easy to follow.",
"tags": ["extension", "usability", "indentation-lines"],
"authors": [
{
"name": "dragonwocky",
"homepage": "https://dragonwocky.me/",
"avatar": "https://dragonwocky.me/avatar.jpg"
},
{
"name": "runargs",
"email": "alnbaldon@gmail.com",
"homepage": "http://github.com/runargs",
"avatar": "https://avatars.githubusercontent.com/u/39810066"
}
],
"options": [
{
"type": "select",
"key": "guideStyle",
"description": "The type of line to use for indent guides.",
"values": ["Solid", "Dashed", "Dotted"]
},
{
"type": "toggle",
"key": "rainbowMode",
"description": "By default, indent guides are coloured based on the current theme. Rainbow mode uses alternating colours at each indent level to better connect corresponding blocks in longer lists.",
"value": false
},
{ "type": "heading", "label": "List Types" },
{
"type": "toggle",
"key": "to-doList",
"description": "Shows indent guides for Notion's to-do list blocks.",
"value": true
},
{
"type": "toggle",
"key": "bulletedList",
"description": "Shows indent guides for Notion's bulleted list blocks.",
"value": true
},
{
"type": "toggle",
"key": "numberedList",
"description": "Shows indent guides for Notion's numbered list blocks.",
"value": true
},
{
"type": "toggle",
"key": "toggleList",
"description": "Shows indent guides for Notion's toggle list blocks.",
"value": true
},
{
"type": "toggle",
"key": "toggleHeadings",
"description": "Shows indent guides for Notion's toggle heading blocks.",
"value": true
},
{
"type": "toggle",
"key": "tableOfContents",
"description": "Shows indent guides for Notion's table of contents blocks.",
"value": true
},
{
"type": "toggle",
"key": "outliner",
"description": "Shows indent guides for the Outliner's table of contents in the side panel.",
"value": true
}
],
"clientStyles": ["client.css"],
"clientScripts": ["client.mjs"]
}

View File

@ -0,0 +1,105 @@
/**
* notion-enhancer: line numbers
* (c) 2020 CloudHill <rl.cloudhill@gmail.com> (https://github.com/CloudHill)
* (c) 2024 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
* (https://notion-enhancer.github.io/) under the MIT license
*/
function LineNumbers({ decorationStyle = "None" }) {
const { html } = globalThis.__enhancerApi,
decorations = {
Border: `pr-[16px] border-r-([2px]
[color:var(--theme--bg-hover)])`,
Background: `pr-[4px] before:(absolute block
h-full w-[calc(100%-24px)] rounded-[4px] right-0
content-empty bg-[var(--theme--bg-hover)] z-[-1])`,
};
return html`<div
class="notion-enhancer--line-numbers mt-[34px]
text-([85%] [var(--theme--fg-secondary)] right)
font-[var(--font--code)] overflow-hidden select-none
relative flex-grow ${decorations[decorationStyle] || ""}"
></div>`;
}
export default async (api, db) => {
const { html, addMutationListener } = api,
decorationStyle = await db.get("decorationStyle"),
numberSingleLines = await db.get("numberSingleLines"),
codeBlockSelector = ".notion-code-block.line-numbers > .notranslate";
// get character width in pixels
const getCharWidth = ($elem) => {
const $char = html`<span style="width:1ch"> </span>`;
$elem.append($char);
const charWidth = getComputedStyle($char).getPropertyValue("width");
$char.remove();
return parseFloat(charWidth);
},
// get line width in pixels
getLineWidth = ($elem) =>
parseFloat(getComputedStyle($elem).getPropertyValue("width")) -
parseFloat(getComputedStyle($elem).getPropertyValue("padding-left")) -
parseFloat(getComputedStyle($elem).getPropertyValue("padding-right")),
// get line height in pixels
getLineHeight = ($elem) =>
parseFloat(getComputedStyle($elem).getPropertyValue("line-height")),
// update inline styles without unnecessary dom updates
applyStyles = ($elem, styles) => {
for (const property in styles) {
if ($elem.style[property] === styles[property]) continue;
$elem.style[property] = styles[property];
}
};
const numberLines = () => {
for (const $code of document.querySelectorAll(codeBlockSelector)) {
const wrap = $code.style.wordBreak === "break-all",
lines = $code.innerText.split("\n"),
numLines = Math.max(lines.length - 1, 1),
numChars = lines.map((line) => line.length).join(","),
numDigits = (Math.log(numLines) * Math.LOG10E + 1) | 0;
if ($code.dataset.lines === wrap + "," + numChars) continue;
$code.dataset.lines = wrap + "," + numChars;
// do not add to single-line blocks if disabled
const visible = numberSingleLines || numLines > 1,
width = visible
? decorationStyle === "Border"
? `calc(100% - 50px - ${numDigits}ch)`
: `calc(100% - 32px - ${numDigits}ch)`
: "",
paddingLeft = visible && decorationStyle === "Border" ? "16px" : "32px";
// shrink block to allow space for numbers
applyStyles($code.parentElement, { justifyContent: "flex-end" });
applyStyles($code, { minWidth: width, maxWidth: width, paddingLeft });
// calculate heights of wrapped lines and render line nums
let totalHeight = 0;
const lineHeight = getLineHeight($code),
charsPerLine = Math.floor(getLineWidth($code) / getCharWidth($code));
$code._$lineNumbers ||= html`<${LineNumbers}...${{ decorationStyle }} />`;
for (let i = 1; i <= numLines; i++) {
const $n = $code._$lineNumbers.children[i - 1] || html`<p>${i}</p>`;
if (!$code._$lineNumbers.contains($n)) $code._$lineNumbers.append($n);
const wrappedHeight =
wrap && lines[i - 1].length > charsPerLine
? Math.ceil(lines[i - 1].length / charsPerLine) * lineHeight
: lineHeight;
applyStyles($n, { height: `${wrappedHeight}px` });
totalHeight += wrappedHeight;
}
applyStyles($code._$lineNumbers, {
display: visible ? "" : "none",
height: `${totalHeight}px`,
});
if (visible && !document.contains($code._$lineNumbers)) {
$code.before($code._$lineNumbers);
} else if (!visible) $code._$lineNumbers.style.display = "none";
}
};
addMutationListener(codeBlockSelector, numberLines);
};

View File

@ -0,0 +1,34 @@
{
"name": "Line Numbers",
"id": "d61dc8a7-b195-465b-935f-53eea9efe74e",
"version": "0.5.0",
"description": "Adds line numbers to code blocks.",
"tags": ["code-line-numbers"],
"authors": [
{
"name": "dragonwocky",
"homepage": "https://dragonwocky.me/",
"avatar": "https://dragonwocky.me/avatar.jpg"
},
{
"name": "CloudHill",
"homepage": "https://github.com/CloudHill",
"avatar": "https://avatars.githubusercontent.com/u/54142180"
}
],
"options": [
{
"type": "toggle",
"key": "numberSingleLines",
"description": "Adds line numbers to code blocks with only one line.",
"value": true
},
{
"type": "select",
"key": "decorationStyle",
"description": "Decorates line numbers with additional styling to distinguish them from code block content.",
"values": ["Border", "Background", "None"]
}
],
"clientScripts": ["client.mjs"]
}

View File

@ -0,0 +1,9 @@
/**
* notion-enhancer: no peeking
* (c) 2021 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
* (https://notion-enhancer.github.io/) under the MIT license
*/
.notion-peek-renderer {
display: none;
}

View File

@ -0,0 +1,26 @@
/**
* notion-enhancer: no peeking
* (c) 2021 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
* (https://notion-enhancer.github.io/) under the MIT license
*/
export default async (api) => {
const { addMutationListener } = api,
peekRenderer = ".notion-peek-renderer",
openInFullPage = `[aria-label="Open in full page"]`,
pageId = () => location.pathname.split(/-|\//g).at(-1),
peekId = () => new URLSearchParams(location.search).get("p");
let _pageId = pageId();
const skipPeek = () => {
const $openInFullPage = document.querySelector(openInFullPage);
if (peekId() === _pageId) {
_pageId = pageId();
history.back();
} else if (peekId() && $openInFullPage) {
_pageId = peekId();
$openInFullPage.click();
} else _pageId = pageId();
};
addMutationListener(peekRenderer, skipPeek);
};

View File

@ -0,0 +1,16 @@
{
"name": "No Peeking",
"id": "cb6fd684-f113-4a7a-9423-8f0f0cff069f",
"version": "0.3.0",
"description": "Globally force pages opening in side peek or center peek to open as full pages instead.",
"tags": ["automation"],
"authors": [
{
"name": "dragonwocky",
"homepage": "https://dragonwocky.me/",
"avatar": "https://dragonwocky.me/avatar.jpg"
}
],
"clientStyles": ["client.css"],
"clientScripts": ["client.mjs"]
}

View File

@ -0,0 +1,135 @@
/**
* notion-enhancer: outliner
* (c) 2021 CloudHill <rl.cloudhill@gmail.com> (https://github.com/CloudHill)
* (c) 2024 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
* (https://notion-enhancer.github.io/) under the MIT license
*/
import { Heading } from "./islands/Heading.mjs";
import { PanelDescription } from "./islands/PanelDescription.mjs";
export default async (api, db) => {
const { html, debounce, addMutationListener, addPanelView } = api,
behavior = (await db.get("smoothScrolling")) ? "smooth" : "auto",
scroller = ".notion-frame .notion-scroller",
equation = ".notion-text-equation-token",
annotation = (await db.get("equationRendering"))
? ".katex-html"
: ".katex-mathml annotation",
page = ".notion-page-content",
headings = [
".notion-header-block",
".notion-sub_header-block",
".notion-sub_sub_header-block",
],
$toc = html`<div></div>`;
addPanelView({
title: "Outliner",
// prettier-ignore
$icon: html`<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24">
<circle cx="5" cy="7" r="2.8"/>
<circle cx="5" cy="17" r="2.79"/>
<path d="M21,5.95H11c-0.55,0-1-0.45-1-1v0c0-0.55,0.45-1,1-1h10c0.55,0,1,0.45,1,1v0C22,5.5,21.55,5.95,21,5.95z"/>
<path d="M17,10.05h-6c-0.55,0-1-0.45-1-1v0c0-0.55,0.45-1,1-1h6c0.55,0,1,0.45,1,1v0C18,9.6,17.55,10.05,17,10.05z"/>
<path d="M21,15.95H11c-0.55,0-1-0.45-1-1v0c0-0.55,0.45-1,1-1h10c0.55,0,1,0.45,1,1v0C22,15.5,21.55,15.95,21,15.95z" />
<path d="M17,20.05h-6c-0.55,0-1-0.45-1-1v0c0-0.55,0.45-1,1-1h6c0.55,0,1,0.45,1,1v0C18,19.6,17.55,20.05,17,20.05z"/>
</svg>`,
$view: html`<section>
<${PanelDescription}>Click on a heading to jump to it.<//>
${$toc}
</section>`,
});
const replaceFloatingOutline = await db.get("replaceFloatingOutline");
if (replaceFloatingOutline) {
document.head.append(html`<style>
.hide-scrollbar.ignore-scrolling-container:has(
div:empty[style*="width"]
) {
display: none !important;
}
</style>`);
}
let $page, $scroller;
const getHeadings = () => {
if (!$page) return [];
return [...$page.querySelectorAll(headings.join(", "))];
},
getHeadingLevel = ($heading) => {
for (let i = 0; i < headings.length; i++)
if ($heading.matches(headings[i])) return i + 1;
},
getHeadingTitle = ($heading) => {
if (!$heading.innerText) return "Untitled";
let title = "";
for (const node of $heading.querySelector("h2, h3, h4").childNodes) {
if (node.nodeType === 3) title += node.textContent;
else if (node.matches(equation)) {
// https://github.com/notion-enhancer/repo/issues/39
const $katex = node.querySelector(annotation);
title += $katex.textContent;
} else title += node.innerText;
}
return title;
},
getBlockOffset = ($block) => {
let offset = 0;
while (!$block?.matches("[data-content-editable-root]")) {
offset += $block.offsetTop;
$block = $block.offsetParent;
}
return offset;
},
updateHeadings = debounce(() => {
$toc.innerHTML = "";
if (!$page) return;
let indent = 0,
prev_level = 0;
const $frag = document.createDocumentFragment();
for (const $heading of getHeadings()) {
const level = getHeadingLevel($heading);
if (level === 1) indent = 1;
else if (level > prev_level) indent = Math.min(indent + 1, level);
else if (level < prev_level) indent = Math.max(indent - 1, level);
prev_level = level;
$heading._$outline = html`<${Heading}
...${{ indent }}
onclick=${() => {
if (!$scroller) return;
const top = getBlockOffset($heading) - 24;
$scroller.scrollTo({ top, behavior });
}}
>${getHeadingTitle($heading)}
<//>`;
$frag.append($heading._$outline);
}
$toc.append($frag);
onScroll();
});
const $progressMarker = html`<span
class="absolute block left-[6px] top-[calc(50%-1px)]
size-[6px] rounded-full bg-[color:var(--theme--fg-secondary)]"
></span>`,
onScroll = () => {
if (!$scroller) return;
const $h = getHeadings().find(($h) => {
return $scroller.scrollTop < getBlockOffset($h) - 16;
})?._$outline;
if ($h) $h.prepend($progressMarker);
},
setup = () => {
if (document.contains($page)) return;
$page = document.querySelector(page);
$scroller = document.querySelector(scroller);
$scroller?.removeEventListener("scroll", onScroll);
$scroller?.addEventListener("scroll", onScroll);
updateHeadings();
};
const semanticHeadings = '[class$="header-block"] :is(h2, h3, h4)';
addMutationListener(`${page} ${semanticHeadings}`, updateHeadings);
addMutationListener(`${page}, ${scroller}`, setup, { subtree: false });
setup();
};

View File

@ -0,0 +1,23 @@
/**
* notion-enhancer: outliner
* (c) 2024 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
* (https://notion-enhancer.github.io/) under the MIT license
*/
function Heading({ indent, ...props }, ...children) {
const { html } = globalThis.__enhancerApi;
return html`<div
role="button"
class="notion-enhancer--outliner-heading block
relative cursor-pointer select-none text-[14px]
decoration-(2 [color:var(--theme--fg-border)])
hover:bg-[color:var(--theme--bg-hover)]
py-[6px] pr-[2px] pl-[${indent * 18}px]
underline-(~ offset-4) last:mb-[24px]"
...${props}
>
${children}
</div>`;
}
export { Heading };

View File

@ -0,0 +1,16 @@
/**
* notion-enhancer: outliner
* (c) 2024 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
* (https://notion-enhancer.github.io/) under the MIT license
*/
function PanelDescription(props, ...children) {
const { html, extendProps } = globalThis.__enhancerApi;
extendProps(props, {
class: `py-[12px] px-[18px] text-(
[13px] [color:var(--theme--fg-secondary)])`,
});
return html` <p ...${props}>${children}</p>`;
}
export { PanelDescription };

View File

@ -0,0 +1,44 @@
{
"name": "Outliner",
"version": "0.5.0",
"id": "87e077cc-5402-451c-ac70-27cc4ae65546",
"description": "Adds a table of contents to the side panel to overview and navigate the current page's headings and subheadings.",
"tags": [
"panel"
],
"authors": [
{
"name": "dragonwocky",
"homepage": "https://dragonwocky.me/",
"avatar": "https://dragonwocky.me/avatar.jpg"
},
{
"name": "CloudHill",
"homepage": "https://github.com/CloudHill",
"avatar": "https://avatars.githubusercontent.com/u/54142180"
}
],
"options": [
{
"type": "toggle",
"key": "smoothScrolling",
"description": "Animates scrolling to a heading smoothly. Disable this to jump to a heading instantly when clicking it in the Outliner's table of contents.",
"value": true
},
{
"type": "toggle",
"key": "equationRendering",
"description": "Attempts to render special symbols from inline equations in headings. Note that position- and size-based formatting will be lost when displaying equations in the Outliner's table of contents. Disable this to display the raw TeX equation instead.",
"value": true
},
{
"type": "toggle",
"key": "replaceFloatingOutline",
"description": "Disables Notion's builtin floating table of contents for a complete switch to the Outliner.",
"value": true
}
],
"clientScripts": [
"client.mjs"
]
}

View File

@ -0,0 +1,15 @@
/**
* notion-enhancer: right to left
* (c) 2021 obahareth <omar@omar.engineer> (https://omar.engineer)
* (c) 2024 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
* (https://notion-enhancer.github.io/) under the MIT license
*/
/* indent rtl toc header levels,
* https://github.com/notion-enhancer/notion-enhancer/issues/616 */
.notion-table_of_contents-block div[style*="margin-left: 24px"] {
margin-inline-start: 24px;
}
.notion-table_of_contents-block div[style*="margin-left: 48px"] {
margin-inline-start: 48px;
}

Some files were not shown because too many files have changed in this diff Show More