βq°ππβ¨οΈ The ultimate notes app β¨οΈππ°qβ
Citronote is a feature-rich note-taking app for desktop. In version 3, I'm re-building the code base, functionality, and more from scratch with the goal of creating the ultimate note-taking app that can: "Save Anything."
This Summer of Making, my goal is to release preview versions even if they can't yet replace the existing version.
No followers yet
Once you ship this you can't edit the description of the project, but you'll be able to add more devlogs and re-ship it as you add new features!
I'm making the final push towards ship.
I've almost finished the trailer video, but I forgot some features I needed to show, so I'm having to shoehorn them in.
Right now I'm mainly working on a demo page. Since my project is Windows software, it'll be a sort of landing page with a download button.
I made a mistake in Git and my directory got corrupted, so I'll git clone it again and work in a new directory.
I've also added the editor library I forked for this app and the Hackatime Project, where I was exploring creating an editor, to this SoM project.
I'm currently working on creating a trailer video. I thought it would be cool to have a trailer.
Suno.ai generated a song that I thought would be a great fit, even better than I expected, so I'll use that song. It's not for commercial use, so it should be okay.
I'm currently preparing for Ship.
It's now possible to add images using Markdown. A better implementation would require a large amount of replacement, which probably wouldn't be possible in time, so I forked the editor library as a temporary implementation and added the necessary functionality.
I've also fixed some minor issues, such as the feedback URL not working and the UI being fixed in Japanese due to lack of localization. I've also created a feedback form.
Im currently preparing for the beta release.
In a previous Devlog, I mentioned trying out Tauri, but it turned out to be difficult, so I went with Electron instead. (Iβm aware that some people donβt prefer Electron apps, so Tauri will remain part of the future vision.)
Most of the Electron app implementation was copied from the previous codebase.
Additionally, I added desktop-specific implementations for the React app, the core logic package, and the built-in features package.
For example:
I also worked on other features, such as:
<img />
; I replaced them with a React component using SVG to allow color changes according to the themeA problem I ran into today: after building the desktop version, the exe tries to load the appβs HTML, but:
I concluded that access was denied because the file didnβt exist in the first place. I also assumed that it was referencing HTML files not in the code because it was falling back due to the original files being missing. I tried many solutions, like correcting the path notation and using different functions.
But none of the solutions worked. I even added logging and window output to debug the file paths being loaded, but nothing was logged at all.
Something seemed offβ¦ and indeed, the issue was that the desktop package doesnβt build the source code contents when generating the exe. In other words, although I thought I was changing the process through repeated trial and error, I had actually been inspecting the same generated output all along. WTF
I've completed a rough implementation of the Scrap feature (infinite scrolling and other features are very difficult and time-consuming, so I implemented a rough implementation that rendered everything), made various adjustments, file uploads, and worked on components that had not yet been implemented and parts that I had been putting off.
I'm currently working on a desktop package using Tauri.
Also, I think the vscode source code will be useful for file system abstraction.
I can now create folders and notes! (Until now, there actually wasnβt any UI for creating them.)
I tried to make a UI like in VSCode, where nested folders are listed and clicking smoothly turns the folder into an input box. But it turned out to be pretty challenging in terms of both implementation and UI design. In the end, it came out nicely, so Iβm glad. (Though some details like existence checks are still missingβIβll add those later.)
Right now, Iβm reviewing and rebuilding the interface of the abstracted file system class.
Also, while talking with ChatGPT, I learned about something called JSDoc. I had thought it was just a relic from the pre-TypeScript era for explicitly stating type information, but being able to write natural language descriptions that show up when hovering over a function is super convenient!
I also want to get better at writing clean, maintainable code, so I bought a book called The Art of Readable Code (Japanese edition). Apparently, itβs quite famous.
Recent progress:
I have also come up with an idea for another project and feel like working on it too, but since I am not sure if that would make it in time for the Summer of Making deadline, it seems better to stay locked in on this one.
I'm continuing work on the editor! I think the progress is going well :)
Main things I did:
I feel like Iβve built a pretty nice plain text editor! Tackling the important functionality of reading and writing files has made me feel like things have really moved forward.
Also, file saving is now debounced. (This was a behavior I had already decided on a while ago.)
The attached file is from a session a little while back, so things are slightly better now.
Ah!! My Unlogged Time has reached 11 hours!!
Whatβs worth mentioning about what Iβve done recently is the design of the βnote typesβ and the development and design of their extensibility and various other things.
Specifically, I implemented:
Since app components might need to be used by built-ins or plugins, we may need to add packages/ui.
I had planned to implement plugin functionality, but it seems difficult, so for now Iβm focusing only on creating a codebase that will make implementing plugins easier in the future. Plugin functionality will come after the MVP ship.
Also, Iβve been developing somewhat aimlessly, and surface-level progress hasnβt been muchβ¦ I usually set βdaily work hoursβ as my goal, but maybe it would be better to set goals like βfeatures to implement today.β Also, development and design might need to be more agileβ¦ :thinking: (The biggest reason development is slow is that Iβm completely inexperienced with large-scale development. My brainβs RAM is slow :( I want to upgrade to DDR5.)
I also need to increase the frequency of Devlogs. If I get lazy and donβt write Devlogs, I forget the details of my development. Honestly, part of me likes saving things up and dumping them all at once, but if I forget details or accidentally go over 10 hours, that defeats the purpose. So Iβll post Devlogs more frequently from now on. Also, Devlogs donβt need to be ridiculously long blog-post-length entries; they can be short, like commit messages.
Aside:
Iβm an individual developer, providing non-physical data to the world via the internet and other channels, but I have a fascination with shipping actual physical products.
Iβm thinking that if this project gains some recognition, Iβd like to create a merch store. Specifically, Iβve made an on-demand store before where you just upload images to create a store, but what I want is to actually hold inventory, ship items myself, and sell things Iβve designed from scratch, not just items with a logo. Doesnβt that sound fun?
Iβve been researching and discussing with ChatGPT, and today I properly learned the concept of OEM. The idea is to design and conceptualize products and then have a manufacturing company produce them. Of course, it will probably be expensive, but I want to do it someday.
β¦Daydreaming about this is fun, but what I can actually do now is type code and bring it to completionβ¦ development is patience.
In the previous Devlog, I was laying the foundation for packages/core (shared logic), but now Iβve mainly started working on packages/app (the React app package).
Right now, Iβm focusing on implementing βfoldersβ in packages/app. Folders can now be displayed in the tree. You canβt create new folders yet, but thatβs only because I havenβt made a proper UI/component for itβthe underlying mechanism for creating folders is already in place.
β
Migrated components and files into packages/app that can be reused as-is from the previous codebase
β
Improved handling of βfoldersβ
β
Display folders in the sidebar tree
β
Previously, the old codebase loaded all folders at once, but now theyβre only loaded when expanded
β
Updated the sidebar tab system to make it easier to extend in the future
β
404 page
β
Handling for browsers that donβt support required Web APIs, plus related component updates
β
Minimal PWA (WIP)
β
And more...
Also, I mentioned before that I would rebuild the codebase from scratch, but for packages/app Iβve carried over a lot from the previous codebase.
Because I was unfamiliar with large-scale development, my code base had become a mess after numerous specification changes.
I lost hope in the current code base, which was riddled with technical debt, so I created a new code base.
I also created a new Git repository.
Oh no, my unlogged time exceeded 10 hours...
This time, I worked on the core design again. It took a lot of trial and error, but I've pretty much nailed down the design. Or rather, I've decided on it.
Things I've tried over the past while:
* How should I import/export packages/core when using it from packages/app? (Consolidate it in index.ts / Barrel export, etc.)
* Should core be an init function + a fairly independent singleton class file? Or should I create a class called Core and consolidate everything there? (In that case, packages/app does not import anything other than the Core class.)
* After much deliberation, I looked into the source code of the OSS I use and found that it uses something called a DI Library. Using this as a reference, I tried out TSyringe.
* I consulted ChatGPT extensively about the specifications until I hit the daily limit.
* And various other things...
In the end, for now,
* I barrel export separate files like initCore and NotebookManager, and use each one in the app.
* Each class file is a singleton.
* TSyringe doesn't seem necessary yet in this design, so I've removed it for now.
So, I'm moving forward with this. If there are no particular problems, I'll leave it as is.
Im rethinking the design.
Currently, Citronote uses a monorepo structure, with the following packages planned:
At the moment, some logic still exists inside app, and the export approach from core is temporary. However, as I work on implementing the concept of note types, I've decided to improve the design of core.
Specifically, with the current design, the appβs note types would end up being hardcoded. But since I want users to be able to add note types via plugins in the future, Iβve decided to both redesign core and implement this with extensibility in mind.
Also, with this redesign, the app's features wonβt be so tightly bound to the app itself - they'll be more flexible. That means it'll be easier to implement user plugin functionality, which is great! Before, I felt like I donβt know how to make extension points for plugins, it seems difficult, but now it feels like it might be possible.
Iβve been working on an editor pane that includes a Markdown editor, a plain text editor, and an image viewer. In the end, I decided to go with a Markdown editor library called ProseMark. Itβs still under development, but I really like where itβs heading, so I wanted to give it a try.
Figuring out how to build the Markdown editor itself took me a long time.
In a note-taking app I made before, I used a library called EasyMDE, which is a fork of the well-known SimpleMDE editor. But honestly, I wasnβt too happy with how it worked. EasyMDE makes you switch between the raw editor and the rendered preview, and while thatβs fine, Iβve always admired the smooth WYSIWYG style of editors like Typora or Obsidian.
So I went hunting for a proper WYSIWYG Markdown editor library. The first round of research didnβt turn up anything great.
At that point, I thought Iβd just build one myself. I tried hacking around with an HTML contentEditable element to hide and show Markdown syntax depending on the cursor position. That turned out to be harder than I expected, so I gave up. My next attempt was to build it with Codemirror. There are some examples of Markdown editors using Codemirror v5, but the current major version is v6, which has a completely different API. This attempt went better than the first one, but it was still tough, and in the end I started looking for existing libraries again.
Thatβs when I discovered a library called HyperMD, which looked like a perfect fit for what I wanted. It worked really well, but then I noticed the last commit was seven years ago. Itβs built on Codemirror v5 too, which doesnβt help.
I wondered if anyone had forked HyperMD and kept it alive. That search led me to ProseMark, which is basically a fresh take on HyperMD built on Codemirror v6. Itβs still in development, so some features are missing and parts of the UX feel a little rough, but I see a lot of potential in it, so I decided to adopt it.
Whatβs cool is that ProseMark is basically doing what I once tried (and failed) to do - building a WYSIWYG Markdown editor on Codemirror v6. I really respect that, and if I get some free time, Iβd love to contribute to the project.
β¨οΈ The page for creating notebooks is (almost) complete.
β¨οΈ I created a menu for clicking on a notebook.
β¨οΈ I added the exists function and the ability to load binary files to the abstracted Filesystem class.
β¨οΈ You can now add icons to notebooks.
π· I've made progress on developing a theme cache for the background during startup.
π· The focus ring style has been improved.
π· Some components have been standardized.
π· I've added a Zustand store for storing classes for currently open notebooks.
π§ I'm working on PWA support.
π© I was trying to implement a mechanism for opening popovers from tab components, but I couldn't figure it out, so I put it off.
π©Ή I fixed an issue where elements would overflow and not fit on a single screen.
and more...
I haven't been able to work much lately, so I think I should take a little more time.
Iβve been hopping around and digging into different features.
I made a Git repo earlier, but since I didnβt really plan things out and just kept jumping between features, Iβll probably end up with an all-in-one commit that bundles all my recent work. If this were a production team project and I were the reviewer, Iβd definitely be confused - but since itβs just me, itβs fine.
I donβt really remember everything I worked on, but looking at the git diff jogged my memory:
Oh, and Hackatime says Iβve passed 100 total hours (across all projects). Iβm calculating how much more work I need to do to get the prize Iβm aiming for. Sadly, my summer vacation is almost over, so Iβm not sure how much time Iβll actually have π and what about homework?
As for what I can actually show in the attachment⦠I built a page component with horizontal slide animations. It looks simple, but things like easing and timing turned out to be trickier than expected. Switching from ease-out to ease-in-out made it look way better, which surprised me. (I took inspiration from GNOME and similar systems for the feel.)
Iβve been working on the note handles, revisiting the class design and such, and now the desktop version can also load notes :) Itβs coming along nicely.
Also, I think the UI for the note list has gotten a bit better.
Itβs still a long way from being finished, but Iβd like to have a minimal MVP ready by the end of September. (I believe itβll probably be extended until 9/30, but this island isnβt going to sink away before then, right!?)
Now can handle and display folders.
I also worked on context menus (WIP), theme support (WIP), and other miscellaneous tasks.
Also, I wrote before that I used Biome, but it was difficult, so I changed it to Prettier.
π I finally initialized a Git repo. Andβ¦ this is where the story beginsβ¦! (yeah kinda late lol)
So, Iβve been dealing with some random little chores. Went through the file directories, tidied things up, deleted files I made during trial-and-error but donβt use anymore, and also got rid of empty folders.
Also I addd Biome for code formatting. Prettier is the famous one, of course, but i thought trying something new sounded cooler, so I went with Biome. Figuring out how to ignore files and folders was way harder than I expected (ChatGPT kept suggesting config files that donβt even exist :( )
And yup, finally made the Git repo. I posted on my Devlog a while back like βIt may be time to init Git π€β andβ¦ itβs been two monthsβ¦
I also got a bit stuck thinking about branch strategy. i usually go with Git Flow when Iβm expecting the project to be kinda big, but there are a lot of people online who say Git Flow is bad, so I hesitated. In the end, I figured I can always change things later if stuff breaks, so I went ahead with Git Flow for now.
P.S. I found a Wikipedia article called βAnalysis paralysisββ¦ and wow, thatβs totally me π€¦
https://en.wikipedia.org/wiki/Analysis_paralysis
Implemeting theme feature. I briefly considered trying Emotion, but ended up going back to Tailwind.
Part of the mechanism for managing and loading notebooks
(Im currently working on changing the theme to change my mood)
I'm working on the notebook selector screen. I think the UI is pretty good.
I was pressured by explorpheus on Slack to post a Devlog, so here it is. β I guess I should post more frequently.
Well, recently Iβve been focusing on building the foundation of the project. Also, in the previous Devlog, I said I introduced Redux Toolkit, but I actually switched to Zustand.
-< Abstraction of Notebooks >-
First, this software plans to support two locations to store notes:
Furthermore, this project is planned to support two platforms: browser and desktop (using Electron).
Hereβs the problem: for the local storage, the way to manipulate the file system differs between browser and desktop. The browser uses the File System Access API, while the desktop version uses Nodeβs fs module via ipc.
To handle this efficiently and maintainably, itβs necessary to abstract away the differences between platforms and storage locations inside the app. This time, since there are two layers β platform and storage location on top of that β it was quite difficult. But I think it was a good learning experience for large-scale development.
-< Window Issue >-
Letβs consider the difference between a βnative appβ and an βElectron app.β
While itβs good to unify languages and platforms technically, what I thought from a UI/UX perspective is that Electron apps basically run on a single window and rarely use multiple windows.
For example, the software I use called βYukkuriMovieMakerβ is made with .NET, so I think itβs a native app. It has windows like:
These are implemented as separate windows, which is common. Electron apps tend to use modals layered over a single window instead.
Windows UI itself rarely layers things inside a single window; rather, it uses multiple windows for that.
I think this is because Electron is a framework to build desktop apps with web technologies, and the general web app principle is βone tab = one app session.β Iβve never seen a web app that opens multiple windows.
Also, many Electron apps are basically wrapped web apps originally made for browsers (e.g., Slack).
(I was thinking about this difference when comparing GDLauncher and MultiMC in the past.)
So, simply put, which do you think is more convenient: βmultiple windowsβ or βone window with modalsβ?
I think multiple windows are more convenient. From a userβs perspective, it might be easier to rely on the OSβs native window features.
However, my app has both desktop and web versions. Since browsers cannot open new windows like desktops, it might be better to use windows on desktop and modals on browser accordingly.
That said, as I mentioned, web apps generally treat one tab as one session. In Electron, each window is a BrowserWindow that opens a specified URL and is equivalent to a tab.
Windows behave like separate tabs, so their state (like Zustand state or variables) are isolated and separate. The only way they can communicate is via ipc through the main process.
Writing this out, supporting multiple windows is really tough. Itβs easier if the appβs core logic is in the main process, but since this app also supports the web app version, core features must live in the React app.
Anyway, unrelated to that, I recently needed to create a selection window separate from the main window, so I worked on the mechanism for that. While building it, I was also thinking about making it flexible enough to support the above window usage.
Honestly, I feel like Iβve spent too much time on design and not enough on coding this time, so I temporarily set that aside and created the mechanism for a βwindow that selects which main window to call.β (What was all that text before for???)
Oh, Iβm still undecided on some things and donβt know how it will turn out.
-< So what did I actually do during this time? -->
Since I was lazy and didnβt initialize a Git repo, some things might be missing, but...
I doubt many people actually read Devlogs, but somehow I ended up writing a really long text. Probably itβs better to post short updates more frequently. The window issue and other topics might be interesting enough to write separately on a blog or something.
I did various things, such as introducing Redux Toolkit for state management, creating an electron package in a monorepo, migrating to pnpm, and trying out electron-builder.
This time, I've attached the current state of the installer.
Oh I almost forgot to record it...
I'm experimenting with the note component. It's hard to make the UI look good.
Building Theme Selection (Onboarding)
"Licensed under MPL 2.0"
I just put this temporary text here, but I'm still considering the license I'll use to actually release it.
I'm trying to build an onboarding screen. This is an mockup I made in Figma quite some time ago, but I'll probably change it.
I needed a separate page for onboarding and other things, so I installed react-router-dom.
I also experimented a bit with styling and design (like how to handle the root folder in the UI).
I created a folder collapsing UI. I love how it works and how it looks :)
P.S. I want to publish an app on Google Play, but it seems that people under 18 can't register, so I'm having trouble :(
If anyone has managed to achieve this in some way, please let me know how you did it!
I'm currently thinking about and putting together the technical specifications for the project.
I have also started developing the UI, including the button and folder selection components, as well as the rough layout of the overall UI.