{"id":13590430,"url":"https://github.com/lelanthran/frame","last_synced_at":"2025-04-08T13:31:08.465Z","repository":{"id":162277865,"uuid":"623100655","full_name":"lelanthran/frame","owner":"lelanthran","description":"Personal DB to help maintain focus on long-sequences of tasks.","archived":false,"fork":false,"pushed_at":"2024-08-11T06:26:35.000Z","size":624,"stargazers_count":123,"open_issues_count":2,"forks_count":2,"subscribers_count":6,"default_branch":"master","last_synced_at":"2024-11-06T10:45:31.009Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/lelanthran.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-04-03T17:39:25.000Z","updated_at":"2024-11-03T07:52:14.000Z","dependencies_parsed_at":"2023-12-23T21:49:49.185Z","dependency_job_id":"7d686f85-2ba4-4734-ac5d-a0e18b4dfe57","html_url":"https://github.com/lelanthran/frame","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lelanthran%2Fframe","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lelanthran%2Fframe/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lelanthran%2Fframe/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lelanthran%2Fframe/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/lelanthran","download_url":"https://codeload.github.com/lelanthran/frame/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247851494,"owners_count":21006768,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-01T16:00:45.305Z","updated_at":"2025-04-08T13:31:04.052Z","avatar_url":"https://github.com/lelanthran.png","language":"C","readme":"# FRAME\n\nEstimated time to read this document: 10m\n\nThis is a small utility that I can use from the command-line to keep\ntrack of where in the bigger picture my current efforts fit.\n\n## Problem\n\nToo often I find myself completing some small and intricate part of my\nhobby/personal project, and realise that I don't remember where I stopped\nlast in the project, which I last looked at as an overview two weeks ago.\n\nI deep-dive into something tiny and complex that takes all of my\nattention, and once that is done and integrated, I'm not too sure what\nI had planned next.\n\n## Solution\n\nI store context as a tree of nodes. I call each node in this tree a `frame`\n(as in *frame of context*, *frame of mind*, or *framing the problem*, etc). At any\ngiven time only a single frame can be active. To see all the notes I made\nwhile working during that frame of context I use `frame status`.\n\nWhen I start a task I first do `push frame 'title of task name here'` and\nenter a short message, and that new frame is the current frame. When I am done I\ndo `frame pop` and the current frame switches to the parent of the popped\nframe. The popped frame is discarded.\n\nAll frames descend from an initial frame called `root`[^1].\n\n\n## Setup for terminal usage\nThe first thing to do is to integrate the frame name into the shell's\n`PS1` variable. The command `frame current` prints the name of the current\nframe and the date it was changed, with the two fields separated by a colon.\n\n```sh\n$ frame current\nroot/projects/frame: Thu May 11 22:26:29 2023\n```\n\nI have the following function which is called within my `PS1` variable:\n\n```sh\nframe_ps1() {\n   frame current --quiet | cut -f 1 -d :\n}\n```\n\nCalling `frame_ps1` within the `PS1` variable prints out the current frame\nafter each command, in every terminal I am logged into[^2].\n\n```sh\n[root/projects/frame]$ echo \"Hello World\"\nHello World\n[root/projects/frame]$ \n```\n\n## Example Usage\nContinuing with the frame `root/projects/frame`, in this case I\nwant to create a GUI browser for this project:\n\n```sh\n[root/projects/frame]$ frame push 'create GUI' --message=\"\nCreate a GUI for frame browsing\n\"\nFrame 0.1.2, (© 2023 Lelanthran Manickum)\nCreated new frame [root/projects/frame/create GUI]\n[root/projects/frame/create GUI]$ \n```\n\n\u003e Had I neglected to provide a `--message` parameter then $EDITOR is opened to\n\u003e allow me to enter a message, similar to how `git commit` works when no message\n\u003e is specified. Each frame can store a single note of unlimited size; the\n\u003e `--message` argument provides the notes when creating a new frame.\n\nThe current frame's name is `create GUI`. It has a parent, `projects`, which\nitself has the parent `root`.\n\n```mermaid\ngraph TD\n   A[root] --\u003e B[projects];\n   B --\u003e C[frame];\n   C --\u003e D[create GUI];\n```\nSome useful commands at this point:\n\n1. `frame status` returns the notes (message) for this frame.\n2. `frame edit` opens the editor to allow editing the notes for this frame.\n\nI have some idea of what needs to be done, namely, startup a new Lazarus\nproject, create the GUI elements, include the `libframe.so` library, ensure\nthat the build compiles, etc.\n\n```sh\n[root/projects/frame/create GUI]$ frame append --message=\"\n1. Create new lazarus project\n2. Add `libframe.so` to the lazarus project\n3. Create GUI elements\n\"\nFrame 0.1.2, (© 2023 Lelanthran Manickum)\nroot/frame/create GUI: Sat May 13 08:20:32 2023\n```\n\nIt takes a few minutes to create the Lazarus project, add the `libframe.so`\nfile and ensure that the linker is finding and accepting the library.\n\nMy next task is to create the GUI elements. This is much more involved, and\nso I create a new frame for it:\n\n```sh\n[root/projects/frame]$ frame push 'GUI Elements' --message=\"\nNeeds to display:\n1. The history, with a filter\n2. A treeview of all frames from root, with the current frame selected and\nexpanded.\n3. An editor box that displays the notes for the current frame, and allows the\nuser to edit those notes.\n\"\nFrame 0.1.2, (© 2023 Lelanthran Manickum)\nCreated new frame [root/frame/create GUI/GUI Elements]\n[root/projects/frame/create GUI/GUI Elements]$ \n```\n\nI do the form design (ridiculously easy in Lazarus, takes about 10m). I\nattempt to populate the `history` GUI element, and realise I forgot to declare\nthe `C` functions in the library file. My Lazarus code cannot call undeclared\nfunctions even if the library is linked correctly.\n\n```sh\n[root/projects/frame/create GUI/GUI Elements]$ frame push 'decls needed\" --message='\nDeclare all the `C` functions in `libframe.so`.\n'\nFrame 0.1.2, (© 2023 Lelanthran Manickum)\nCreated new frame [root/frame/create GUI/GUI Elements/decls needed]\n```\n\nTo make things easier, I paste the `C` header file into ChatGPT and ask for\nthe Lazarus definitions. It provides the definitions, helpfully hallucinating\na few types. and I copy the results into a Lazarus sourcefile and fix all the\ncompilation errors.\n\nFinally, the needed declarations are in. To return to whatever I was\npreviously doing (what was it again):\n```sh\n[root/frame/create GUI/GUI Elements/decls needed] $ frame pop\nFrame 0.1.2, (© 2023 Lelanthran Manickum)\n[src/frm.c:55] Failed to switch to new dir [root/frame/create GUI/GUI Elements/decls needed]\n[src/frm.c:1440] Warning: using relative path [root/frame/create GUI/GUI Elements/decls needed] failed, trying absolute path\nCurrent frame\n   root/frame/create GUI/GUI Elements\n\nNotes (Sat May 13 08:26:59 2023)\n   Needs to display:\n   1. The history, with a filter\n   2. A treeview of all frames from root, with the current frame selected and\n   expanded.\n   3. An editor box that displays the notes for the current frame, and allows the\n   user to edit those notes.\n```\n\nRight, I was on the first item. Maybe create a frame for that:\n\n```sh\n[root/frame/create GUI/GUI Elements] $ frame push 'populate history element' --message='\n\u003e create a populate_history() procedure\n\u003e '\nFrame 0.1.2, (© 2023 Lelanthran Manickum)\nCreated new frame [root/frame/create GUI/GUI Elements/populate history element]\n```\n\nI create the named function, run it, watch in disbelief as it crashes, and\nthen investigate the crash. Turns out there's a bug in the library. New frame\ntime:\n```sh\n[root/frame/create GUI/GUI Elements/populate history element] $ frame push 'debug frm_history' --message='\n\u003e frm_history, when called multiple times in the same session, has a double-free\n\u003e bug. This does not show up in the c/line `frame history` command because the `frame`\n\u003e program runs the specified command and then exits.\n\u003e '\nFrame 0.1.2, (© 2023 Lelanthran Manickum)\nCreated new frame [root/frame/create GUI/GUI Elements/populate history element/debug frm_history]\n```\n\nI switch to working on the frame library instead. I update the test script to\nreproduce the error, and using valgrind, and then some gdb, I come up with an\nappropriate fix.\n\n```mermaid\ngraph TD\n   A[root] --\u003eB[projects];\n   B[projects] --\u003e C[frame];\n   C --\u003e D[create GUI];\n   D --\u003e E[populate history element];\n   E --\u003e F[debug frm_history];\n   F --\u003e G[update tests to reproduce bugs];\n   G --\u003e H[fix bug];\n```\n\nNow that the bug in frm_history has been fixed, I perform multiple `frame pop`s,\nuntil I reach a frame that is still incomplete: `populate history element'.\n\nAfter each `pop`, the current frame is **deleted**, never to be seen again.\nThis helps keep the signal/noise ratio to an acceptable number.\n\nOnce I am at `populate history element`, I relink and rerun the program. It\npopulates the history just fine. I continue my development in this manner;\n`push`ing new frames when I start something even slightly long, `pop`ping\nframes once I am done with whatever I am currently working on.\n\nWhen I need to record more notes or thoughts related to the current frame, I\nuse `frame edit` and record my notes in the editor that opens.\n\nWhen I come\nback to my computer after (for example) a good nights sleep, my terminal will\ndisplay the name of the current frame. If that is not sufficient to remind me\nwhere I stopped last, then `frame status` displays all my notes.\n\nSometimes I come back to a frame after multiple days; I create a frame, store\nmy notes, then 'push' new frames diving ever deeper into whatever it is I am\ndoing, and then two days later I have finally `pop`ped my way back to the\noriginal frame, and thankfully I can use `frame status` to remember what it\nwas I wanted to do next.\n\nIt's all very linear, and intentionally so. You can create new frames\nwithout immediately switching to the newly created frame by using `frame new`.\nI do this when I know in advance that I need to do $X, $Y and $Z: then it's\nsimpler to just do `frame create $X`, `frame create $Y` and `frame create $Z`,\n\n## Not only linear\nI create multiple frames off `root`; typically one for each project. The\n`frame switch` command accepts a target frame, and attempts to switch to the\nsub-branch of that frame that you were last working on.\n\nFor example, having frames\n\n```mermaid\ngraph TD\n   A[root] --\u003e B[frame];\n   A --\u003e C[game];\n   B --\u003e D[create GUI];\n   D --\u003e E[populate history];\n   E --\u003e G[debug crash];\n   B --\u003e H[write docs];\n   C --\u003e I[Scrabble Clone];\n   I --\u003e J[implement basic user mgmt];\n   J --\u003e K[email verification on signup];\n   I --\u003e L[create board];\n   L --\u003e M[store game state];\n```\nMy current frame is *[root/frame/create GUI/populate history]*. The last time I\nwas in a sub-branch of *[root/game]*, it was in the *[email verification on\nsignup]* sub-branch.\n\nWhen I do `frame switch root/game`, it returns me to the branch\n*[root/game/Scrabble Clone/implement basic user mgmt/ email verification on\nsignup]*.\n\nThe next time I do `frame switch root/frame`, it returns me to\n*[root/frame/create GUI/populate history]*.\n\n## Command reference\nYou can get a list of commands using `frame --help`. The important commands\nare\n\n1. `push \u003ctitle\u003e` - push a new child frame with specified title.\n2. `pop` - discard current frame and make parent current.\n3. `edit` - edit the context attached to the current frame.\n4. `switch \u003cpath\u003e` - (smart) switch to a new path.\n\n\n[^1]: I made a conscious decision at the start of this project that `root` will\nalways be explicit and not implicit. This avoids errors where a typo results\nin addressing root when the user did not mean to. For example the following\nerror is impossible when root is explicit: `rm -rf myhomedir/Downloads /lib`.\n\n[^2]: In reality, my `PS1` variable is a lot more complex than this, as it\nincludes the git branch (if any) and other information (current directory,\nusername, etc). My prompt uses a separate line for the `frame` name, as this\nname can get quite long.\n","funding_links":[],"categories":["C"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flelanthran%2Fframe","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flelanthran%2Fframe","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flelanthran%2Fframe/lists"}