obsidianmd / jsoncanvas Goto Github PK
View Code? Open in Web Editor NEWAn open file format for infinite canvas data.
Home Page: https://jsoncanvas.org
License: MIT License
An open file format for infinite canvas data.
Home Page: https://jsoncanvas.org
License: MIT License
For example, is #FF0000
a preset value for red
? Or is there some kind of theme tie-in that this conveys rather than a hard value used in all implementations?
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.
hi guys please set VazirMatn font for persian language default font.
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?
The JSON Canvas format is should be based on well-defined data standards:
I think some properties are missing to have the JSONCanvas standard to be adopted by awider range of apps.
type
propertyIt would be a good addition to have another property on the Edge
object:
type
which could have those values:
sharp
for sharp turnscurved
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
propertyHaving 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 linedashed
: For a dashed linemixed
: A mix between dotted
and dashed
none
: To hide the edge without removing ite.g:
//edge object
{
...properties,
"style": "solid"
},
{
...properties,
"style": "dashed"
},
{
...properties,
"style": "dotted"
},
{
...properties,
"style": "mixed"
},
{
...properties,
type: "none"
}
fromLabel
& toLabel
propertiesFor 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": "*"
}
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:
The downside of this is that implementations will be slightly more complicated but it will give the spec a lot more room to grow.
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:
And an example of more domain-specific diagram types:
What would the Diagram Type be used for:
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.
Seems this repo is only a spec without code. To make this really useful, we need link to the npm package or github repo with useable code.
For example, the infinite canvas library https://github.com/tldraw/tldraw opensource both the spec and the source code.
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.
fromOffset
and toOffset
propertyCurrently creating three edges between a pair of nodes will create this clutter:
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.
// 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.
While I don't foresee the spec changing too often, embedding the spec version into the canvas body would allow for migration handling.
I also suggest using semantic versioning in this case.
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.
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
)
jsoncanvas is amazing!
while the spec is pretty short, i suggest adding a playground page on the website with live preview to folks to try it out quickly.
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.
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.
The spec states that the color field expects a string. Does that mean that preset colors should be given as e.g. "red"
? If so, what is the need for the numbers they each map to?
This is such a good and simple way to share ideas, will be amazing to have this part of the repo and visible from vscode or any other IDE
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"
}
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
The page https://jsoncanvas.org/ should list known implementations. I guess by know it's
People should be encouraged to use the format in other software as well.
So Kinopio has the concept of cards (nodes) that can have connections between each other (edges). But based on a lot of feedback requests I'm also shipping soon the ability to connect boxes (groups) to both cards and other boxes:
How should I represent this new kind of possible edge relationship in my .canvas export file?
At top level, add a $schema
property to allow to refer to a JSON Schema.
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.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.