[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-77779":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":9,"htmlUrl":10,"language":11,"languages":9,"totalLinesOfCode":9,"stars":12,"forks":13,"watchers":14,"openIssues":15,"contributorsCount":9,"subscribersCount":16,"size":16,"stars1d":17,"stars7d":18,"stars30d":19,"stars90d":16,"forks30d":16,"starsTrendScore":20,"compositeScore":21,"rankGlobal":9,"rankLanguage":9,"license":9,"archived":22,"fork":22,"defaultBranch":23,"hasWiki":22,"hasPages":22,"topics":9,"createdAt":9,"pushedAt":9,"updatedAt":24,"readmeContent":25,"aiSummary":26,"trendingCount":16,"starSnapshotCount":16,"syncStatus":27,"lastSyncTime":28,"discoverSource":29},77779,"files.md","zakirullin\u002Ffiles.md","zakirullin","🌱 Private, quiet space for thinking. Simple app for .md files. LLM-friendly.",null,"https:\u002F\u002Fgithub.com\u002Fzakirullin\u002Ffiles.md","Go",3463,163,17,6,0,150,382,723,450,105.64,false,"main","2026-06-12 04:01:22","\u003Cimg src=\"https:\u002F\u002Fgithub.com\u002Fzakirullin\u002Ffiles.md\u002Fraw\u002Fmain\u002Fweb\u002Fimg\u002Ficon.png\" alt=\"Files.md icon\" title=\"Files.md\" align=\"right\" height=\"76\" \u002F>\n\n# Files.md  \nPrivate, quiet space for thinking. A simple app for your `.md` files.  \n\n\u003Cimg src=\"https:\u002F\u002Fgithub.com\u002Fzakirullin\u002Ffiles.md\u002Fraw\u002Fmain\u002Fweb\u002Fimg\u002Fappdark.png\" alt=\"Files.md screenshot\" title=\"Files.md\"\u002F>\n\nYou can store your whole life:\n- 📌 Notes\n- 📝 Documents, Projects\n- 💚 Journal, Habits\n- ✅ Checklists, Tasks\n\nAll in plain `.md` files, local-first. LLM-friendly. **Private - no data is sent to server**.    \n\n> Own your data as plain local files.  \n> Own the software that opens those files.  \n> Grow your knowledge with files and your own brain.  \n> Grow the software around it with an LLM.  \n> **Plain files and self-owned software can last through the ages**.  \n \nTry it out: [app.files.md](https:\u002F\u002Fapp.files.md) (Beta). Main site: [files.md](https:\u002F\u002Ffiles.md).\n\n**I have been building this project for 5 years**. Consider **[supporting on GitHub 💚](https:\u002F\u002Fgithub.com\u002Fsponsors\u002Fzakirullin)**.  \n\nYou can use this app for Zettelkasten, Second Brain, Notes taking, Journaling, To Do lists, Checklists, and more.  \n\n[Dump your thoughts](#dump-your-thoughts) · [How to think deeply](#how-to-think-deeply) · [Second Brain?](#second-brain) · [Journal, tasks and checklists](#journal) · [Files structure](#files-structure)\n\n## Another note taking app? \nMaybe. But this time:\n- Only necessary features, **restrictions foster creativity**\n- **Chat-like flow for lazy thought capture**\n- Free and open source\n- No need to install anything, all you need is a browser\n- Works offline\n- **Local-first, files don't leave your device**\n- Extremely simple code. **One person or an LLM can fit the whole project in head**\n- The codebase is ready for your LLM to extend to your needs\n- Portable, no build systems, just open `web\u002Findex.html` \n- Optional out of the box synchronization \n- The server is just one binary (or use iCloud\u002FDropbox\u002FGoogle Drive for sync)\n- Telegram chatbot for on-the-go access to your files\n\n## How to use\n- Open [app.files.md](https:\u002F\u002Fapp.files.md) (Chrome is recommended)\n- Click \"Install files.md\" on the right side of the address bar:\n\u003Cdiv align=\"center\">\n    \u003Cimg src=\"https:\u002F\u002Fgithub.com\u002Fzakirullin\u002Ffiles.md\u002Fraw\u002Fmain\u002Fweb\u002Fimg\u002Finstall.png\" alt=\"Install\" title=\"Install\" width=\"350\"\u002F>\n\u003C\u002Fdiv>  \n\n- Open a local folder to persist changes  \n- Occasionally hit force-refresh (`Cmd+Shift+R`) to get new updates.  \n\n## Dump your thoughts\nYou can use chat to quickly dump your thoughts.  \n\n\u003Cdiv align=\"center\">\n    \u003Cimg src=\"https:\u002F\u002Fgithub.com\u002Fzakirullin\u002Ffiles.md\u002Fraw\u002Fmain\u002Fweb\u002Fimg\u002Fapp.png\" alt=\"Files.md screenshot\" title=\"Files.md\" width=\"700\"\u002F>\n\u003C\u002Fdiv>\n\nOpen the chat and send a message:\n\u003Cdiv align=\"center\">\n    \u003Cimg src=\"https:\u002F\u002Fgithub.com\u002Fzakirullin\u002Ffiles.md\u002Fraw\u002Fmain\u002Fweb\u002Fimg\u002Fnote1.png\"  width=\"350\"\u002F>\n\u003C\u002Fdiv>\n\nChoose where to save (can do later):\n\u003Cdiv align=\"center\">\n    \u003Cimg src=\"https:\u002F\u002Fgithub.com\u002Fzakirullin\u002Ffiles.md\u002Fraw\u002Fmain\u002Fweb\u002Fimg\u002Fnote2.png\" width=\"350\"\u002F>\n\u003C\u002Fdiv>\n\n**With this flow you can quickly save notes, tasks, journal records and checklists**.  \n\n## Save things in the chatbot\nOpen the chat, write something and press `Enter`:\n\n\u003Cdiv align=\"center\">\n    \u003Cimg src=\"https:\u002F\u002Fgithub.com\u002Fzakirullin\u002Ffiles.md\u002Fraw\u002Fmain\u002Fweb\u002Fimg\u002Fbot.gif\" alt=\"Saving things in bot\" title=\"Saving things in bot\" width=\"350\"\u002F>\n\u003C\u002Fdiv>\n\nThat's it.\n\n[Telegram Bot](https:\u002F\u002Ft.me\u002FFilesMDBot). **Other messengers will follow**.\n\n## How to think deeply \n**Connect ideas. Let them compound. Think through.**\n\n1) I used [app.files.md](https:\u002F\u002Fapp.files.md) to grow my knowledge about brain and software development\n2) I added new notes to either `brain` or `dev` folders. One idea per note\n3) I made connections between the relevant notes in the web app (type `[`)  \n4) Everything is connected, just as in our brain\n5) **I spent time travelling through the notes and thinking it through**\n6) At one point, some `brain` and `dev` notes appeared very related\n7) This connection between two different domains produced an insight\n8) I wrote an article based on that insight: [Cognitive Load in Software Development](https:\u002F\u002Fgithub.com\u002Fzakirullin\u002Fcognitive-load)\n\nAll this activity helped me to:\n- **Think deeply** (which is very important in the AI-age)\n- **Think systematically and see the bigger picture**\n- **Write insightful texts**\n\nTo achieve all that, **you'll have to use your brain**, not advanced templates or AI workflows.\n\n- Start with no structure at all, 0 folders\n- One idea per note\n- Every note should be understood without context\n- Apply new knowledge immediately, don't save it for future self\n- Link related notes\n- **Revisit your notes and think through**\n\nMy friends and I have been using this simple setup for five years, and it works well.\n\n## Second Brain?\nI'll quote [I Deleted My Second Brain](https:\u002F\u002Fwww.joanwestenberg.com\u002Fi-deleted-my-second-brain-692aa40d59d5f06dd5131e43\u002F):\n\n> Obsidian is a brilliant piece of software. I love it, dearly. But like anything, without restraint, it can also be a trap. Markdown files in nested folders. Plugins that track your productivity. Graph views that suggest omniscience. There’s an illusion of mastery in watching your notes web into constellations. But constellations are projections. They tell stories. They do not guarantee understanding.\n>\n> When I first started using PKM tools, I believed I was solving a problem of forgetting. Later, I believed I was solving a problem of integration.\n> \n> **Eventually, I realized I had created a new problem: deferral. The more my system grew, the more I deferred the work of thought to some future self who would sort, tag, distill, and extract the gold.**\n> \n> **That self never arrived.**\n\nThe Second Brain is thrilling.  \nAdvanced guru templates, plugins and AI workflows...  \nOne wants to scrape the wisdom of the whole internet.  \nThere's some beauty in this neat system. Every new note brings dopamine.  \n\n*Second Brain is getting better*.    \n**But the first brain is not improving**.  \nAnd that’s an issue.  \nIn the AI age, your first brain is as valuable as ever.  \n\nUse **your brain** to think through the notes.  \nThe tool is not important, your thinking is.  \n\nBefore adding a new note, try to answer these questions:  \n- How this new knowledge can sharpen my judgment or expand my taxonomy?  \n- How can I see the world differently, given this new knowledge?  \n- How can I act differently?\n\n## Notes can prevent experience\n- Reading and taking notes can easily fool us into believing that we understand a text\n- We think we understand, but in reality **we just know**  \n- **At some point our \"knowing\" is so good, that we start feeling that we actually do it (or at least tried)**\n\nThe worst thing is that **we don’t let new experiences emerge because we already have knowledge**. It's a knowledge barrier. Life gives us opportunities to live through new experiences, but we refuse, because \"we already know\".\n\n## Self-help through reading and taking notes?\n**Harm caused at the emotional level must be healed at the emotional level.**\n\nNot through intellectual work and taking notes.  \nReading without action is entertainment. A form of procrastination.  \n**No amount of self-help books can heal emotional wounds.**  \nWhat can help is psychotherapy, rescripting and chair work. Meditation.  \n**Healing happens by feeling.**\n\n## When to take notes\nIf your goal is to:\n- Develop a deeper, more structured understanding of something\n- Do research\n- Write an article or a book\n\nThen taking notes is perfectly fine.\n\n## Journal\nAre you feeling good about something? Send a message.\n\n\u003Cdiv align=\"center\">\n    \u003Cimg src=\"https:\u002F\u002Fgithub.com\u002Fzakirullin\u002Ffiles.md\u002Fraw\u002Fmain\u002Fweb\u002Fimg\u002Fjournal.png\" title=\"Journaling\" width=\"350\"\u002F>\n\u003C\u002Fdiv>\n\nThen click \"To Journal\".  \nOr just add ` jj` or ` жж` at the end of your message.\n\nYour record is gonna be saved nicely in the `journal\u002FYYYY.MM Month.md` file.\n\n## Tasks ✅\nYou're in the flow.  \nA colleague asks you to send a report.  \nHolding that in your head drains energy.  \nDrop a message and stay in flow.\n\n\u003Cdiv align=\"center\">\n    \u003Cimg src=\"https:\u002F\u002Fgithub.com\u002Fzakirullin\u002Ffiles.md\u002Fraw\u002Fmain\u002Fweb\u002Fimg\u002Ftask.png\" title=\"Adding a task\" width=\"350\"\u002F>\n\u003C\u002Fdiv>\n\nAdd only small, actionable items. Not things like \"Plan a vacation\".  \nYour task should be the first, small step of what should be done anyway.  \n**Not what you wish to be done, but don't have motivation just yet.**   \nYour task list should not create a feeling of guilt.  \n\nFor tasks that should be done later press \"To Later\".  \n\n## Checklists 🛒\nA friend recommends a book to you.  \nYou're out of butter and buns.  \nHolding things like this in your head is taxing.   \n\nDrop all that to chat, then move to a matching checklist.  \n\n## How to sync\n| Setup                                                 | Where your files live                 | Sync across devices | Server needed      | Best for                                             |\n|-------------------------------------------------------|---------------------------------------|-------------------|--------------------|------------------------------------------------------|\n| **Local-first**, `app.files.md` doesn't send any data  | A folder on your device               | No                | None               | Maximum privacy, your data doesn't leave your device |\n| **Cloud-folder sync** (iCloud\u002FDropbox\u002FGoogle Drive)   | Your existing cloud folder | Yes               | None (cloud provider) | Sync across devices without running a server         |\n| **[Self-hosted sync server](docs\u002Fyour-own-server.md)** | Your own (or local) server | Yes               | One Go binary      | Sync between devices inside your network             |\n| **Hosted sync server**                                | Our managed server         | Yes               | `api.files.md`      | Try it instantly, no setup                           |\n\n## Files structure\nYou don't have to think about the structure, it is predefined.  \nAlthough, you're free to use whatever structure you want.  \n\n- Chat: `Chat.md`\n- Notes: `brain\u002FNote.md`, `\u003Ccategory>\u002F*.md`\n- Checklists: `Read.md`, `Watch.md`, `Shop.md`, `MyChecklist_.md`\n- Journal: `journal\u002F2024.08 August.md`\n- Tasks: `Later.md`\n- Habits: `habits\u002FAte consciously.md`, `habits\u002F*.md`\n- Images: `media\u002F*` (png, jpg, webp, gif)\n- Archive: `archive\u002F*.md`\n- Config: `config.json`\n\nScheme is also available at [files.md\u002Fllms.txt](https:\u002F\u002Ffiles.md\u002Fllms.txt).  \nYou can copy-paste it into `CLAUDE.md` or `AGENTS.md`, so that your AI agent would understand the structure.  \n\n## Hotkeys\n\n| Hotkey                                 | Action                       |\n|----------------------------------------|------------------------------|\n| `[`                                    | Insert a link to a file      |  \n| `Cmd+K` \u002F `Ctrl+K`                     | Open file search modal       |\n| `Cmd+N` \u002F `Ctrl+N`                     | New file                     |\n| `Cmd+M` \u002F `Ctrl+M`                     | Move file                    |\n| `Cmd+D` \u002F `Ctrl+D`                     | Delete file                  |\n| `Cmd+Enter` \u002F `Ctrl+Enter`             | Open chat                    |\n| `Cmd+Shift+Enter` \u002F `Ctrl+Shift+Enter` | Toggle chat dialog           |\n| `Cmd+[` \u002F `Ctrl+[`                     | Go to previous file          |\n| `Cmd+]` \u002F `Ctrl+]`                     | Go to next file              |\n| `Cmd+~` \u002F `Ctrl+~`                     | Toggle sidebar               |\n| `Cmd+B` \u002F `Ctrl+B`                     | Toggle **bold**              |\n| `Cmd+I` \u002F `Ctrl+I`                     | Toggle *italic*              |\n| `Cmd+Y` \u002F `Ctrl+Y`                     | Insert checkbox              |\n| `Cmd\u002FCtrl` + `Click`                   | Copy inline text \u002F open link |\n| `Ctrl+Cmd+Space`                       | Insert emoji (macOS)         |\n\n## Useful scripts for your files\nAll scripts are in `cmd` and can be run **inside your files directory**. Install [Go](https:\u002F\u002Fgo.dev\u002Fdoc\u002Finstall) first.\n\n### Add Whoop metrics to journal\n```\ngo run \u002Fabs\u002Fpath\u002Fto\u002Ffiles.md\u002Fcmd\u002Fwhoop\u002Fwhoop.go\n```\n\n### Convert wikilinks to markdown links\nConvert `[[wikilinks]]` to standard `[Name](\u002Fpath.md)` (`--dry-run` available):\n```\ngo run \u002Fabs\u002Fpath\u002Fto\u002Ffiles.md\u002Fcmd\u002Ftomdlinks\u002Ftomdlinks.go .\n```\n\n### Insert backlinks\nAdds links back to referencing files (`--dry-run` available):\n```\ngo run \u002Fabs\u002Fpath\u002Fto\u002Ffiles.md\u002Fcmd\u002Fbacklink\u002Fbacklink.go\n```\n\n### Shift journal timestamps\nShift timestamps in journal files by N hours (useful after timezone change):\n```\ngo run \u002Fabs\u002Fpath\u002Fto\u002Ffiles.md\u002Fcmd\u002Fshifttime\u002Fshifttime.go\n```\n\n## Documentation\n[Deploy on your own server](docs\u002Fyour-own-server.md)    \n[Chatbot](docs\u002Fbot.md)    \n[Sync flow](docs\u002Fsync-flow.md)  \n[Integration tests](docs\u002Fintegration-tests.md)  \n\n## Repository structure\n- `web` - web app (PWA), `index.html` is an entrypoint\n- `web\u002Flib` - frontend libs\n- `cmd\u002Fserver` - entrypoint for server\n- `cmd\u002F*\u002F` - useful scripts for `.md` files\n- `server\u002Fbot.go` - bot\n- `server\u002Fsync\u002F` - sync API server code\n- `vendor` - backend libs\n- `tests` - E2E tests, test both the web app and the server\n\n## How to contribute\n- Junior developers should be able to understand the code\n- Ideally, every PR should remove or simplify code, not add it\n- **The less code we have, the more flexible we are**\n- All dependencies are our code and responsibility. So, avoid dependencies if possible\n- Code should be self-sufficient, so `vendor` and `web\u002Flib` folders are included in the repository\n- **Do we really need this feature? Will it help us to do the real job, or does it just give dopamine?**\n\nRefer to [this guide](https:\u002F\u002Fgithub.com\u002Fzakirullin\u002Fcognitive-load) for more comprehensive rules.\n\n## Backend guidelines\n- We write **tests**\n- We don't use get* prefix for methods\n- No panics, errors are part of business logic\n- If we are ignoring an error - we leave a WHY comment\n- We wrap errors all the time, we should add method's context\n- No iterators for client code\n- We prefer real implementations or at least fakes over mocks and stubs\n- Imports should only be renamed to avoid a name collision with other imports\n- **With portability in mind, everything is stored in plain `.md` files**\n\n## Frontend guidelines\n- Use `PATCHED` keyword if you modify libs in-place\n- **It would be fantastic if, one day, we replaced `CodeMirror` with our own tiny implementation**\n- No build systems, **in 10 years we will open `\u002Fweb\u002Findex.html` and it should just work**\n- Don't forget that awaits between lock check and lock acquire can cause race condition\n- Avoid flaky e2e tests. First we get negative emotions, then we stop running all the tests\n- Most bugs are caused due to race conditions, when an async flow is interrupted mid through\n\n## Glossary\n- `filename` - a filename with extension, like \"note.md\" (USE THIS AS ID)\n- `header` - an extension-stripped and capitalized filename, like \"Note\"\n- `body` - file's content \n- `dir` - a dir that is meant to store notes under some category, like \"happiness\"\n- `userID` - chatID. For the most part we're only using chatID as userID (PM with the bot)\n- `ctime` for file - data blocks or metadata change time: file's ownership, location, file type and permission settings changed time. Parent folder renaming won't affect, moving the file does affect, renaming the file does affect. We need this to track file's location changes, like to understand when it was moved to archive, to track task's angry level etc\n- `mtime` for file - mtime (modification time) for a file refers to the time when the contents of the file were last modified. Unlike ctime, it is not affected by changes to the file's metadata, such as ownership, permissions, or renaming. We rely on that for synchronization.\n- `ctime` for dir - adding or removing files or subdirectories (similar to `mtime` plus inode changes like renaming files)\n\nAny file can be uniquely identified by filename and dir. We only support one level of nesting.\n\n## Performance\nThe project is blazing fast :) If you're afraid of using files or mutexes unnecessarily for performance reasons, take a look at this:  \n```\nMutex lock\u002Funlock = 25 ns\nRead 4K randomly from SSD = 150,000 ns\n1 ms = 1,000,000 ns\n```\n\n## ADRs (Architecture Decision Records)\n- `20.05.2026` Added LaTeX support, even though I wasn't happy about +20 font files. LaTeX is text-based and LLM-friendly. Text + Math will cover pretty much everything. \n- `06.05.2026` Moved from Today.md to Chat.md. CustDev showed that users have trouble grasping \"today\" concept. And besides, \"open chat\" phrase has meaning in both bot and webapp.\n- `02.05.2026` Now hide-token runs synchronously on every change, previously it had 100ms debounce which caused jitter on by word removals in links and formatted texts.\n- `02.05.2026` Merged Inbox.md and Today.md to Today.md. Inbox name is too abstract, productivity-related and GTD-ish. I want calmness and simplicity. Today is like \"the page I live in chat\".\n- `23.04.2026` Moved from API_HOST, APP_HOST to API_URL, APP_URL. For different environments it's better to provide more information like desired schema in configuration.\n- `22.04.2026` Inbox entries in the bot are now identified by a stable content hash (`fs.Hash` of the block with the `- [ ] `\u002F`- [x] ` marker stripped) instead of a positional index, so a button keeps pointing at the right line even if other entries are added\u002Fremoved\u002Fcompleted in between.\n- `22.04.2026` It was mentally taxing to see two buttons\u002Fmessages \"to inbox\" and \"to chat\", it was not as mentally easy just to drop a task for chat. Because it went to inbox, and 1 more click needed. That one click was the reason adding new tasks became frustrating. I let go of two different flow, and now everything goes to inbox, and every item is inbox is a markdown checklist item. As a bonus, PWA app is now very handy as it shows tasks for chat by default. Also, maybe \"inbox\" is a mentally overloaded term, and \"chat\" sounds better. Will see.\n- `11.04.2026` Even though I want to store links as plain markdown links, visually I want to work with them as if they were minimal [links]. For that I decided to hide (...) part when cursor is on the line. The (...) part is only hidden for markdown-files link.\n- `11.04.2026` Brought back standard Markdown Links. I want the knowledge base to be cross-platform. It should work in GitHub.\n- `05.04.2026` Tried to move web\u002F* stuff in the root folder for simplicity. Bad decision - there should be an explicit dir which we can use as public DOCROOT on our server.\n- `21.09.2025` Switched to [link] for links. The `[link](full%20path)` syntax is too overwhelming and clunky, plus we don't want to deal with path changes.\n- `21.09.2025` Removed WASM. I had a bug when a message was removed from Inbox.txt, and was not added to a file (I pressed \"move to file\" button). I wasn't able to reproduce the issue, but what I found is a lot of complexity. JS -> Go (writeFile) -> Go awaiting a promise from JS -> JS Golang runtime somewhere in between -> JS (writeFile) -> Go (returning from promise) -> Sending results back to JS. And it has to be done in a separate goroutine, because both WASM and JS are running in the same thread. Also, Golang's WASM is still experimental. We have too many components and a lot of uncertainty involved. I didn't want to implement same functionality in JS back then, at the solution served for some time. Now it's time to reimplement the functionality in JS and give up all this complexity. Also, inbox.wasm is ~8MB and I wanted the application to be really small.  \n- `11.07.2025` Decided to use OPFS as an initial driver for file system. Better browsers support, less hustle for users. The app starts with OPFS driver by default, if needed, user can replace the driver with Local FileSystem API by opening a local dir. DirHandle would be saved to IndexedDB in such scenario and reused every time.\n- `08.07.2025` Root folder is now '\u002F', not ''. All files in webapp are identified by path, not by 'dir' + 'filename', restricting to 1 level of nesting.\n- `08.07.2025` Dropbox is changing some metadata for newly created files, thus ctime is changed. I was thinking about moving to mtime for sync, but that wouldn't allow us detect renames (though, we detect them through a separate mechanism anyway), so mtime can be more reliable. Also sync won't be triggered by permission\u002Fownership change etc. Migrated to mtime. Mtime is used for content-based sync, ctime is used for append-only sync log (renames\u002Fdel). Also we can restore mtime from .git\u002Farchive, unlike ctime.\n- `30.06.2025` Decided to migrate every flow to Chat.md, even todo lists. Added - we can't work with multiline tasks with this flow, we may want support both files and indices. We have two ways of doing so - encode params in a uniform way, and use same command handlers with IFs. Or we can use different command handlers to handle chat\u002Ffile movements. I decided to go for different command handlers. Added, if we go for different commands - move to buttons config would be complicated. Added, maybe we can move files back to Chat.md on \"file move\", and reuse the existing flow? Added, so far seems good. Our chat.md log acts as an append-only log. As a bonus, if we don't finish some flow (like schedule\u002Fmove), the content would be saved in log and we can continue scheduling\u002Fmoving from the app.\n- `29.06.2025` All incoming messages go to Chat.md now by default. Before that they got moved to `\u002Ftoday` (and become tasks), which was good for a simple todo list, but not as convenient for other use cases. I realized that during meetings, all I needed was a simple input field where I can dump whatever stuff from my head with no further immediate action. With a possibility to review and organize it later. It can be tasks, it can be journal records, or it can be files. Also, it's better to have a really simple easy to understand default flow - we dump all the messages into one file, and that's it.  \n- `27.06.2025` Default mode for chat is \"One big file\" now, i.e. the only thing it does is dumps all the messages into one file. Again, let's start with the simplest flow, not to overwhelm users. Added later. If we choose full mode, we'll have to create dirs upfront so that \"to habits\", \"to read\u002Fshop\" etc. would work. If users don't need it, he removes the dirs, and we don't recreate them (as we would do in \"on-the-fly mode\"). So, we can't use on-the-fly strategy everywhere.\n- `26.06.2025` Before we created all necessary dirs upfront, now we create dirs on the fly. That way we won't clutter user's knowledge base right from the start.\n- `24.06.2025` Switched to microseconds for tracking file changes during sync. Gap between consecutive files creation is more than enough - ranging from 5000μs to 1000μs. We didn't go for nanosec because js is having troubles with int64 precision. Added later. Linux is using cached kernel time, which is updated at `CONFIG_HZ` interval (`grep CONFIG_HZ \u002Fboot\u002Fconfig-$(uname -r)`), in my case the value is 1000 (1ms). Most real-world operations operations are spaced much further apart than 1ms due to: user interaction, network latency, disk i\u002Fo. We might only have issue if we update files inside an effective\u002Fnative loop. \n- `16.06.2025` I believe it's time to make our knowledge base cross-platform, by forbidding characters like \":?\u003C>*\" in filenames. These characters aren't allowed in some environments (like Windows, PWA).\n- `14.06.2025` I wanted bot-like functionality in browser. I didn't want to re-write well-tested code in TypeScript, so I used wasm~~. And it worked perfectly good.\n- `12.06.2025` We use Telegram bot as distract-free write-only entrance to our knowledge base. The only issue is, it is not as wildly popular in EU\u002FUSA. I've come to the idea that we can transform app.files.md to a chat once we decrease the window size! Would be default behaviour on mobiles.\n- `04.06.2025` Introduced append-only log for syncing. Stateless sync is tricky to implement - we would have to send all files in every request. Since we're only renaming on server - we'll only track renames.\n- `04.06.2025` For content-only sync (no renames\u002Fdeletes) we don't store any state on server, we compare hashes & last ctimes \n- `11.11.2024` Removed Wikilinks support. Only plain Markdown links, our knowledge base must be interoperable.\n- `26.10.2024` Updates are now processed sequentially on per-user basis. Because there were some race conditions on concurrent file writings. Also we faced out-of-order forwarded messages processing, and it was impossible to collapse them to one message.\n- `06.10.2024` **Removed fyne.io**. At first, I wanted a lightweight alternative to Electron, and fyne.io seemed to be an ideal candidate. After a few days working with it 80% of bot functionality was implemented, and I was pretty happy with it. The thing is, to implement the rest of the functionality, we would have to apply A TREMENDOUS amount of effort. I am talking tiny details such as scrolling, emojis rendering, text selecting behaviour, links support, etc. And in future we would have to implement image uploading and markdown\u002Fhtml renderer, which would be also painful in such non-webview based toolkit. As much as I hate using the web stack for the desktop applications, it doesn't seem like we have a choice. Let's try wails.io.\n- `09.09.2024` We use vendoring for dependencies. We want all our few dependencies to be in the repo, so we don't care about blocked\u002Fremoved dependencies. Our repository is the self-sufficient source of truth.\n- `01.09.2025` We use granular locks (in db, journal, userconfig) instead of one global per user lock so to avoid bottlenecks. Workers might use 3rd party API like ChatGPT, and we don't want to hold user's lock all that time. **PATCHED**, we added sequential per-user updates processing, `bot` can't cause RCs on its own, but `bot` & `worker` can, so we should continue using granular locks.\n- `20.08.2024` We read every userconfig value from the config file on every access. We don't need load\u002Fsave whole config before\u002Fafter `bot.Answer()` method. We have to reread it every time we need to change it, so we don't write back any stale data. Let's imagine we load config only once before `bot.Answer()`, next, we may have significant networking delays in `bot.Answer()` (let's say 2 seconds when making external requests), there are good changes that during those 2 seconds `worker.MoveDueTasks()` will modify `userconfig.Schedule`, causing data race (after bot's answer we write back stale data). And we don't want our schedule lost.\n- `08.08.2024` Sanitize Early, we gave up sanitizing in Path method. That's an unexpected behaviour - it breaks paths. We should sanitize everything as soon as we received. Most commands work with md5 hashes, for such cases no sanitize is needed\n- `13.07.2024` `gofumpt` for stricter formatting. `gofumpt` is happy with a subset of the formats that gofmt is happy with. The less we have to choose between different formating options, the better\n- `13.07.2024` FS's structure should have userFS name, to reflect the fact it user user-namespaced\n- `09.07.2024` Note term is way too vague. Let's try to use \"file\" term, without any high level abstraction (like note) \n- `08.07.2024` Gave up on AST parsing\u002Frendering. We had lots of corner cases via AST and the code was way complex. Markdown isn't that hard to parse, we can do it via good old straigforward code. We have 3x times less code now, and it is far less mentally taxing to understand. We did the same for MD->HTML conversion. Telegram doesn't support whole range of HTML tags, so it was easier to write our own md-to-html converter.\n- `08.07.2024` Adherence to Tolerant Reader principles. If enconunter gibberish during parsing - we skip it, but if we encounter flags of valid data (let's say `###`) but data itself is invalid - we panic. TODO preserve gibberish during read-write cycle.\n- `07.07.2024` Usage of https:\u002F\u002Fgithub.com\u002Frivo\u002Funiseg. In Go, strings are read-only slices of bytes. They can be turned into Unicode code points using the for loop or by casting: []rune(str). However, multiple code points may be combined into one user-perceived character or what the Unicode specification calls \"grapheme cluster\". For example, white circle \"⚪\" has two runes, but one grapheme cluster.\n- `13.06.2024` Markdown to HTML conversion. User can have invalid Markdown in his notes, and TG API would fail to send invalid Markdown directly. So, first we escape HTML, then we convert user's Markdown to HTML and finally send it via Telegram API as HTML.\n- `13.06.2023` File hashing. Everywhere where we have user input - we should use fs.hash, otherwise we get long filenames, and tg returns `INVALID_DATA` error (callbackData max 64 bytes)\n- `13.06.2023` Introduced `db.go`. We had to abstract away Redis anyway (otherwise it's hard to write tests)\n- `13.03.2023` Package db.go doesn't store userID (we often use it separately...) Do we? Maybe we gonna use it without userID (like global bot stats?). Added: moved userID to class. Maybe in later we'll need this class outside of user's scope, but let's stay in the future :)\n- `13.06.2023` We can't ucfist filename in fs.Put - what if that was user-created file (outside the bot), i.e. it comes with lowercase\n\n","Files.md 是一个简洁的应用程序，用于管理和编辑你的Markdown文件。它采用Go语言开发，核心功能包括支持笔记、文档、项目、日记、习惯记录、检查清单和任务管理等功能，并且所有数据都以纯文本形式存储在本地，确保了隐私安全。该应用强调极简设计，仅提供必要的功能，通过限制来激发创造力，并具有类似聊天的界面以便于快速记录想法。此外，Files.md 支持离线使用，无需安装额外软件，非常适合需要私密空间进行思考、整理个人知识体系或日常事务管理的用户。对于追求数据自主权及偏好开源解决方案的人来说，这是一个理想的选择。",2,"2026-06-11 03:55:58","trending"]