I added a new image popup to my #macOS notes app 🚀
https://video.nostr.build/22aed672da66e5a5420961e727607f9d65a3627611eefd922a2a19eb84db15de.mp4
Simply click on an image, and it will open the image in a popup with a blurry and slightly dark background. I think it looks beautiful 🤩
Also added a subtle fade-out effect to the note content right below the tabs at the top.
Can't wait to begin working on the iPhone and iPad app, but there's still more to be done on the macOS version 💻
I still need to implemented the visual canvas functionality, which will be a major part of the app. I think it will be perfect for brainstorming and organizing notes.
The home tab also needs to be implemented. Right now, there’s just placeholder text. Still figuring out exactly what I want with it 🤔 #dev #Swift #AppKit
Frederik Handberg
npub1nj0c...2gqz
23 🇩🇰 Studying for a degree in Software Engineering while building fun projects and working freelance as a News Photographer 📷
I share my software projects, photos and videos from my work as a news photographer, and progress updates as I learn to sew garments.
Basically, I just write about my hobbies.
frederikhandberg.com
Notes (20)
Funny how in autumn I get way more productive. I just code all the time 😂 And besides coding, I walk the dog 🐕🦺
The weather outside is cold and it’s raining 🌧️ Not very fun to be outside this time of the year…
https://video.nostr.build/0b5345bf00e3578054ca2f471782ad0301c11c0bbdc07b83af497ae065cb8617.mp4
Really well written.
nostr:nevent1qqsxvmrur6jxcnfru4lpuxpwc9c4y0fm7xue2r8xadprzmsn86sagpgzyzusc09hr4nrg0spzpx4exkl0kc96dn98vtkq8lektht42qmueuzxqcyqqqqqqgpzemhxue69uhhyetvv9ujuurjd9kkzmpwdejhgptue6v
Importing images and videos is now working in my notes app 🚀
But it's not quite perfect yet! There's still more work to do 💻
https://video.nostr.build/2bdbf56be17a988d968618f431d61a00ddb660abece83589338730df8141a8f8.mp4
When importing media for the first time, an `/assets` folder is created in the root directory where all media is saved. Media files are reference by relative paths in the note documents.
Need to work more on videos (notice the black bars on top and bottom - they should be removed). When placing cursor on video and then swiping up and down, the video starts scrubbing which is annoying. Need to figure out how to disable that.
Resizing images and videos doesn't work.
I also need to get fullscreen working. Basically, when double-clicking on an image, it should go into "fullscreen" - well, more like a popup with a blurry and dark background. I should probably add an option in the context menu when right-clicking an image to make it a fullscreen image that fills the entire screen. Two more options I should add could be "Open image in new tab" and "Reveal in Finder".
This is unrelated to media and is more of a general issue: There's a problem with the 'Files' sidebar not updating/re-rendering when new files are imported. It just does not render the new files, so they are never displayed in the sidebar, unless the user quits and reopens the app. I think this should be fairly easy to fix. I probably just forgot to observe an `NSNotification`...
#dev #Swift #AppKit #macOS
nostr:nevent1qvzqqqqqqypzp8yls8khjhc0tma9tzfjsfrg0kz0cln2fnaxmdwk6w6sljmlltkzqyt8wumn8ghj7un9d3shjtnwdaehgu3wvfskueqprpmhxue69uhhyetvv9ujuumwdae8gtnnda3kjctvqy28wumn8ghj7un9d3shjtnyv9kh2uewd9hsqg93x3mrphesysd24u705mxx757scs27a6xt6v8fkjmq3z0fk0n3fczu08p9
Today, I'm going to be working on the functionality to insert media like images and videos into documents for my notes app.
I'll concentrate on getting images working, but maybe I can even get support for videos implemented as well before the end of today.
**I have some decisions I need to make:**
- Where should media be saved? I'm thinking of creating an `/assets` folder automatically when the user imports some media.
- How should notes reference media? Should probably be the same way as Obsidian - just use a relative path. However, it's important that the path updates automatically if the user moves the media file elsewhere.
- What to do if there's a naming conflict? Either, the app can check if the imported media is identical - in that case, there's no reason to import a duplicate so just cancel the import and let the user know, that the media has already been imported. Otherwise, I could just add "(1)", "(2)" and so on after the file name. The last option is certainly easier, so I think I'll start with that, then I can improve it later...
Hopefully I can get it working before the end of today 🤞
Exactly!
Algorithms aren’t necessarily bad.
However, the user **MUST** always be in control.
This is the problem with centralized platforms. They give no control to the user. All users on a platform like TikTok are forced to use their addictive algorithm designed to steal their attention for as much time as possible.
nostr:nevent1qqsy46zfm2nlsp6gpfkpmsvpkxfkw56dsaqg4yqxxyu28g07hfu84vczyqlhwrt96wnkf2w9edgr4cfruchvwkv26q6asdhz4qg08pm6w3djgqcyqqqqqqgpzpmhxue69uhkummnw3ezuamfdejsd9p3gq
I changed the UI a bit for my native #macOS notes app 🚀
https://video.nostr.build/f78e325c420ff6deb94cf3f1b82e960088d18abb769fd5ef7dda3637c7859f8b.mp4
Now the note documents are displayed inside what looks like a page. I personally really like the design, but it’s just an option the user can toggle. If disabled, the note shows without the page outline.
**What I have fixed:**
- Indentation now works in lists. Using tab to indent and Shift+Tab to unindent.
- Deleting list items is now possible with backspace. If the item has text, it’s transformed into a normal text block. If empty, the list item is removed.
- Collapsible headings.
- Added a translucent background color when hovering blocks.
- Improved caret navigation between blocks. It feels much more natural now because the app remembers the x-position, so the caret stays in the same place on the x-axis when moving between text blocks with the arrow keys up and down (not implemented for lists yet).
**Next task:** Implement functionality to delete blocks.
I also need to figure out how to show the text formatting options.
I see two options:
1. Either show a toolbar
2. Or show all the formatting options in a sidebar
#Swift #AppKit #dev
Also, I’m not sure how many so-called “content creators” or influencers actually want to build a following on #Nostr, knowing that if they lose their private key or if it gets compromised, they’re just shit out of luck.
There’s nothing they can do. Of course that turns people away.
nostr:nevent1qvzqqqqqqypzp8yls8khjhc0tma9tzfjsfrg0kz0cln2fnaxmdwk6w6sljmlltkzqyxhwumn8ghj7mn0wvhxcmmvqy28wumn8ghj7mn0wd68ytn00p68ytnyv4mqz9nhwden5te0wfjkccte9ehx7um5wghxyctwvsq3samnwvaz7tmjv4kxz7fwwdhx7un59eek7cmfv9kqqgxru49wmgsgwzusrch8gk7ekee3mnp3sxllcjquswept9ljyx4h6yrkt5sn
Agreed with everything except the part about keys. I do think it’s a big problem, but there’s no viable solution. Keys definitely scare people away unless they’re already from the blockchain/crypto space. And the fact that you can’t recover them if lost isn’t exactly appealing either.
But it is what it is... 🤷♂️
nostr:nevent1qvzqqqqqqypzqx78pgq53vlnzmdr8l3u38eru0n3438lnxqz0mr39wg9e5j0dfq3qyg8wumn8ghj7mn0wd68ytnhd9hx2qgdwaehxw309ahx7uewd3hkcqgkwaehxw309aex2mrp0yhxummnw3ezucnpdejqz9rhwden5te0dehhxarj9ehhsarj9ejx2asqypllpnuw26ne7s6a84hew4aluqjz8fsjtqv7n7xezw5cylvrfdvmgsx0azp
The worst part about building a native #macOS app is having to relaunch it every single time you make a change just to see if it works.
Would be nice to have a hot-reload feature like in #Flutter.
Been a beautiful day, but cold. Winter is coming.


My notes #app is slowly starting to be in a functional state.
But there's still much work to be done 😅
Tasks I need to work on for my notes app:
- The 'Files' sidebar needs to update in real time when the user is creaing folders and files outside the app. So, if the user creates a new folder inside the directory through Finder, the app should immediately update to show the new folder in the UI.
- UI of the formatting toolbar needs to be fixed. It's the toolbar with all the options like toggling bold, italic, underline, and other formatting options. I need to figure out how I want it to look and where it should be located.
- When deleting a note or folder, a confirmation popup should appear. Currently, notes or folders just get deleted immediately when clicking 'Delete' in context menu. Also, they don't get deleted to the trash where they can still be recovered. No, instead they get permanently deleted 😬
- Folders can already be renamed, but the 'Rename' option in the context menu for notes is not working. The user can currently only rename a note by opening it and then using the textfield containing the note's name (this field can be edited to change the name of the note).
- Only paragraphs and headings can be edited. The rest of the blocks can't be edited yet. I need to implement independent rich text editors for each of the block-types.
- Navigation. The back- and forward buttons don't work. They should be like in Chrome, where you can go back to the previous page. I will need a way to keep track of which pages the user has viewed and in the correct order.
- I need a local SQLite database. This will allow me to have super fast and efficient search functionality.
#dev #Swift #AppKit #macOS
But there's still much work to be done 😅
Tasks I need to work on for my notes app:
- The 'Files' sidebar needs to update in real time when the user is creaing folders and files outside the app. So, if the user creates a new folder inside the directory through Finder, the app should immediately update to show the new folder in the UI.
- UI of the formatting toolbar needs to be fixed. It's the toolbar with all the options like toggling bold, italic, underline, and other formatting options. I need to figure out how I want it to look and where it should be located.
- When deleting a note or folder, a confirmation popup should appear. Currently, notes or folders just get deleted immediately when clicking 'Delete' in context menu. Also, they don't get deleted to the trash where they can still be recovered. No, instead they get permanently deleted 😬
- Folders can already be renamed, but the 'Rename' option in the context menu for notes is not working. The user can currently only rename a note by opening it and then using the textfield containing the note's name (this field can be edited to change the name of the note).
- Only paragraphs and headings can be edited. The rest of the blocks can't be edited yet. I need to implement independent rich text editors for each of the block-types.
- Navigation. The back- and forward buttons don't work. They should be like in Chrome, where you can go back to the previous page. I will need a way to keep track of which pages the user has viewed and in the correct order.
- I need a local SQLite database. This will allow me to have super fast and efficient search functionality.
#dev #Swift #AppKit #macOS I have now implemented support for note documents based on a JSON-structure. The rendering is looking nicely and shortcuts are working - such as `CMD + B` for applying bold styling.
Still work to do with the UI. For example, I'm not really sure where the toolbar should be with all the formatting options. Maybe it should only appear when selecting some text. Then the toolbar could show above the selected text. But that approach is also a bit annoying, because the toolbar will then cover part of the text... The Craft app is using that approach and I'm not particularly a fan of it.
The word and character counters are totally incorrect. They are based on some old logic that I haven't updated yet.
Saving the note is working with shortcut `CMD + S`. I need to show a popup when trying to close a note with unsaved changes. This was actually working before, but the logic was based on an older ViewModel called `NotesViewModel`.
I'm still in the process of switching from having one massive ViewModel to multiple independent ViewModels each with their own responsibilities (some examples of my new VMs are `TabsViewModel`, `FilesViewModel`, `SearchViewModel`, etc.).
Obviuosly, it's bad practice to centralize all code in one ViewModel because it grows so massive that it becomes difficult to maintain (it breaks SRP from SOLID). The reason for me having just one ViewModel for so long, was that I really just wanted to move fast so I could quickly get something working. But when I later wanted to introduce new ViewModels, it then became much harder, because there was so much code that had to be restructured and moved into new ViewModels. So many things broke and it almost took a full day to get everything working again. Some small parts still isn't working, but that's because I just haven't prioritized those things. There are much more important things than getting character counters working again... #dev #Swift #AppKit #macOS


The big #AI companies told everyone that they could build a whole app using an #LLM.
The real software developers like myself quickly found out that it was absolutely not true, unless it’s a simple little app/website. AI is definitely useful, but it still requires you to know programming if you want to build something awesome.
Because of this, much of the hype has died off. There’s still hype, but people are getting much more realistic about what current LLMs are capable of.
I think a lot people has gotten an app idea at some point - not only developers, but also completely normal people.
The normal people were told that they could use LLMs to build their app ideas. Then they tried, and quickly figured out that it’s actually way harder and the AI still requires a lot of guidance. You can’t just write a prompt and expect the AI to give you a perfectly usable and functional app. If you don’t know how to program, you can’t read and understand the code that the AI writes, which makes it impossible to guide it in a precise way.
I suppose this is the reason for why tools like Lovable are getting less Google searches. A lot of these LLM tools were made for people who couldn’t code and now those people seem to be using AI a lot less for trying to build their app ideas. Basically, they gave up, which means many AI tools are experiencing less growth and perhaps even a decline.
https://youtu.be/tKPtZtsLgUA
#Nostr is enshittification-proof.
If a client or relay becomes enshittified, you have the freedom to move.
https://youtu.be/P1EKQidRooc
I have worked on implementing the #JSON structure for my notes app. All notes are saved as JSON files, except for the #Markdown files. Unfortunately, I have put them on a hold because I’m having too much trouble with developing the live-preview feature, so for now, I’m concentrating on JSON. I need to have a format that allows all formatting options you can think of, and defining a JSON structure allows me to build this. Markdown is too limiting, but great for really simple notes - and this is why the plan is to keep it (perhaps Asciidoc in the future as well).
I have defined all the different blocks as models. Each type of supported object is its own type of block, such as text, image, video, code-block, bullet point list, checklist, etc.
Each block can then have styling, but it depends on the specific block which styling is supported. For example, it doesn’t make sense to support bold formatting for an image, only for text-related blocks.
I have decided to basically support all formatting options. All the way from simple things like bold and italic, to text and highlight colors, alignment (left, center, and right), etc.
The rendering works, but it’s not editable yet - well, you can click options in the toolbar and that works (like clicking button to toggle bold styling), but can’t write or delete anything. Still figuring out the best way. I’m using an AttributedString and perhaps it will work well with NSTextView. First time working with AttributedString, so need to do some experimenting…
nostr:nevent1qqsyg0k0hu035pgst87e6vry50swpvg46f6nkfcalfcywuh6vcrwfdczyzwflq0d090s7hh62kyn9qjxslvyl3lx5n86dk6ad5a4pl9hl7hvyqcyqqqqqqgpz3mhxue69uhkummnw3ezummcw3ezuer9wcgp06up
There’s really nothing preventing us from having algorithms on #Nostr like what’s found on Threads, X, Reddit, and so on.
It’s completely doable from a development standpoint, but it's not free to run.
Me personally, if I were to develop a recommendation algorithm for Nostr, I’d probably start with a service that scrapes public relays and stores all notes. Storage is cheap, especially for text, so this is not a problem. The great thing about Nostr is that everything is public, so scraping data is incredibly easy.
Next, I would map user behavior by fetching all user interactions for a specific npub (the npub who wishes to use an algorithm). These interactions could be likes, replies, reposts, follows, and so on. This is to figure out what kind of content the user engages with most.
I would then assign a ranking to each note (could be from 1-10) based on how closely the note matches the user’s observed interests and relationships.
Notes from npubs they follow or interact with often would score higher. The same with notes that resemble content they’ve previously liked or replied to.
Over time, the algorithm would adapt dynamically, so it keeps learning from new interactions. LLMs have certainly made ranking easier.
**The problem:** This is the part that gets expensive (the ranking of notes), and especially when tailored to each user.
nostr:nevent1qvzqqqqqqypzqx84ftc7zrzmk734g69s7c4jjhgjx3us8j0e2uudqewgf0h3gqh0qyt8wumn8ghj7un9d3shjtnwdaehgu3wvfskueqpp4mhxue69uhkummn9ekx7mqpz3mhxue69uhhyetvv9ujuerpd46hxtnfduqzprnywvcy20zl380emtc0llueawuclnjsasfhp7yxksna2jrzr04zxn8uvg
I have implemented a fullscreen search view in my notes app.
This means, the app now has two search functions:
- Search in sidebar
- The fullscreen search
https://video.nostr.build/e3fc15c3f152785cd701847b1ba0dead2283433c57e96437b54b18b1345ef5eb.mp4
However, the button to access the fullscreen search only appears after closing the sidebar. Because otherwise, the user can use the search function directly inside the sidebar.
The UI in the fullscreen search is not finished yet. The search results should show a mini-preview of the note’s content. I also need to get the filters menu working. It can open and close, I can toggle the filters options, but the filters aren’t actually doing anything right now, as the logic has not been implemented correctly yet.
I suppose it would also be a good idea to show a close button with “ESC” text, so users realize they can click `ESC` key to unfocus the search input and close fullscreen search. #dev #Swift #AppKit #macOS
Explaining what #Nostr is and the parts that make up Nostr in a non-technical way is difficult. It’s like if you where to explain to normies what email is and what it can do. Everyone knows what email is, but no one really knows it on a technical-level except for devs. Ask a normie what SMTP is and they wouldn’t know, and it’s fine, because they shouldn’t need to know.
Likewise, people don’t need to know about NIPs. But very fundamental parts of Nostr that have direct impact on how the user interacts with Nostr are unavoidable for people to understand. Keypair is one example. People need to understand the use of the npub and nsec and how they should manage these (like never sharing the nsec to anyone).
Maybe relay, but you can use Nostr without ever worrying about relays - depends on the specific client though.
nostr:nevent1qqsrgjwcc8228wwz6rn9vlmkukq8y8gqc7dgvv4m9jkrswgj078eauqzyzusc09hr4nrg0spzpx4exkl0kc96dn98vtkq8lektht42qmueuzxqcyqqqqqqgpp4mhxue69uhkummn9ekx7mq8guxk3
Haven't tried this solution for removing menu items, but could be interesting to give a try:
https://stackoverflow.com/a/77351807
My current implementation does not actually replace the menu items, it just changes the function they execute. So I could make the 'Close' menu item do something completely different like opening a new window or something weird.
Basically, I specify which menu item I would like to manipulate, and then call the function that should be executed. #Swift #AppKit #macOS
nostr:nevent1qvzqqqqqqypzp8yls8khjhc0tma9tzfjsfrg0kz0cln2fnaxmdwk6w6sljmlltkzqy28wumn8ghj7mn0wd68ytn00p68ytnyv4mqzxrhwden5te0wfjkccte9eekummjwsh8xmmrd9skcqg5waehxw309aex2mrp0yhxgctdw4eju6t0qyt8wumn8ghj7un9d3shjtnwdaehgu3wvfskueqqyzvpy0lj5zc0mnchw2gu4wg4nqv42sknl9h4tz8gny2zf68x5frgymkcu8d