goby.garden

goby is a work-in-progress desktop application. this page is inspired by broadsheet, and tracks my live progress through various notetaking contexts. ā€” Nico

Currently I describe goby as an interface for creating and managing personal databases. Compared to existing tools that fall under that umbrella, I want Goby to prioritize things like personal data ownership, durable and transparent file format (SQLite), and referential compatibility with assets stored in different places (desktop files, web URLs, raw text, etc).

goby journal

[src]

1/27/2025

Iā€™ve previously come to the conclusion that it would be pretty difficult to support ordered lists for relation properties with my SQL model - I wonder if I could do it via the method detailed here though, just adding two _order columns to the junction table (one for each side), and enforcing the order when I save to the database...

1/6/2025

Another big change that I have planned is moving the property definitions to their own tables, instead of defining them in a JSON field.

goby-database/notes.md

[src]

Contents:

  1. running notes
  2. technical terminology
  3. things this program should be able to do

...and more below

Running notes

1/18/2025

Picking up on the reflection from the 16th: Iā€™ve taken care of cases where transfers get queued for nonexistent targets, and general duplicate actions, by filtering those out in the consolidation step. The open question is what to do when someone deletes a property which was previously a target, and then adds their own new target within the same class. Normally when you delete a property which was a target, I queue the creation of a one-way relation to replace it, and transfer the connections from the previous two-way.

Itā€™s an open question whether to keep that behavior at all, since I could imagine the function just deleting everything that involves deleted targets, with no attempts at preserving data. Likely the interface will automatically handle some of this anyway, and send an input that takes it into account. But on the other hand I think it is nice, especially if someone uses the class as a programmatic interface (not that I necessarily see that happening, but maybe if this project miraculously takes off). So I think Iā€™ll leave it in, and in the future what I could do is make that an option, e.g. "safe mode", set to true/false, that way you can bypass it if you prefer.

What Iā€™d like to do though, since this problem is about validation with other relationship edits, is to address it in the consolidation function. And I think I have a way to accomplish that: when consolidating, if there are multiple creations/transfers involving the same pair of classes and one matching property, pick the one with the highest level of specificity, meaning privilege prop targets to class targets.

1/16/2025

  • I think I need to modify my logic for deleting or creating relations on the basis of classes/properties being deleted
    • e.g. if I delete a property, but rather than targeting the whole class, I already have a change queued to move the target to a different property. In that situation, it should honor whatā€™s already in the queue instead of queing or overriding another transfer
    • also, what happens if the classes on either side of a relation are deleted? there may technically be available transfers individually, but they cancel each other out. I need to make sure the classes and properties in new_sides, exist, otherwise reject a transfer and just delete.

1/13/2025

Current to-do:

  • refactor junctions so that:
    • in the junction list, the following are normalized: side_a_class_id, side_a_prop_id, side_b_class_id, side_b_prop_id
      • should be retrieved as a JunctionSides json array
    • in individual junctions, the columns should be a little more descriptive, e.g. class_2_property_5 or class_3
  • refactor properties so they exist in their own tables, and are created when classes are created
  • refactor class/junction retrieval and caching
    • reflect above changes
    • separate retrieval of items, properties, and relations
    • get relation targets from junction list rather than storing them on the property (DRY)
  • implement junction transfer
  • take a pass at unifying/clarifying the terminology a little bit
    • maybe replace "junction" with "relationship" and "relation" with "connection"?

Currently working through revising the edit schema function. The class and prop edit loops were simple enough but the relationship edits introduce some complexity:

  • relationships can be one-way (e.g. class A prop 1 -> class B) or two-way (e.g. class A prop 1 <-> class B prop 2)
  • if a two-way is converted to a one-way, or vice versa (i.e., when both of the classes and at least one of the properties persist) we want to transfer the old connections to the new relationship, instead of deleting entirely.
  • I should also enforce the rule that a property can only target one property from a class

How to handle this? What Iā€™m thinking:

  1. create a new consolidated edits array
  2. add the type:"transfer" relations to the array
  3. loop through type:"create" relations
    • check if thereā€™s an existing relation that matches both classes and one property
      • if it exists:
        • look for a type:"delete" which deletes this relation
          • if thereā€™s a delete, push a transfer
          • if thereā€™s not a delete, ignore this creation because itā€™s invalid
      • if it does not exist
        • add the type:"create" normally
  4. loop through the type:"delete" relations
    • check if thereā€™s already a transfer for it in the consolidated array, and ignore if so
  5. loop through the consolidated edits array and apply the changes

1/7/2025

  • the current dilemma is an empty selection is showing up for FROM in the class retrieval SQL query, causing a syntax error. Iā€™ve isolated the problem to be that in the sandbox file, Iā€™m attempting to create a relation property without specifying targets, although the property implicitly has some targets, just based on the junctions Iā€™m declaring in the same action_edit_class_schema call. So there are a few dimensions here:

  • I want to figure out why my type definitions for the input to that function arenā€™t flagging that there should be relation_targets for properties being created

  • I need an approach to relation props that do not have any targets. I donā€™t necessarily want to allow someone to create them manually, but if all the targeted classes gets deleted, I need a way of handling that which does not break things. Here is my inclination:

    1. In the interface, warn you if your changes will result in a relation property without any targets. Allow them to do it though, because I know it would be annoying to have to go through editing every property which depends on a class before deleting the class itself.

    2. Conditionally handle empty relation props in the class retrieval function by making them return an empty array instead of it breaking by trying select out of nonexistent SQL tables

  • I wonder at what stage I should be performing validation for synchronicity between the junctions and the classes being created. There have sort of been two schools of thought Iā€™ve been bouncing between while building action_edit_class_schema:

    • goby-interface will regardless have to implement a way of on-the-fly editing the staged schema in concert with the GUI, so the input that this function will be getting programmatically should be correct.
    • However, I may want to expose this function for developers to use outside of the goby-interface environment, and it should be modular and stable enough to not crash the database if you fuck something up (such as the error Iā€™m encountering in my sandbox). Part of that is just doing type-checking to prevent malformed inputs, which would resolve my current issue, but part of it is making sure the input is logically consistent.

Right now there is a level of redundancy involved, in that Iā€™m specifying both a list of changes to classes, which include some of the relationships which are specified by the junction list. I think in an earlier incarnation of this function, assuming I set it up correctly (which without types is a little iffy to me), it may have been able to infer the targets from the junctions. In any case thereā€™s not an exact parity between the two arrays, such that I could inversely infer the whole junction list directly from the list of changes to classes, because it is the whole junction list: a list of every relationship that is recorded by the project database. Then thereā€™s action_update_relations, which is independently performing validation and figuring out what SQL changes need to be made by simply comparing the current and staged lists of junctions.

I think I wanted to pass the whole junction list because thatā€™s way easier from the side of the interface. I will likely have the junctions separated into their own array rather than only implicitly contained in the properties, because thatā€™s a convenient way to represent the data and because it prevents me from having to fetch the whole schema of every class. Then in the editing mode, instead of recording each edit to the relations, I would just need to record the new holistic state and give it to my code here to deal with it. But if Iā€™m doing diffing somewhere anyway, I might as well handle that logic on the front-end, or even provide myself with some utility functions via this package to do it there.

In terms of what actually makes sense to pass to action_edit_class_schema, I think I ought to only include the class array, and compile a list of resulting junction changes. What does this entail in terms of handling?

  • For property creation: simple enough, create new junctions for each of the targets of the new property
  • For class creation: no need to do anything unless a relation property is also created
  • For class deletion: delete all the relations which involve this class (see discussion above of empty relations)
  • For property deletion: delete any junction tables for one-way class targets of this property. For two-way property targets, instead of completely deleting the tables, I want to convert them to represent one-way class targets by the surviving property
  • For property modification: if the targets change, I just need to create or delete the corresponding tables

This precipitates the realization that another thing that I will have to infer somewhere is what properties will have to be changed as a result of explicitly editing others. Namely, targets will need to be added or removed to corresponding classes based on the changes I make.

It also precipitates the realization that it will be difficult to conclude what changes I need to execute while looping over the change list, since changes may override each other (e.g. I donā€™t have to delete a property on a class if later on in the edit queue Iā€™m deleting the class altogether). This is probably why I thought to pass the complete new state of the schema rather than a list of edits.

How about this - instead of consolidating the representation of the edit into one list, maybe I differentiate it more:

  1. List of class creations and deletions, and title/metadata edits
  2. List of on-property changes, like deletion/creation, data types, and title or styling
    • explicitly does NOT include targets. they will be populated in 3.
  3. List of relation changes, like changing the targets of the properties
    • does NOT include changes implied by class and property deletion. these will be inferred and added when processing #1 and #2
    • DOES include uncreated properties from #2. When going through those changes

This will accomplish a few things:

  • normalize the changes into different categories
  • establish an order of operations which prevents changes going through that counteract/override each other
    • each step depends to a certain extend on the ones that come before it. e.g. properties of a new class need the ID of the class registered before they are created, and new relationships need the IDs of newly created properties (and classes) in order to be themselves registered.
  • prevent information redundancy in the parameters, and the need as a developer to explicitly detail all of the implications of the change that Iā€™m making
  • create a validation funnel for any change to the schema; since every change goes through this function, it can be the central location for any validation logic, and you can make either isolated or bulk changes, which are always handled through the same stable procedure

It has one notable drawback:

  • I have to create data property columns through ALTER TABLE rather than defining them with their class. But I might have been doing this already? (yes, I am already doing this)

Another separate thing that occurs to me, which may be a way of solving the current error without philosophical ponderings, is that whatā€™s actually causing the error is me trying to retrieve the items in a class mid-way through editing the schema, because I call refresh_class_cache during the class/property creation process. I think this is either an oversight from when I was last working on this, or an oversight from the past couple days of converting to typescript. I should have two functions:

  1. One that refreshes the list of classes (with IDs, titles, metadata), and each of their schemas.
  2. One that refreshes the list of items for each of the classes fetched by #1, populating the items array which will be created, empty, in #1.

So in #1, the other thing I will need to do is search the cache before I replace the array to see if that class is already recorded, and copy over the items from the old ClassData object to the new one.


Other random thoughts:

  • maybe I should replace "metadata" everywhere with "attributes"
  • maybe rename PropertyDefinition to PropertyConfiguration or PropertyConfig
  • could I hypothetically add order to relations? just via an order column in junction tables?
    • I do not think so, because the order would be different for each item
    • but I could potentially add the order as an array of IDs in the property metadata? this would just require a dreaded maneuver, storing the data in multiple places...
    • it could be a JSON column in junction tables keyed by class and prop... but that does sound like a Pain to manage
  • could I hypothetically use one-to-many columns in the global table of junctions that identify them with properties and/or classes?

I donā€™t know if this would implicitly accomplish the above BUT could I just normalize the class and prop IDs in the junction list like so?:

| id INTEGER NOT NULL PRIMARY KEY | side_a_class_id INTEGER | side_a_prop_id INTEGER | side_b_class_id INTEGER | side_b_prop_id INTEGER |
FOREIGN KEY(side_a_class_id) REFERENCES system_classlist(id)
FOREIGN KEY(side_b_class_id) REFERENCES system_classlist(id)

I guess I have to decide whether to create a global properties table as well, just like objects, which could be the foreign key reference for the property ids.

  • or... do I create a global table for class and prop IDs, such that I can just have side_a and side_b referencing IDs in that table, whether they are classes or properties (okay that might be slightly unhinged)

Goby technical terminology:

Some rough definitions of the terms that I use in the goby-database codebase*.

Conceptual architecture:

  • item:
    • an entity possessing properties
    • can either be independent
      • represented as a free-floating block on a workspace canvas
    • ...or belong to a class, inheriting the properties it has from said class
      • represented as a row in a table
  • class:
    • the declaration of a kind of item, with a user-defined set of properties.
    • represented as a table, which contains all the items belonging to a class
  • property:
    • some user-defined quality of an item, e.g. the title, page length, or genre of a book
    • two kinds:
      • ā€œdataā€ property: any kind of raw data, i.e. a string of text, a url, a file path, an image, a number, a data, etcetera.
      • ā€œrelationā€ property: a kind of connection drawn between items in the database.
        • For example, an ā€œauthorā€ property of books which references items in the ā€œauthorā€ class.
        • It may involve relations to multiple different classes/+properties
  • connection:
    • a link of some kind between two items
  • relation:
    • the declaration of a kind of connection existing between items, mediated through their properties.
      • For example, in a parent-child relationship, the "parents" property in a child would be linked to the "children" property in a parent
    • relation is to connection as class is to item
    • a junction is the technical component in SQL by which connections belonging to a given relation are recorded in the database

Visual architecture:

  • workspace:
    • a gridded, spatial canvas on which items and classes can be represented and edited visually as rectangular cells of mainly text content.
  • block:
    • a discrete object placed somewhere within a workspace
    • typically the visual representation of an item or class (and its members)

*Unfortunately for now I canā€™t compose the definitions without resorting to some level of jargon, loosely pulled from my education in philosophy and logic, as well as some level of circularity, owing to the way these terms are constitued by their relation to other terms.

Things this program should be able to do:

  • initialization

    • check if system tables exist, if not, create:
      • root object (rows/cells) table that generates/records their unique ID
      • junction table for individual relations
      • class list
      • junction table list
      • image data table for all image data, referenced elsewhere by file name
  • user actions:

    • create a class
    • define properties on classes
      • static data
      • relations
        • create and/or modify the participant classes
        • create and/or modify the junction table
    • create objects in classes
    • enter data for property
    • undo anything (can this be achieved? see Implementing undo/redo below)
      • sqlite has a page detailing a method for achieving this
    • importing data
      • csv to class
      • ability to convert columns into relations by text-matching
  • utilities:

    • create a table
      • core system tables
      • user-defined class tables (recording members of a class and their static properties)
      • junction tables (recording defined relations between classes)
  • data retrieval:

    • retrieving data related to classes or objects in json form
    • pagination-friendly
  • validation:

    • changing column type
      • from multiple to single
      • int to string and back
    • defining new relations
      • making sure objects on the single-select side of a relation aren't added to multiple objects in another class
      • class-to-self relations
    • deleting rows, columns, and classes
      • for relational properties, the related class should have the option of dropping their column, converting to a string, or (if they're connected to other existing classes) just removing those relations

Stored data

  • class metadata:
    • functional:
      • properties
        • type
        • junction ID if relevant
        • property ID
        • styling: order, whether or not it displays, its column width
    • styling:
      • the property it uses as a label (default:name)
      • its color

Core concepts:

  • relations types are a unit, embodied by a junction table
    • they define a type of property, shared across its constituent classes

Junction tables (how relations work in goby)

  • the targeting problem in the old system(see ref):
    • when you make a relation, you can pick multiple targets. the question is: can those targets have each other as possible relations? in other words, can that junction table host relationships not involving the class on which the junction was initiated?
  • the resolution:
    • an overhaul of the way relations are stored and structured, so that it all around makes more sense and isn't arbitrarily tied to the shape of a big junction table (see next bullet)
  • the new system:
    • when you make a relation property, you pick targets like before
    • you can just pick a class as a target, in which case it's one-way, or you can additionally specify a property in a targeted class to link it with
      • The big change here is that properties can specify which targets they link with, rather than having to be "linked" across the board (which didn't make much sense anyway). And they can target any set of classes/link with any property as long as they follow a common sense rule:
    • the basic rule governing possible relationships is that a property can only be linked to one property from a class
      • however, that one property can be itself
      • Q: can two properties within the same class be linked to each other?
        • A: Yes. Imagine "parent of" and "child of" properties.
      • Q: what happens if you start with a class_1.property_A targeting class_2 without any link, and decide to link it with class_2.property B ?
        • I would transfer any unlinked relations from class_1.property_A to this new link, along with any unlinked relations from class_2.property_B which target class_1
        • only caveat is I would validate the relations to make sure they don't violate any constraints on either property, e.g. class_1.property_A having a limit of 1 relation per object
    • the technical implementation: individual junction tables
      • rather than have a single junction table for all of the targets of a property, I'm going to have one junction table for each target. It will have just two columns, one for each class/class.property.
        • following the rule above, the only condition is there can only be one junction table for a class.property and another class
        • junctionlist structure:
          | id | classA_id.propA_id | classA_id.propA_id | metadata? |
          
        • each junction structure:
          | classA_id.propA_id | classB_id.propB_id |`
          
  • maybe "count" could be generalized to a "max" condition?
    • although maybe in the interface still making it a toggle between the single and multi-select that people are familiar with
    • this doesn't work because the conditions are supposed to determine candidates for a relation, and if this is a condition then a single select will have no candidates

Return format for relation properties:

  • for each row, an array of the objects its connected to, in the format: {class_id:X,prop_id:X,object_id:X}

Structure of SQL request including relation prop structured as JSON array

WITH cte AS (SELECT person, ('[' || GROUP_CONCAT(clothing,',') || ']') AS clothing
  FROM (
    
    SELECT person, json_object('type','shirt','id',shirts) AS clothing
    FROM junction_shirts

    UNION

    SELECT person, json_object('type','pant','id',pants) AS clothing
    FROM junction_pants

    UNION

    SELECT person, json_object('type','shoe','id',shoes) AS clothing
    FROM junction_shoes)
GROUP BY person)
SELECT p.id, p.name, c.clothing
FROM people p LEFT JOIN cte c
ON c.person = p.id;

Window management

After some considerations about how windows will work in Goby, Iā€™m moving forward with the idea of having the database file store information about windows in a separate table.

Here is what Iā€™m thinking for the table structure:

  • window ID #
  • type: home/hopper/ or workspace
    • Iā€™m thinking that there will only be one home window and one hopper window, added during the init process as IDs #1 and #2 in the table
  • open : true/false
    • goby will iterate over this and check if it has to open anything
  • metadata: {json}
    • .position (on desktop): [x,y]
    • .dimensions:[w,h]
    • .type: (for workspaces) canvas/focus
    • .items:(for workspaces) [array of objects and tables in this view and their styling meta]
      • .position (in window): [x,y]
      • other styling TBD...

Names versus IDs:

  • One goal is to make the sql database on its own somewhat legible
  • However, without care, names will run amuck and renaming something will require changing the name in a thousand places.
  • Current approach to this for classes and properties is have their names on the actual tables and columns, and their IDs in places where metadata for classes is stored, so at most you only need to change their name in two places

Development thoughts:


Misplaced interface thoughts:

  • relation-select reactivity: instead of some array-copying madness, just have the selector set to the current items as an event, fired with every data update
  • editing relations:
    • I think I'm going to narrow from the previous iteration of the relation creator/editor so you can only configure one relation at a time, meaning you can't edit the constraints on the other relations
  • since a system-wide undo/redo could be quite difficult to implement, an alternative could be using transactions, so after making a change, particularly a table structure change, you would be prompted to commit or reject changes
    • MAYBE there could even be an enterable "transaction mode", in which you make a variety of changes, and then make a decision about whether to accept or reject them.

Misplaced general organization thoughts:

  • maybe the website can have a kind of "timeline" pulling in the goby are.na channels using the api, letting you drag a slider to move forward/backward in the notes i take about it, which appear as a scattered collage

Implementing undo/redo

  • This isnā€™t top of agenda for me right now because itā€™s really complicated, and based on my current understanding it shouldnā€™t be too tricky to build into the codebase later on.
  • Undo/redo functionality isnā€™t built into SQLite, but they do detail a way of technically achieving it here. I donā€™t fully understand how it works yet.
  • I think a simple first goal, when I do get to this, would be to implement undo/redo when it comes to simple data entry, i.e. changing table cells or adding/deleting entire rows.
    • Where Iā€™m anticipating this will get messy is when it comes to Gobyā€™s class design, which allows you to design your own table schema. Undoing/redoing changes to table schema is a more complicated thing which probably isnā€™t accounted for in the customary approach linked above.
    • Moreover, Iā€™m expecting that simple changes like adding an object to a class or changing item styling will be the typical use cases for Command-Z functionality.
    • For class design, I can take advantage of SQLite transactions to provide a brute force way of letting you discard all changes and roll back to a saved state. Maybe the interface can give you some way of ā€œcommittingā€ changes, or a way of entering ā€œtransaction modeā€
  • Another thing to consider/look into: is there a way I could integrate this with git somehow, and have a sort of brute-force undo-redo powered by rolling back changes at the raw data level? Reminds me of that thing that happens when you open an indesign file and get a second, temporary file. Could I somehow track changes while you work and live commit them?

Test suite checklist

Basic:

  • create a class
  • delete a class
    • deal with relation fall-out
  • add a row to a class
  • delete a row from a class
    • deal with relation fall-out
  • add a data property to a class
  • delete a property from a class
    • not allowed if class only has one property
    • deal with fallout if that class is the label

Relation properties ā€” test the following actions/options in relevant combinations with each other:

  • adding a new relation prop
  • setting the conditions.max (formerly count) and validating relations correspondingly
  • deleting a relation prop
  • linking a relation prop to another prop, new or existing
  • having a property target its own parent class
  • removing a link between two props
  • adding a new relation

Returning data:

  • return relation props in the format specified in Return format for relation properties

Workspaces in the database

  • columns in a generated workspace table:
    • type (of thing, e.g. item, class, etc.)
    • block id (assigned for workspace purposes, should be integer primary key)
    • concept id (item id for type='item', class id for type='class', etc for any categories I add in the future )
    • properties (styling like position and size)

goby-interface/notes.md

[src]

1/20/2025

Since last touching this repo many things have changed in my tooling + my perspective:

  • Iā€™ve refactored/overhauled goby-database in a way that likely deprecated some of the back-end code I had set up
  • Svelte 5 is a thing that I want to use
  • Typescript is a thing that I want to use both with svelte and electron
  • Iā€™m rethinking my interface dev priorities:
    • Instead of with standalone objects, I want to begin with the class interface (as tables, cards, or something else) and more importantly with the class schema editor, which is maybe the most unique and personally interesting aspect of this.
    • I donā€™t know if the idea of a canvas is as important to me as it was? I could totally imagine starting with a basic editor for a single class placed normally in the dom (maybe with the ability to tab between classes).

To all these ends, Iā€™ve done a complete overhaul of the directory structure and run commands, so now I can use typescript, svelte 5 and my update to goby-database are installed, the distribution code is more clearly bundled/isolated, and Iā€™m giving myself a clean slate to get started with interface development again.

Connecting with what I mentioned in the last bullet, I may start by creating a new window type, 'basic', which disregards the workspace parameters I had set out below in the anatomy section.

Anatomy of the interface:

  • home: where to select a project, and detached window for index
  • index: a list of objects, classes, and workspaces in the project
    • accessible on the home page and as a drawer in workspaces
  • workspace: a freeflow canvas for placing elements
    • object instances
      • detached cells with text, images, urls, etc
    • tables
      • class instances
      • other customized table views/class filters
  • dropper: frictionless deposit box for new items

General interface musings:

  • I have an idea sketched out/in my mind for a new interface for configuring relation props, relying less on obscure language/iconography and more on physical analogies of drawing connections and wiring circuits
  • Possible semantic elements
    • circles for classes, diamonds for properties, squares/rectangles for objects
  • Iā€™m imagining that this iteration of the interface will be much more stylistically pared back from my previous one, in consideration of the types of interfaces Iā€™m most comfortable working in.
    • Possibly less lines
  • Models/references:
    • IOS Stickies
  • Instead of having string versus paragraph cells, maybe I can just have a text cell with an option to wrap or not
  • It would make sense / feel nice to me if sorting order isn't saved unless you explicitly save it, and if a sorting reset was easily identifiable/accessible
  • In general my current thought is to implement Goby's more unconventional but conceptually important interface features, and otherwise make it as frictionless as possible, and then I can add on settings as I build which are more visually obscured but facillitate advanced usage/presentation modes
  • Iā€™m realizing the shift to the ā€œworkspaceā€ model for windows clarifies a lot of things, in that by decoupling the styling from the styled class/object, I don't have to worry about things not being displayed, or being artificially constrained in their display. The data for any element is stored with minimal styling information, and then a user decides when, where, and how represent it visually, with that styling information tied to the workspace.
    • An "index" drawer accessible through a button in the top-right corner of each page contains everything that exists in the database, and I'm thinking you could drag and drop stuff from it into a workspace.
    • This means you could conceivable have multiple instances of an element, for example different instances of a class each filtering its contents or representing it in a visually distinct manner.
    • on the development side, this means rather than looping through classes (as in the first iteration), a workspace will loop through each of the elements inside of it to place it.
  • With the persistent "index" of items system, it'll have to be clear how and when an item is fully deleted versus merely stashed, and still visible. My thought right now is that items will always delete by default on using the DEL key, with a contextmenu (right click) option to stash them, whereas classes will be the reverse (stashed by delete key, deleted with right click)

Front-end development:

  • It would be ideal to use the same UI elements for both the single select and the multiple select
  • Right now I'm using a store value to record the currently ā€œfocusedā€ UI elementā€™s ID. But Iā€™m anticipating in the future that there may be multiple focused items at a time, in that you may focus an element inside of the currently focused element (e.g. a search field inside of a dropdown).
    • To address this, I think the store variable will need to be an array, and the unfocus function will need to check when another element is clicked/focused to see if itā€™s a descendant of any of the focused items in the array. If itā€™s not, those items need to be unfocused, and if so, then the most recently interacted-with element needs to be added to the array.
  • I discovered just now with some playing around in the tutorial that if you bind an undefined variable to a component prop that has defaults, the variable will take on the default value, which is surprising and will hopefully save me some mess.

Home page

better-sqlite3 misadventures

better-sqlite3 is tricky to combine with electron because it uses the native node installation on your operating system, but to work with Electron it actually needs to use Electronā€™s internal node version.

Step 1 in addressing that was re-compiling the node-module for Electronā€™s node version, which you do by following the steps here using electron-rebuild.

  1. delete node-modules and package-lock.json
  2. run npm i
  3. run ./node_modules/.bin/electron-rebuild

However, after resolving that, I started running into an issue with Electron crashing as soon as I ran any commands with better-sqlite3. This turned out to be brand new bug as of writing this, and to fix it the maintainers recommended running

npm install
cd node_modules/better-sqlite3
npx node-gyp rebuild --debug --build-from-source --runtime=electron --target=26.1.0 --dist-url=https://electronjs.org/headers
cd ../..
npm run start

to manually rebuild the module binary, which seems to have worked for me.

This also meant that ultimately I couldnā€™t just use npm link to install the goby-database code. But instead of just copying it over (which I did last time, ensuing in all sorts of confusions and problems), I just went ahead and published the WIP to npm, and then installed it into the goby-interface repo.


Ways forward:

  • text object

    • set up click event listener to add tentative text object at the clicked grid coordinate
    • figure out how to wire up default item props with the page_contents variable
    • set up validator upon exiting writing mode to see whether a new item should be added to the db (if it has no id and a length>0) or if an existing item has to be deleted (if it has an id and a length<0)
      • set up functions in `goby-database`` to correspondingly add/remove items and set their values
      • this should be called from item with a debouncer so it only updates the db when it's reasonably clear you're done typing
    • set up TextCell component using textarea, with wrapping controls
      • set up hitting shift+enter or esc or clicking elsewhere to exit writing mode
  • drag selection

    • detect when blocks/class rows are in the selection rectangle and add them to the selection
    • make the rectangle persistent if it doesn't encounter any blocks, and provide options to fill region with a text or image item
  • contextmenu

    • set up event dispatcher signaling that an action has been clicked on in the menu
    • make it appear on the left side of the mouse if the menu would go over the page edge
  • set up right-click "context menu" dialog with options to create a class or add an image

  • set up class creation, which should involve placing the circle+line and giving the class a name (which is validated to make sure itā€™s unique)

  • continue considering the mechanics of different interactions, particularly the creation and modification of relation properties

*occasionally outdated/bypassed

goby feature repository

[src]

9/10/2023

Opening a multiple select field as its own isolated ā€œblockā€ on the canvas

8/23/2023

Complete index:

A list of all workspaces, classes, and freestanding objects. Classes would be accordions (possibly using <summary>/<details>), so upon clicking on them you would be able to see their constituent objects.

Iā€™m imagining this to be accessible from the home tab once a project is open, and possibly also as some kind of drawer nav in all workspaces.

8/23/2023

Single table workspaces/windows

Iā€™m thinking of my fondness of the Stickies app on MacOS (where Iā€™m currently drafting this), and the way it isolates notes in different window contexts. I think it would be nice if Goby had a similar functionality, possibly with a responsive column width (while still keeping the widths in grid square units). Iā€™m also realizing that developers have access to the ā€œfloat on topā€ window type on MacOS, so I could add that as an option.

8/23/2023

Visual joins:

In the same way that you can join tables in SQLite on a shared column, I think it would be cool if you could visually link up two or more classes using their relation properties, possibly separating multiple choice selections into multiple rows and only showing repeated information once. I could imagine how the header for a table could be augmented to represent that:

ā— Typefaces + ā— Foundries on ā¬©Foundry/ā¬©Created

8/20/2023

Matrix view:

Ability to create a matrix table representation of two classes intersected in their relations with a third, e.g. in a table displaying political candidate positions by issue, made using the intersection of a candidates table and issues table on a position table

goby moods

[src]

imaginary use cases

[src]

8/5/2022

meal planning

I'm envisioning a list of meals typified by cooking complexity, recipe, leftover friendliness, nutrition rating, etc., and then related to their various raw ingredients, so you could apply a filter limiting the result to what you can make with what you currently have at home. (Bonus, doubles as a running shopping list.)

8/2/2022

keeping different assets related to a project organized within a single documentation space

Right now with Goby I create a separate channel whenever I have a specific category of note which I want to write, e.g. dev notes, future features, use cases. Instead with goby, I could simply keep all these notes in one place and separate them by tables with different fields.

8/2/2022

planning a paper

The paper that Iā€™m currently working on is giving me a moment of ā€œgee I wish Goby was built-out enough to use for thisā€, so Iā€™m going to record the use-case so I can keep it in mind when Iā€™m in the weeds of working on the interface later on:

I have just gotten finished reading a collection of texts by different authors. The exercise has left me feeling ready to generate a list of ā€œthemesā€ which serve several purposes:

  • they allow me to find patterns between the texts: different ways in which the same idea or problem is being articulated, and which I could use my paper to compare
  • they allow me to group passages and quotes that I can draw upon while writing, some of which belong to multiple themes. This gives me an impetus to revisit each text as well.
  • they can be related with one another, so I have a more holistic view of my topic and how different lines of argument support one another
  • they can be related back to the authors, so I can get a sense of how their worldviews stand in relation to one another

This is a mapping task too complex to be done, at least in a conveniently and orderly fashion, using a simple tagging or mind-mapping tool.


Ironically the subject of the paper is in part the idea that you canā€™t perfectly crystallize ideas in representative logical relations, as I seem to be advocating here. But I have never seen Goby as a project of accurately representing reality; rather, the act of ordering ideas and assets in this way is a pathway, a ā€œdisposable ladderā€ if you will, to greater and clearer understanding.

8/2/2022

dev log

Making Goby dev notes channel and realizing it would be useful to be able to enter bugs and future features with fields for task type, screenshots, front end v. back end, and what other entries it depends on.