Git Product home page Git Product logo

jsoncanvas's People

Contributors

alexwiench avatar cheeksthegeek avatar ericaxu avatar fasblom avatar jg-l avatar julienduroure avatar kepano avatar lishid avatar marknote avatar notnout avatar ongaeshi avatar ossldossl avatar rob-gordon avatar supersonicpineapple avatar trbndev avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

jsoncanvas's Issues

group types nodes are containing types but don't specify dimensions

I'm not sure if group nodes are supposed to act like boxes in Kinopio which are a type of node that can contain other nodes. If so, to draw a box I would need a width and height (and ideally a color) to know how to draw it. These attributes aren't specified in the docs though.

image

node width and height should be optional

For importing, Kinopio computes a node's dimensions dynamically based on it's contents so explicit width and height are unnecessary in my context (manual resizing is an optional action that the user can take). Also because Kinopio's design also has to work on mobile, Kinopio cards might have more padding or a larger default font size than other apps.

But also going the other way with exporting, the width and heights that kinopio uses might look wonky in another app so I can specify them but I wouldn't want them to be treated as gospel elsewhere.

BGL and jsoncanvas

Over in the Breadboard project, we have a BGL format that looks surprisingly similar to json canvas. BGL JSON schema is here.

I wonder if there's an opportunity to unify or at least bring them closer together?

See breadboard-ai/breadboard#1034

Create JSON Schema to further define and constrain the spec.

I would suggest creating a JSON Schema to define the spec.

Here's a first pass at that.

{
    "$schema": "https://json-schema.org/draft/2020-12/schema",
    "$id": "https://github.com/obsidianmd/jsoncanvas/canvas.json",
    "title": "An open file format for infinite canvas data.",
    "description": "Infinite canvas tools are a way to view and organize information spatially, like a digital whiteboard. Infinite canvases encourage freedom and exploration, and have become a popular interface pattern across many apps.\nThe JSON Canvas format was created to provide longevity, readability, interoperability, and extensibility to data created with infinite canvas apps. The format is designed to be easy to parse and give users ownership over their data. JSON Canvas files use the .canvas extension.\nJSON Canvas was originally created for Obsidian. JSON Canvas can be implemented freely as an import, export, and storage format for any app or tool. This site, and all the resources associated with JSON Canvas are open source under the MIT license.",
    "type": "object",
    "properties": {
        "nodes": { "title": "An optional array of nodes.", "$ref": "#/$defs/nodeCollection"},
        "edges": { "title": "An optional array of edges.", "$ref": "#/$defs/edgeCollection"}
    },

    "$defs": {
        "nodeCollection": {
            "title": "An array of nodes.",
            "type": "array",
            "items": { "$ref": "#/$defs/node"}
        },
        "edgeCollection": {
            "title": "An array of edges.",
            "type": "array",
            "items": { "$ref": "#/$defs/edge"}
        },

        "node": {
            "title": "A node.",
            "description": "Nodes are objects within the canvas. Nodes may be text, files, links, or groups.",
            "oneOf": [
                { "$ref": "#/$defs/textNode" },
                { "$ref": "#/$defs/fileNode" },
                { "$ref": "#/$defs/linkNode" },
                { "$ref": "#/$defs/groupNode" }
            ]
        },

        "genericNode": {
            "title": "A generic node.",
            "type": "object",
            "required": ["id", "type", "x", "y", "width", "height"],
            "properties": {
                "id": {
                    "title": "A unique identifier for the node.",
                    "type": "string"
                },
                "type": { "title": "The node type (a discriminator).", "$ref": "#/$defs/nodeType"},
                "x": { "title": "The x position of the node in pixels.", "$ref": "#/$defs/coordinate" },
                "y": { "title": "The y position of the node in pixels.", "$ref": "#/$defs/coordinate" },
                "width": { "title": "The width of the node in pixels.", "$ref": "#/$defs/dimension" },
                "height": { "title": "The height of the node in pixels.", "$ref": "#/$defs/dimension" },
                "color": { "title": "The color of the node.", "$ref": "#/$defs/color" }
            }
        },

        "textNode": {
            "title": "Text type nodes store text.",
            "$ref": "#/$defs/genericNode",
            "required": ["type", "text"],
            "properties": {
                "type": { "$ref": "#/$defs/textType" },
                "text": { "title": "Text in plain text with Markdown syntax.", "type": "string"}
            }
        },

        "fileNode": {
            "title": "File type nodes reference other files or attachments, such as images, videos, etc.",
            "$ref": "#/$defs/genericNode",
            "required": ["type", "file"],
            "properties": {
                "type": { "$ref": "#/$defs/fileType" },
                "file": { "title": "The path to the file within the system.", "type": "string"},
                "subpath": { "title": "A subpath that may link to a heading or a block. Always starts with #.", "$ref": "#/$defs/subpathReference"}
            }
        },

        "linkNode": {
            "title": "Link type nodes reference a URL.",
            "$ref": "#/$defs/genericNode",
            "required": ["type", "url"],
            "properties": {
                "type": { "$ref": "#/$defs/linkType" },
                "url": { "title": "The URL referenced by the link", "type": "string", "format": "iri" }
            }
        },

        "groupNode": {
            "title": "Group type nodes are used as a visual container for nodes within it.",
            "$ref": "#/$defs/genericNode",
            "required": ["type"],
            "properties": {
                "type": { "$ref": "#/$defs/groupType" },
                "label": { "title": "A text label for the group.", "type": "string"},
                "background": { "title": "The path to the background image.", "type": "string"},
                "backgroundStyle": { "title": "The rendering style of the background image.", "$ref": "#/$defs/backgroundStyles"}
            }
        },

        "nodeType": {
            "type": "string",
            "enum": ["text", "file", "link", "group"]
        },

        "textType": {
            "title": "The type of a text node.",
            "const": "text"
        },

        "fileType": {
            "title": "The type of a file node.",
            "const": "file"
        },

        "linkType": {
            "title": "The type of a link node.",
            "const": "link"
        },

        "groupType": {
            "title": "The type of a group node.",
            "const": "text"
        },

        "edge": {
            "title": "Edges are lines that connect one node to another.",
            "type": "object",
            "required": ["id", "fromNode", "toNode"],
            "properties": {
                "id": {
                    "title": "A unique identifier for the edge.",
                    "type": "string"
                },
                "fromNode": {
                    "title": "The node id where the connection starts.",
                    "type": "string"
                },
                "fromSide": {
                    "title": "The side where this edge starts.",
                    "$ref": "#/$defs/side"
                },
                "fromEnd": {
                    "title": "The shape of the endpoint at the edge start",
                    "$ref": "#/$defs/endpointShape"
                },
                "toNode": {
                    "title": "The node id where the connection ends.",
                    "type": "string"
                },
                "toSide": {
                    "title": "The side where this edge ends.",
                    "$ref": "#/$defs/side"
                },
                "toEnd": {
                    "title": "The shape of the endpoint at the edge end",
                    "$ref": "#/$defs/endpointShape"
                },
                "color": {
                    "title": "The color of the line.",
                    "$ref": "#/$defs/color"
                },
                "label": {
                    "title": "The text label for the edge.",
                    "type": "string"
                }
            }
        },

        "side": {
            "title": "The side of a node.",
            "type": "string",
            "enum": ["top", "right", "bottom", "left"]
        },

        "endpointShape": {
            "title": "The shape of the endpoint of an edge.",
            "type": "string",
            "enum": ["none", "arrow"]
        },

        "coordinate": {
            "title": "A co-ordinate (x or y) in pixels.",
            "type": "integer"
        },

        "dimension": {
            "title": "A dimension (width or height) in pixels.",
            "type": "integer",
            "minimum": 1
        },

        "color": {
            "title": "The color type is used to encode color data for nodes and edges",
            "oneOf": [
                { "$ref": "#/$defs/hexColor" },
                { "$ref": "#/$defs/presetColor" }
            ]
        },

        "hexColor": {
            "title": "A color in hexadecimal format.",
            "type": "string",
            "pattern": "^#(?:[0-9a-fA-F]{3}){1,2}$"
        },

        "presetColor": {
            "title": "A preset color.",
            "description": "Six preset colors exist, mapped to the following numbers:\n1 red\n2 orange\n3 yellow\n4 green\n5 cyan\n6 purple",
            "type": "integer",
            "enum": [1, 2, 3, 4, 5, 6]
        },

        "subpathReference": {
            "title": "A subpath that may link to a heading or a block. Always starts with #.",
            "type": "string",
            "pattern": "#(?:.*)"
        },

        "backgroundStyles": {
            "title": "The rendering style of a background image.",
            "description": "Options are:\ncover - fills the entire width and height of the node.\nratio - maintains the aspect ratio of the background image.\nrepeat - repeats the image as a pattern in both x/y directions.",
            "type": "string",
            "enum": ["cover", "ratio", "repeat"]
        }
    }
}

From this we can generate object models to validate and operate over the files in a variety of different languages.

For example, I attach code generated by Corvus.JsonSchema to work with the object model in C#/dotnet.

Corvus.JsonSchema.Generated.zip

are file nodes always local relative paths?

reading the spec, I 'm unsure whether file nodes are meant to be relative local path references to files on the user's own machine only, or are they also for remote files with absolute urls too? eg (https://cdn.kinopio.club/71ta143mwPRfinHf9t-ML/kaomoji-palette-main.zip)

Proposal: Edge offset property

fromOffset and toOffset property

Currently creating three edges between a pair of nodes will create this clutter:

image

This is of course not very useful, and I therefor propose fromOffset and toOffset. The properties store an integer for the pixel offset of the edge. Where 0 is center of the side, positive being 'up' the side and negative being 'down' it. Meaning a positive offset will push the edge towards the positive axis of the side. For example, with toSide: 'bottom' will toOffset: -100 push the edge arrow 100 pixels towards -x (left).
Offsets other than 0 need to be clamped with in sideLength/2 and -sideLength/2, as the edge shouldn't be able to start and end past the node side.
The properties are optional and default to 0 (the center) when not defined.

image

// Edge object
{
   ...properties,
  "fromOffset": 0,
  "toOffset": 100
},
  • fromOffset (optional, integer) is the pixel offset on the side where this edge starts.
  • toOffset (optional, integer) is the pixel offset on the side where this edge ends.

This proposal shouldn't be considered done. I will try to accommodate any feedback.

Proposal: Edges additional properties

I think some properties are missing to have the JSONCanvas standard to be adopted by awider range of apps.

type property

It would be a good addition to have another property on the Edge object:

type which could have those values:

  • sharp for sharp turns
  • curved for smooth turns (the default one)
  • straight no turns, a straight line between fromNode and toNode

See the example:

//edge object
{
   ...properties,
  "type": "curved"
},
{
   ...properties,
   "type": "sharp"
}

style property

Having a canvas style would add more variety on edge styles.

style would somehow follow the border CSS property style:

  • solid: For a solid line (the default one)
  • dotted: For a dotted line
  • dashed: For a dashed line
  • mixed: A mix between dotted and dashed
  • none: To hide the edge without removing it

e.g:

//edge object
{
   ...properties,
   "style": "solid"
},
{
   ...properties,
   "style": "dashed"
},
{
   ...properties,
  "style": "dotted"
},
{
   ...properties,
  "style": "mixed"
},
{
   ...properties,
   type: "none"
}

fromLabel & toLabel properties

For now we can only set a label to an edge, that would be supposedly displayed at the middle of it.
What if we want to display a label that at the end or the beginning of the edge, or even both?

They would work like the label property but used to be displayed at the start or/and the end of an edge.
Think about SQL schema n-m relationships.
Those property would not be self exclusive and using the 3 labels properties would work.

{
   ...properties,
  "fromLabel": "1",
  "toLabel": "n",
  "label": "1-n relationship"
},
{
   ...properties,
  "fromLabel": "1"
},
{
   ...properties,
   "toLabel": "*"
}

Need for clarification: toEnd & fromEnd

Hi,

The specification lacks clarity on the behavior when "toEnd" and "fromEnd" are unset, since they're optional. It should be specified that omitting "toEnd" or "fromEnd" defaults to "toEnd":"none" and "fromEnd":"none" respectively. This clarification is necessary to ensure consistent handling of null values for these parameters across different renderings.

Specify content format more strictly

There are many incompatible markdown dialects. Consider switching to HTML or specifying a specific dialect, and define what to do when unsupported content is encountered.

Define coordinate system

As far as I can tell, the spec doesn't define the coordinate system in use (e.g. which direction is positive in each axis, what point on the entity is represented by its {x,y} position.

For interoperability, it would be good to tie this down explicitly.

Diagram Types

The current spec v1.0 defines "nodes + edges on a canvas" and some general metadata on nodes and edges. Very minimal, which is good.

In terms of minimalism as well as the need to convert a diagram into specific export formats it would be fantastic to see support for definition of Diagram Type. Examples of types:

  • UML Class Diagram
  • Mindmap Diagram
  • Sticky Notes (brainstorming / ideation)

And an example of more domain-specific diagram types:

What would the Diagram Type be used for:

  • Load tool palettes exclusively containing relevant shapes and tools.
  • Indicate diagram validity or restriction of illegal actions ("can't connect these shapes").
  • Allow export to valid domain-specific data format / controlled vocabulary.
    • E.g. in case of the event diagrams the export might be used to bootstrap Event Catalog and generate boilerplate code for your eventsourcing project.

Thus far I haven't found universal way to define diagram types in any canvas-based diagramming solution. Restricting the UI to conform to a specific diagram type involves custom API's and even then cannot be fully achieved (unless by signficant custom code and/or forking the diagrammer codebase).

In most canvas-based diagrammers you draw rather than diagram. I.e. a adding a sticky note involves creating a rectangle, giving a color, sometimes adding a textbox within and grouping that with the rectangle. That makes it hard, highly customised, or unfeasible to export to a specific format / vocabulary.

Proposal: canvasz — a zipped canvas file, which packages a the markdown files along with it

Right now, the main difference between other file formats comparable to JSON Canvas is that JSON Canvas or some other superset/abstraction of JSON Canvas isn't self-sufficient yet, meaning if I have a lot of markdown files referred-to inside my canvas file, and my Obsidian Vault or setup inside one of the other apps enabled with JSON Canvas import/export capability is not managed well, the JSON Canvas file is going to end up mostly unusable due to non-existent or unreachable file links.

Hence, I propose having another layer of abstraction over JSON Canvas, called JSON Canvas Zipped, and the file extension for which would be .canvasz, and when unzipped, would have this directory structure present inside:

unzipped_canvasz_file_dir/
├── index.canvas
├── file1.md
├── file2.md
└── file3.md

If Obsidian or other software with complete Canvas import/export implementations choose to export, the user could be offered the ability to export as either. When importing, the offer would be to choose between a plain text JSON-based canvas file, or the packaged canvasz file. If the user chooses the latter, the import implementation component would track down each file from the internal setup, and package it in the given structure for the user to use it anywhere.

This proposal is an effort towards making the JSON Canvas file or at least an abstraction over it to be self-sufficient in the data it refers to internally.

— chaitanya

Node Based Editing

I am working on a node based editor that is also aiming to be generic and an open format.

I also store the relationships in a pretty similar way, but curious if there could be some support for edges and nodes with additional metadata:

{
  "nodes": [
    {
      "id": "node-1",
      "type": "number-value",
      "x": 36,
      "y": 48,
      "width": 176,
      "height": 68,
      "inputs": {
          "source": {
             "type": "number",
             "value": "1"
          }
      },
      "outputs": {
          "result": {
             "type": "number",
             "value": "#source"
          }
      }
    },
    {
      "id": "node-2",
      "type": "number-value",
      "x": 36,
      "y": 48,
      "width": 176,
      "height": 68,
      "inputs": {
          "source": {
             "type": "number",
             "value": "2"
          }
      },
      "outputs": {
          "result": {
             "type": "number",
             "value": "#source"
          }
      }
    },
    {
      "id": "node-3",
      "type": "number-add",
      "x": 36,
      "y": 48,
      "width": 176,
      "height": 68,
      "inputs": {
          "left": {
             "type": "number",
             "value": "0"
          },
          "right": {
             "type": "number",
             "value": "0"
          }
      },
      "outputs": {
          "result": {
             "type": "number",
             "value": "#left + #right"
          }
      }
    }
  ],
  "edges": [
    {
      "id": "edge-1",
      "fromNode": "node-1",
      "fromSide": "right",
      "fromEnd": "none",
      "toNode": "node-3",
      "toSide": "left",
      "toEnd": "arrow",
      "metadata": {
          "output": "#result",
          "input": "#left"
      },
    },
    {
      "id": "edge-2",
      "fromNode": "node-2",
      "fromSide": "right",
      "fromEnd": "none",
      "toNode": "node-3",
      "toSide": "left",
      "toEnd": "arrow",
      "metadata": {
          "output": "#result",
          "input": "#right"
      },
    }
  ]
}

This could allow an expressive format for editing between applications too.

The metadata object could also be top level but might conflict with spec properties:

{
  "id": "edge-1",
  "fromNode": "node-1",
  "fromSide": "right",
  "fromEnd": "none",
  "toNode": "node-3",
  "toSide": "left",
  "toEnd": "arrow",
  "output": "#result",
  "input": "#left"
}

IDEA: Provide a real JSON-SCHEME spec

With a real spec in json schema, every implementer has an automated way of checking conformity, allows for versioning and removes (future) ambiguity . Bonus is that it helps implementers to generate type definitions in the host language.

See https://json-schema.org/

Version property

It looks like the spec doesn't leave a lot of room for future additions.

You could add a version property to the root and specify what the behavior should be if an unrecognized property is found.

My suggestion is that the spec should state something like the following:

  • unrecognized properties must be ignored by any implementation
  • the implementation should not attempt to parse the canvas if the version is greater that the version that the implementation supports
  • when writing a canvas file, the lowest version should be used that supports all features used in the current canvas document

The downside of this is that implementations will be slightly more complicated but it will give the spec a lot more room to grow.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.