├── .gitignore
├── .gitattributes
├── spec
├── v0.3
│ ├── hello-world-example.png
│ ├── extensions
│ │ ├── ports-node.json
│ │ ├── relative-node.json
│ │ ├── parent-child-rel.json
│ │ └── hyperedge-rel.json
│ ├── core
│ │ ├── set-rel.json
│ │ ├── group-rel.json
│ │ ├── oval-node.json
│ │ ├── rect-node.json
│ │ ├── path-node.json
│ │ ├── edge-rel.json
│ │ └── arrow-node.json
│ ├── schema.json
│ └── extensions.md
├── v0.4
│ ├── hello-world-example.png
│ ├── extensions
│ │ ├── ports-node.json
│ │ ├── relative-node.json
│ │ ├── parent-child-rel.json
│ │ └── hyperedge-rel.json
│ ├── core
│ │ ├── set-rel.json
│ │ ├── group-rel.json
│ │ ├── oval-node.json
│ │ ├── rect-node.json
│ │ ├── path-node.json
│ │ ├── edge-rel.json
│ │ └── arrow-node.json
│ ├── schema.json
│ └── extensions.md
├── v0.5
│ ├── hello-world-example.png
│ ├── extensions
│ │ ├── ports-node.json
│ │ ├── transforms-node.json
│ │ ├── textstyle-node.json
│ │ ├── hyperedge-rel.json
│ │ ├── parent-child-rel.json
│ │ └── anchored-node.json
│ ├── core
│ │ ├── group-rel.json
│ │ ├── oval-node.json
│ │ ├── rect-node.json
│ │ ├── path-node.json
│ │ ├── edge-rel.json
│ │ └── arrow-node.json
│ ├── how-to-spec.md
│ ├── schema.json
│ └── extensions.md
├── v0.6
│ ├── hello-world-example.png
│ ├── extensions
│ │ ├── ports-node.json
│ │ ├── page-node.json
│ │ ├── oval-node.json
│ │ ├── rect-node.json
│ │ ├── path-node.json
│ │ ├── group-rel.json
│ │ ├── viewport-canvas.json
│ │ ├── edge-rel.json
│ │ ├── transforms-node.json
│ │ ├── textstyle-node.json
│ │ ├── hyperedge-rel.json
│ │ ├── arrow-node.json
│ │ ├── parent-child-rel.json
│ │ └── anchored-node.json
│ ├── how-to-spec.md
│ └── schema.json
├── v0.6.1-draft
│ ├── hello-world-example.png
│ ├── extensions
│ │ ├── ports-node.json
│ │ ├── page-node.json
│ │ ├── oval-node.json
│ │ ├── rect-node.json
│ │ ├── path-node.json
│ │ ├── group-rel.json
│ │ ├── viewport-canvas.json
│ │ ├── edge-rel.json
│ │ ├── transforms-node.json
│ │ ├── textstyle-node.json
│ │ ├── hyperedge-rel.json
│ │ ├── arrow-node.json
│ │ ├── parent-child-rel.json
│ │ └── anchored-node.json
│ ├── how-to-spec.md
│ └── schema.json
└── v0.1
│ ├── schema
│ ├── schemas.schema.json
│ ├── open-canvas.schema.json
│ ├── node.schema.json
│ ├── relation.schema.json
│ └── schema.combined.schema.json
│ └── spec.md
├── .editorconfig
├── .pre-commit-config.yaml
├── example
├── single-node.json
├── cookbook-sticky-note.ocif.json
├── circle-node.json
└── 4x4-rect-node-grid.ocif.json
├── .github
└── workflows
│ ├── static_checks.yml
│ └── file_format.py
├── public
└── _redirects
├── making-of.md
├── design
├── requirements.md
├── adr-000-TEMPLATE.md
├── goals.md
├── resource-structure.svg
└── diagram-files.svg
├── README.md
├── catalog.md
└── cookbook.md
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea
2 | .DS_Store
3 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Normalize EOL for all files that Git considers text files.
2 | * text=auto eol=lf
3 |
--------------------------------------------------------------------------------
/spec/v0.3/hello-world-example.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ocwg/ocif-spec/HEAD/spec/v0.3/hello-world-example.png
--------------------------------------------------------------------------------
/spec/v0.4/hello-world-example.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ocwg/ocif-spec/HEAD/spec/v0.4/hello-world-example.png
--------------------------------------------------------------------------------
/spec/v0.5/hello-world-example.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ocwg/ocif-spec/HEAD/spec/v0.5/hello-world-example.png
--------------------------------------------------------------------------------
/spec/v0.6/hello-world-example.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ocwg/ocif-spec/HEAD/spec/v0.6/hello-world-example.png
--------------------------------------------------------------------------------
/spec/v0.6.1-draft/hello-world-example.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ocwg/ocif-spec/HEAD/spec/v0.6.1-draft/hello-world-example.png
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | end_of_line = lf
6 | insert_final_newline = true
7 | max_line_length = 120
8 | trim_trailing_whitespace = true
9 |
--------------------------------------------------------------------------------
/.pre-commit-config.yaml:
--------------------------------------------------------------------------------
1 | default_language_version:
2 | python: python3
3 |
4 | repos:
5 | - repo: local
6 | hooks:
7 | - id: file-format
8 | name: file-format
9 | language: python
10 | entry: python .github/workflows/file_format.py
11 | types_or: [text]
12 |
--------------------------------------------------------------------------------
/example/single-node.json:
--------------------------------------------------------------------------------
1 | {
2 | "ocif": "https://canvasprotocol.org/ocif/v0.5",
3 | "nodes": [
4 | {
5 | "id": "shape:054132af-4d2f-48ed-b239-f5129dd35288",
6 | "position": [122.34441018202554, -276.51955082999257]
7 | }
8 | ],
9 | "relations": [],
10 | "schemas": []
11 | }
12 |
--------------------------------------------------------------------------------
/spec/v0.3/extensions/ports-node.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json-schema.org/draft/2020-12/schema",
3 | "title": "@ocwg/node/ports",
4 | "description": "Node ports extension",
5 | "type": "object",
6 | "properties": {
7 | "ports": {
8 | "type": "array",
9 | "items": {
10 | "$comment": "Each item is an ID of a node",
11 | "type": "string"
12 | }
13 | }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/spec/v0.4/extensions/ports-node.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json-schema.org/draft/2020-12/schema",
3 | "title": "@ocif/node/ports",
4 | "description": "Node ports extension",
5 | "type": "object",
6 | "properties": {
7 | "ports": {
8 | "type": "array",
9 | "items": {
10 | "$comment": "Each item is an ID of a node",
11 | "type": "string"
12 | }
13 | }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/spec/v0.4/core/set-rel.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json-schema.org/draft/2020-12/schema",
3 | "title": "@ocif/rel/set",
4 | "description": "Set relation extension",
5 | "type": "object",
6 | "properties": {
7 | "members": {
8 | "type": "array",
9 | "description": "IDs of members of the set",
10 | "items": {
11 | "description": "node ID or relation ID",
12 | "type": "string"
13 | }
14 | }
15 | },
16 | "required": ["members"]
17 | }
18 |
--------------------------------------------------------------------------------
/spec/v0.4/core/group-rel.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json-schema.org/draft/2020-12/schema",
3 | "title": "@ocif/rel/group",
4 | "description": "Group relation extension",
5 | "type": "object",
6 | "properties": {
7 | "members": {
8 | "type": "array",
9 | "description": "IDs of members of the group",
10 | "items": {
11 | "description": "node ID or relation ID",
12 | "type": "string"
13 | }
14 | }
15 | },
16 | "required": ["members"]
17 | }
18 |
--------------------------------------------------------------------------------
/spec/v0.3/core/set-rel.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json-schema.org/draft/2020-12/schema",
3 | "title": "@ocwg/rel/set",
4 | "description": "Set relation extension",
5 | "type": "object",
6 | "properties": {
7 | "members": {
8 | "type": "array",
9 | "description": "IDs of members of the set",
10 | "items": {
11 | "description": "node ID or relation ID",
12 | "type": "string"
13 | }
14 | }
15 | },
16 | "required": [
17 | "members"
18 | ]
19 | }
20 |
--------------------------------------------------------------------------------
/spec/v0.3/core/group-rel.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json-schema.org/draft/2020-12/schema",
3 | "title": "@ocwg/rel/group",
4 | "description": "Group relation extension",
5 | "type": "object",
6 | "properties": {
7 | "members": {
8 | "type": "array",
9 | "description": "IDs of members of the group",
10 | "items": {
11 | "description": "node ID or relation ID",
12 | "type": "string"
13 | }
14 | }
15 | },
16 | "required": [
17 | "members"
18 | ]
19 | }
20 |
--------------------------------------------------------------------------------
/example/cookbook-sticky-note.ocif.json:
--------------------------------------------------------------------------------
1 | {
2 | "ocif": "https://canvasprotocol.org/ocif/v0.6",
3 | "nodes": [
4 | {
5 | "id": "sticky-note-milk", "position": [ 100, 100 ], "size": [ 150, 75 ], "resource": "res_milk", "data": [
6 | {
7 | "type": "@ocif/node/rect", "fillColor": "#FFFF00"
8 | }
9 | ]
10 | }
11 | ], "resources": [
12 | {
13 | "id": "res_milk", "representations": [
14 | {
15 | "mimeType": "text/plain", "content": "Buy milk"
16 | }
17 | ]
18 | }
19 | ]
20 | }
21 |
--------------------------------------------------------------------------------
/spec/v0.1/schema/schemas.schema.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json-schema.org/draft/2020-12/schema",
3 | "$id": "schemas.schema.json",
4 | "title": "schema",
5 | "type": "object",
6 | "description": "inline definition of extended node schemas",
7 | "required": [],
8 | "propertyNames": {
9 | "pattern": "^@"
10 | },
11 | "patternProperties": {
12 | "^@": {
13 | "type": "object",
14 | "properties": {
15 | "$schema": { "type": "string" }
16 | },
17 | "required": ["$schema"]
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/spec/v0.5/extensions/ports-node.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json-schema.org/draft/2020-12/schema",
3 | "title": "@ocif/node/ports",
4 | "description": "Node ports extension",
5 | "type": "object",
6 | "properties": {
7 | "type": {
8 | "type": "string",
9 | "const": "@ocif/node/ports"
10 | },
11 | "ports": {
12 | "type": "array",
13 | "items": {
14 | "$comment": "Each item is an ID of a node",
15 | "type": "string"
16 | }
17 | }
18 | },
19 | "required": ["type", "ports"]
20 | }
21 |
--------------------------------------------------------------------------------
/spec/v0.6/extensions/ports-node.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json-schema.org/draft/2020-12/schema",
3 | "title": "@ocif/node/ports",
4 | "description": "Node ports extension",
5 | "type": "object",
6 | "properties": {
7 | "type": {
8 | "type": "string",
9 | "const": "@ocif/node/ports"
10 | },
11 | "ports": {
12 | "type": "array",
13 | "items": {
14 | "$comment": "Each item is an ID of a node",
15 | "type": "string"
16 | }
17 | }
18 | },
19 | "required": ["type", "ports"]
20 | }
21 |
--------------------------------------------------------------------------------
/spec/v0.6.1-draft/extensions/ports-node.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json-schema.org/draft/2020-12/schema",
3 | "title": "@ocif/node/ports",
4 | "description": "Node ports extension",
5 | "type": "object",
6 | "properties": {
7 | "type": {
8 | "type": "string",
9 | "const": "@ocif/node/ports"
10 | },
11 | "ports": {
12 | "type": "array",
13 | "items": {
14 | "$comment": "Each item is an ID of a node",
15 | "type": "string"
16 | }
17 | }
18 | },
19 | "required": ["type", "ports"]
20 | }
21 |
--------------------------------------------------------------------------------
/spec/v0.3/core/oval-node.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json-schema.org/draft/2020-12/schema",
3 | "title": "@ocwg/node/oval",
4 | "description": "Oval node extension",
5 | "type": "object",
6 | "properties": {
7 | "strokeWidth": {
8 | "type": "number",
9 | "description": "The line width."
10 | },
11 | "strokeColor": {
12 | "type": "string",
13 | "description": "The color of the stroke."
14 | },
15 | "fillColor": {
16 | "type": "string",
17 | "description": "The color of the fill."
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/spec/v0.4/core/oval-node.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json-schema.org/draft/2020-12/schema",
3 | "title": "@ocif/node/oval",
4 | "description": "Oval node extension",
5 | "type": "object",
6 | "properties": {
7 | "strokeWidth": {
8 | "type": "number",
9 | "description": "The line width."
10 | },
11 | "strokeColor": {
12 | "type": "string",
13 | "description": "The color of the stroke."
14 | },
15 | "fillColor": {
16 | "type": "string",
17 | "description": "The color of the fill."
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/spec/v0.3/core/rect-node.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json-schema.org/draft/2020-12/schema",
3 | "title": "@ocwg/node/rect",
4 | "description": "Rectangle node extension",
5 | "type": "object",
6 | "properties": {
7 | "strokeWidth": {
8 | "type": "number",
9 | "description": "The line width."
10 | },
11 | "strokeColor": {
12 | "type": "string",
13 | "description": "The color of the stroke."
14 | },
15 | "fillColor": {
16 | "type": "string",
17 | "description": "The color of the fill."
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/spec/v0.4/core/rect-node.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json-schema.org/draft/2020-12/schema",
3 | "title": "@ocif/node/rect",
4 | "description": "Rectangle node extension",
5 | "type": "object",
6 | "properties": {
7 | "strokeWidth": {
8 | "type": "number",
9 | "description": "The line width."
10 | },
11 | "strokeColor": {
12 | "type": "string",
13 | "description": "The color of the stroke."
14 | },
15 | "fillColor": {
16 | "type": "string",
17 | "description": "The color of the fill."
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/.github/workflows/static_checks.yml:
--------------------------------------------------------------------------------
1 | name: 📊 Static Checks
2 | on: [push, pull_request]
3 |
4 | concurrency:
5 | group: ci-${{ github.actor }}-${{ github.head_ref || github.run_number }}-${{ github.ref }}-static
6 |
7 | jobs:
8 | static-checks:
9 | name: Code style and file formatting
10 | runs-on: ubuntu-24.04
11 | steps:
12 | - name: Checkout
13 | uses: actions/checkout@v4
14 | with:
15 | fetch-depth: 2
16 |
17 | - name: Style checks via pre-commit
18 | uses: pre-commit/action@v3.0.1
19 | with:
20 | extra_args: --all-files
21 |
--------------------------------------------------------------------------------
/spec/v0.6/extensions/page-node.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json-schema.org/draft/2020-12/schema",
3 | "title": "@ocif/node/page",
4 | "description": "Page node extension. Gives nodes page numbers and/or a page label.",
5 | "type": "object",
6 | "properties": {
7 | "type": {
8 | "type": "string",
9 | "const": "@ocif/node/page"
10 | },
11 | "pageNumber": {
12 | "type": "number",
13 | "description": "The page number."
14 | },
15 | "label": {
16 | "type": "string",
17 | "description": "A page label."
18 | }
19 | },
20 | "required": ["type"]
21 | }
22 |
--------------------------------------------------------------------------------
/example/circle-node.json:
--------------------------------------------------------------------------------
1 | {
2 | "ocif": "https://canvasprotocol.org/ocif/v0.5",
3 | "nodes": [
4 | {
5 | "id": "n1",
6 | "position": [100, 100],
7 | "size": [50, 50],
8 | "resource": "r1",
9 | "data": [
10 | {
11 | "type": "@ocif/node/oval"
12 | }
13 | ]
14 | }
15 | ],
16 | "resources": [
17 | {
18 | "id": "r1",
19 | "representations": [
20 | {
21 | "mime-type": "text/plain",
22 | "content": "Hello, World!"
23 | }
24 | ]
25 | }
26 | ],
27 | "relations": [],
28 | "schemas": []
29 | }
30 |
--------------------------------------------------------------------------------
/spec/v0.6.1-draft/extensions/page-node.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json-schema.org/draft/2020-12/schema",
3 | "title": "@ocif/node/page",
4 | "description": "Page node extension. Gives nodes page numbers and/or a page label.",
5 | "type": "object",
6 | "properties": {
7 | "type": {
8 | "type": "string",
9 | "const": "@ocif/node/page"
10 | },
11 | "pageNumber": {
12 | "type": "number",
13 | "description": "The page number."
14 | },
15 | "label": {
16 | "type": "string",
17 | "description": "A page label."
18 | }
19 | },
20 | "required": ["type"]
21 | }
22 |
--------------------------------------------------------------------------------
/spec/v0.5/core/group-rel.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json-schema.org/draft/2020-12/schema",
3 | "title": "@ocif/rel/group",
4 | "description": "Group relation extension",
5 | "type": "object",
6 | "properties": {
7 | "members": {
8 | "type": "array",
9 | "description": "IDs of members of the group",
10 | "items": {
11 | "description": "node ID or relation ID",
12 | "type": "string"
13 | }
14 | },
15 | "cascadeDelete": {
16 | "type": "boolean",
17 | "description": "If true, deleting the group will delete all members. Default is true.",
18 | "default": true
19 | }
20 | },
21 | "required": ["members"]
22 | }
23 |
--------------------------------------------------------------------------------
/spec/v0.5/core/oval-node.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json-schema.org/draft/2020-12/schema",
3 | "title": "@ocif/node/oval",
4 | "description": "Oval node extension",
5 | "type": "object",
6 | "properties": {
7 | "type": {
8 | "type": "string",
9 | "const": "@ocif/node/oval"
10 | },
11 | "strokeWidth": {
12 | "type": "number",
13 | "description": "The line width."
14 | },
15 | "strokeColor": {
16 | "type": "string",
17 | "description": "The color of the stroke."
18 | },
19 | "fillColor": {
20 | "type": "string",
21 | "description": "The color of the fill."
22 | }
23 | },
24 | "required": ["type"]
25 | }
26 |
--------------------------------------------------------------------------------
/spec/v0.5/core/rect-node.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json-schema.org/draft/2020-12/schema",
3 | "title": "@ocif/node/rect",
4 | "description": "Rectangle node extension",
5 | "type": "object",
6 | "properties": {
7 | "type": {
8 | "type": "string",
9 | "const": "@ocif/node/rect"
10 | },
11 | "strokeWidth": {
12 | "type": "number",
13 | "description": "The line width."
14 | },
15 | "strokeColor": {
16 | "type": "string",
17 | "description": "The color of the stroke."
18 | },
19 | "fillColor": {
20 | "type": "string",
21 | "description": "The color of the fill."
22 | }
23 | },
24 | "required": ["type"]
25 | }
26 |
--------------------------------------------------------------------------------
/spec/v0.6/extensions/oval-node.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json-schema.org/draft/2020-12/schema",
3 | "title": "@ocif/node/oval",
4 | "description": "Oval node extension",
5 | "type": "object",
6 | "properties": {
7 | "type": {
8 | "type": "string",
9 | "const": "@ocif/node/oval"
10 | },
11 | "strokeWidth": {
12 | "type": "number",
13 | "description": "The line width."
14 | },
15 | "strokeColor": {
16 | "type": "string",
17 | "description": "The color of the stroke."
18 | },
19 | "fillColor": {
20 | "type": "string",
21 | "description": "The color of the fill."
22 | }
23 | },
24 | "required": ["type"]
25 | }
26 |
--------------------------------------------------------------------------------
/spec/v0.6/extensions/rect-node.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json-schema.org/draft/2020-12/schema",
3 | "title": "@ocif/node/rect",
4 | "description": "Rectangle node extension",
5 | "type": "object",
6 | "properties": {
7 | "type": {
8 | "type": "string",
9 | "const": "@ocif/node/rect"
10 | },
11 | "strokeWidth": {
12 | "type": "number",
13 | "description": "The line width."
14 | },
15 | "strokeColor": {
16 | "type": "string",
17 | "description": "The color of the stroke."
18 | },
19 | "fillColor": {
20 | "type": "string",
21 | "description": "The color of the fill."
22 | }
23 | },
24 | "required": ["type"]
25 | }
26 |
--------------------------------------------------------------------------------
/spec/v0.6.1-draft/extensions/oval-node.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json-schema.org/draft/2020-12/schema",
3 | "title": "@ocif/node/oval",
4 | "description": "Oval node extension",
5 | "type": "object",
6 | "properties": {
7 | "type": {
8 | "type": "string",
9 | "const": "@ocif/node/oval"
10 | },
11 | "strokeWidth": {
12 | "type": "number",
13 | "description": "The line width."
14 | },
15 | "strokeColor": {
16 | "type": "string",
17 | "description": "The color of the stroke."
18 | },
19 | "fillColor": {
20 | "type": "string",
21 | "description": "The color of the fill."
22 | }
23 | },
24 | "required": ["type"]
25 | }
26 |
--------------------------------------------------------------------------------
/spec/v0.6.1-draft/extensions/rect-node.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json-schema.org/draft/2020-12/schema",
3 | "title": "@ocif/node/rect",
4 | "description": "Rectangle node extension",
5 | "type": "object",
6 | "properties": {
7 | "type": {
8 | "type": "string",
9 | "const": "@ocif/node/rect"
10 | },
11 | "strokeWidth": {
12 | "type": "number",
13 | "description": "The line width."
14 | },
15 | "strokeColor": {
16 | "type": "string",
17 | "description": "The color of the stroke."
18 | },
19 | "fillColor": {
20 | "type": "string",
21 | "description": "The color of the fill."
22 | }
23 | },
24 | "required": ["type"]
25 | }
26 |
--------------------------------------------------------------------------------
/spec/v0.4/core/path-node.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json-schema.org/draft/2020-12/schema",
3 | "title": "@ocif/node/path",
4 | "description": "Path node extension",
5 | "type": "object",
6 | "properties": {
7 | "strokeWidth": {
8 | "type": "number",
9 | "description": "The line width."
10 | },
11 | "strokeColor": {
12 | "type": "string",
13 | "description": "The color of the stroke."
14 | },
15 | "fillColor": {
16 | "type": "string",
17 | "description": "The color of the fill."
18 | },
19 | "path": {
20 | "type": "string",
21 | "description": "The path data in SVG 'd' attribute syntax."
22 | }
23 | },
24 | "required": ["path"]
25 | }
26 |
--------------------------------------------------------------------------------
/spec/v0.5/core/path-node.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json-schema.org/draft/2020-12/schema",
3 | "title": "@ocif/node/path",
4 | "description": "Path node extension",
5 | "type": "object",
6 | "properties": {
7 | "strokeWidth": {
8 | "type": "number",
9 | "description": "The line width."
10 | },
11 | "strokeColor": {
12 | "type": "string",
13 | "description": "The color of the stroke."
14 | },
15 | "fillColor": {
16 | "type": "string",
17 | "description": "The color of the fill."
18 | },
19 | "path": {
20 | "type": "string",
21 | "description": "The path data in SVG 'd' attribute syntax."
22 | }
23 | },
24 | "required": ["path"]
25 | }
26 |
--------------------------------------------------------------------------------
/spec/v0.3/core/path-node.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json-schema.org/draft/2020-12/schema",
3 | "title": "@ocwg/node/path",
4 | "description": "Path node extension",
5 | "type": "object",
6 | "properties": {
7 | "strokeWidth": {
8 | "type": "number",
9 | "description": "The line width."
10 | },
11 | "strokeColor": {
12 | "type": "string",
13 | "description": "The color of the stroke."
14 | },
15 | "fillColor": {
16 | "type": "string",
17 | "description": "The color of the fill."
18 | },
19 | "path": {
20 | "type": "string",
21 | "description": "The path data in SVG 'd' attribute syntax."
22 | }
23 | },
24 | "required": [
25 | "path"
26 | ]
27 | }
28 |
--------------------------------------------------------------------------------
/public/_redirects:
--------------------------------------------------------------------------------
1 | # Redirect root to latest version of spec
2 | / https://github.com/ocwg/spec/blob/main/spec/v0.6/spec.md 302
3 |
4 | # Version-specific spec and schema files
5 | /:version https://github.com/ocwg/spec/blob/main/spec/:version/spec.md 301
6 | /:version/spec.md https://github.com/ocwg/spec/blob/main/spec/:version/spec.md 301
7 | /:version/schema.json https://github.com/ocwg/spec/blob/main/spec/:version/schema.json 301
8 | /:version/extensions.md https://github.com/ocwg/spec/blob/main/spec/:version/extensions.md 301
9 |
10 | # Core extension URIs
11 | /:version/core/:extension.json https://github.com/ocwg/spec/blob/main/spec/:version/core/:extension.json 301
12 |
13 | # Other extension URIs
14 | /:version/extensions/:extension.json https://github.com/ocwg/spec/blob/main/spec/:version/extensions/:extension.json 301
15 |
--------------------------------------------------------------------------------
/spec/v0.6/extensions/path-node.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json-schema.org/draft/2020-12/schema",
3 | "title": "@ocif/node/path",
4 | "description": "Path node extension",
5 | "type": "object",
6 | "properties": {
7 | "type": {
8 | "type": "string",
9 | "const": "@ocif/node/path"
10 | },
11 | "strokeWidth": {
12 | "type": "number",
13 | "description": "The line width."
14 | },
15 | "strokeColor": {
16 | "type": "string",
17 | "description": "The color of the stroke."
18 | },
19 | "fillColor": {
20 | "type": "string",
21 | "description": "The color of the fill."
22 | },
23 | "path": {
24 | "type": "string",
25 | "description": "The path data in SVG 'd' attribute syntax."
26 | }
27 | },
28 | "required": ["path"]
29 | }
30 |
--------------------------------------------------------------------------------
/spec/v0.6.1-draft/extensions/path-node.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json-schema.org/draft/2020-12/schema",
3 | "title": "@ocif/node/path",
4 | "description": "Path node extension",
5 | "type": "object",
6 | "properties": {
7 | "type": {
8 | "type": "string",
9 | "const": "@ocif/node/path"
10 | },
11 | "strokeWidth": {
12 | "type": "number",
13 | "description": "The line width."
14 | },
15 | "strokeColor": {
16 | "type": "string",
17 | "description": "The color of the stroke."
18 | },
19 | "fillColor": {
20 | "type": "string",
21 | "description": "The color of the fill."
22 | },
23 | "path": {
24 | "type": "string",
25 | "description": "The path data in SVG 'd' attribute syntax."
26 | }
27 | },
28 | "required": ["path"]
29 | }
30 |
--------------------------------------------------------------------------------
/spec/v0.6/extensions/group-rel.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json-schema.org/draft/2020-12/schema",
3 | "title": "@ocif/rel/group",
4 | "description": "Group relation extension",
5 | "type": "object",
6 | "properties": {
7 | "type": {
8 | "type": "string",
9 | "const": "@ocif/rel/group",
10 | "description": "Identifies the group relation extension."
11 | },
12 | "members": {
13 | "type": "array",
14 | "description": "IDs of members of the group",
15 | "items": {
16 | "description": "node ID or relation ID",
17 | "type": "string"
18 | }
19 | },
20 | "cascadeDelete": {
21 | "type": "boolean",
22 | "description": "If true, deleting the group will delete all members. Default is true.",
23 | "default": true
24 | }
25 | },
26 | "required": ["type", "members"]
27 | }
28 |
--------------------------------------------------------------------------------
/spec/v0.6.1-draft/extensions/group-rel.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json-schema.org/draft/2020-12/schema",
3 | "title": "@ocif/rel/group",
4 | "description": "Group relation extension",
5 | "type": "object",
6 | "properties": {
7 | "type": {
8 | "type": "string",
9 | "const": "@ocif/rel/group",
10 | "description": "Identifies the group relation extension."
11 | },
12 | "members": {
13 | "type": "array",
14 | "description": "IDs of members of the group",
15 | "items": {
16 | "description": "node ID or relation ID",
17 | "type": "string"
18 | }
19 | },
20 | "cascadeDelete": {
21 | "type": "boolean",
22 | "description": "If true, deleting the group will delete all members. Default is true.",
23 | "default": true
24 | }
25 | },
26 | "required": ["type", "members"]
27 | }
28 |
--------------------------------------------------------------------------------
/spec/v0.4/extensions/relative-node.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json-schema.org/draft/2020-12/schema",
3 | "title": "@ocif/node/relative",
4 | "description": "Relative constraints for nodes",
5 | "type": "object",
6 | "properties": {
7 | "source": {
8 | "type": "string",
9 | "description": "ID of the source node. The ID of the source node, which is used as a reference for the relative positioning."
10 | },
11 | "position": {
12 | "type": "array",
13 | "items": {
14 | "type": "number"
15 | },
16 | "description": "Relative position of the target. The numbers given in the array are vector-added to the position of the source node."
17 | },
18 | "rotation": {
19 | "type": "number",
20 | "description": "Relative angle of the target. The rotation is added to the rotation of the source node."
21 | }
22 | },
23 | "required": ["source"]
24 | }
25 |
--------------------------------------------------------------------------------
/spec/v0.6/extensions/viewport-canvas.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json-schema.org/draft/2020-12/schema",
3 | "title": "@ocif/canvas/viewport",
4 | "description": "A viewport is a rectangle that defines at what part of a canvas the app should initially pan and zoom.",
5 | "type": "object",
6 | "properties": {
7 | "position": {
8 | "description": "The top-left corner of the viewport.",
9 | "type": "array",
10 | "items": {
11 | "type": "number"
12 | },
13 | "minItems": 2,
14 | "maxItems": 3,
15 | "default": [0, 0]
16 | },
17 | "size": {
18 | "description": "The width and height (in 3D: also depth) of the viewport.",
19 | "type": "array",
20 | "items": {
21 | "type": "number"
22 | },
23 | "minItems": 2,
24 | "maxItems": 3,
25 | "default": [100, 100]
26 | }
27 | },
28 | "required": ["position", "size"]
29 | }
30 |
--------------------------------------------------------------------------------
/spec/v0.3/extensions/relative-node.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json-schema.org/draft/2020-12/schema",
3 | "title": "@ocwg/node/relative",
4 | "description": "Relative constraints for nodes",
5 | "type": "object",
6 | "properties": {
7 | "source": {
8 | "type": "string",
9 | "description": "ID of the source node. The ID of the source node, which is used as a reference for the relative positioning."
10 | },
11 | "position": {
12 | "type": "array",
13 | "items": {
14 | "type": "number"
15 | },
16 | "description": "Relative position of the target. The numbers given in the array are vector-added to the position of the source node."
17 | },
18 | "rotation": {
19 | "type": "number",
20 | "description": "Relative angle of the target. The rotation is added to the rotation of the source node."
21 | }
22 | },
23 | "required": [
24 | "source"
25 | ]
26 | }
27 |
--------------------------------------------------------------------------------
/spec/v0.6.1-draft/extensions/viewport-canvas.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json-schema.org/draft/2020-12/schema",
3 | "title": "@ocif/canvas/viewport",
4 | "description": "A viewport is a rectangle that defines at what part of a canvas the app should initially pan and zoom.",
5 | "type": "object",
6 | "properties": {
7 | "position": {
8 | "description": "The top-left corner of the viewport.",
9 | "type": "array",
10 | "items": {
11 | "type": "number"
12 | },
13 | "minItems": 2,
14 | "maxItems": 3,
15 | "default": [0, 0]
16 | },
17 | "size": {
18 | "description": "The width and height (in 3D: also depth) of the viewport.",
19 | "type": "array",
20 | "items": {
21 | "type": "number"
22 | },
23 | "minItems": 2,
24 | "maxItems": 3,
25 | "default": [100, 100]
26 | }
27 | },
28 | "required": ["position", "size"]
29 | }
30 |
--------------------------------------------------------------------------------
/spec/v0.4/core/edge-rel.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json-schema.org/draft/2020-12/schema",
3 | "title": "@ocif/rel/edge",
4 | "description": "Edge relation extension",
5 | "type": "object",
6 | "properties": {
7 | "start": {
8 | "type": "string",
9 | "description": "ID of start (source) element."
10 | },
11 | "end": {
12 | "type": "string",
13 | "description": "ID of end (target) element."
14 | },
15 | "directed": {
16 | "type": "boolean",
17 | "description": "Is the edge directed?",
18 | "default": true
19 | },
20 | "rel": {
21 | "type": "string",
22 | "description": "Represented relation type. Can be a URI to represent the predicate of an RDF triple."
23 | },
24 | "node": {
25 | "type": "string",
26 | "description": "The ID of a node that visually represents the edge."
27 | }
28 | },
29 | "required": ["start", "end"]
30 | }
31 |
--------------------------------------------------------------------------------
/spec/v0.5/core/edge-rel.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json-schema.org/draft/2020-12/schema",
3 | "title": "@ocif/rel/edge",
4 | "description": "Edge relation extension",
5 | "type": "object",
6 | "properties": {
7 | "start": {
8 | "type": "string",
9 | "description": "ID of start (source) element."
10 | },
11 | "end": {
12 | "type": "string",
13 | "description": "ID of end (target) element."
14 | },
15 | "directed": {
16 | "type": "boolean",
17 | "description": "Is the edge directed?",
18 | "default": true
19 | },
20 | "rel": {
21 | "type": "string",
22 | "description": "Represented relation type. Can be a URI to represent the predicate of an RDF triple."
23 | },
24 | "node": {
25 | "type": "string",
26 | "description": "The ID of a node that visually represents the edge."
27 | }
28 | },
29 | "required": ["start", "end"]
30 | }
31 |
--------------------------------------------------------------------------------
/spec/v0.3/core/edge-rel.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json-schema.org/draft/2020-12/schema",
3 | "title": "@ocwg/rel/edge",
4 | "description": "Edge relation extension",
5 | "type": "object",
6 | "properties": {
7 | "start": {
8 | "type": "string",
9 | "description": "ID of start (source) element."
10 | },
11 | "end": {
12 | "type": "string",
13 | "description": "ID of end (target) element."
14 | },
15 | "directed": {
16 | "type": "boolean",
17 | "description": "Is the edge directed?",
18 | "default": true
19 | },
20 | "rel": {
21 | "type": "string",
22 | "description": "Represented relation type. Can be a URI to represent the predicate of an RDF triple."
23 | },
24 | "node": {
25 | "type": "string",
26 | "description": "The ID of a node that visually represents the edge."
27 | }
28 | },
29 | "required": [
30 | "start", "end"
31 | ]
32 | }
33 |
--------------------------------------------------------------------------------
/spec/v0.1/schema/open-canvas.schema.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json-schema.org/draft/2020-12/schema",
3 | "$id": "open-canvas.schema.json",
4 | "title": "Open Canvas",
5 | "type": "object",
6 | "description": "The root object for a open-canvas file",
7 | "required": ["schema_version", "nodes"],
8 | "properties": {
9 | "schema_version": {
10 | "type": "string"
11 | },
12 | "nodes": {
13 | "type": "array",
14 | "description": "A list of nodes.",
15 | "items": {
16 | "$ref": "node.schema.json"
17 | }
18 | },
19 | "relations": {
20 | "type": "array",
21 | "description": "A list of relations.",
22 | "items": {
23 | "$ref": "relation.schema.json"
24 | }
25 | },
26 | "schemas": {
27 | "type": "object",
28 | "description": "A dictionary of extended schemas",
29 | "$ref": "schemas.schema.json"
30 | },
31 | "resources": {
32 | "type": "object",
33 | "description": "A dictionary of resources which can be referenced by nodes and relations."
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/spec/v0.6/extensions/edge-rel.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json-schema.org/draft/2020-12/schema",
3 | "title": "@ocif/rel/edge",
4 | "description": "Edge relation extension",
5 | "type": "object",
6 | "properties": {
7 | "type": {
8 | "type": "string",
9 | "const": "@ocif/rel/edge",
10 | "description": "Identifies the edge relation extension."
11 | },
12 | "start": {
13 | "type": "string",
14 | "description": "ID of start (source) element."
15 | },
16 | "end": {
17 | "type": "string",
18 | "description": "ID of end (target) element."
19 | },
20 | "directed": {
21 | "type": "boolean",
22 | "description": "Is the edge directed?",
23 | "default": true
24 | },
25 | "rel": {
26 | "type": "string",
27 | "description": "Represented relation type. Can be a URI to represent the predicate of an RDF triple."
28 | },
29 | "node": {
30 | "type": "string",
31 | "description": "The ID of a node that visually represents the edge."
32 | }
33 | },
34 | "required": ["type", "start", "end"]
35 | }
36 |
--------------------------------------------------------------------------------
/spec/v0.6.1-draft/extensions/edge-rel.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json-schema.org/draft/2020-12/schema",
3 | "title": "@ocif/rel/edge",
4 | "description": "Edge relation extension",
5 | "type": "object",
6 | "properties": {
7 | "type": {
8 | "type": "string",
9 | "const": "@ocif/rel/edge",
10 | "description": "Identifies the edge relation extension."
11 | },
12 | "start": {
13 | "type": "string",
14 | "description": "ID of start (source) element."
15 | },
16 | "end": {
17 | "type": "string",
18 | "description": "ID of end (target) element."
19 | },
20 | "directed": {
21 | "type": "boolean",
22 | "description": "Is the edge directed?",
23 | "default": true
24 | },
25 | "rel": {
26 | "type": "string",
27 | "description": "Represented relation type. Can be a URI to represent the predicate of an RDF triple."
28 | },
29 | "node": {
30 | "type": "string",
31 | "description": "The ID of a node that visually represents the edge."
32 | }
33 | },
34 | "required": ["type", "start", "end"]
35 | }
36 |
--------------------------------------------------------------------------------
/spec/v0.3/extensions/parent-child-rel.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json-schema.org/draft/2020-12/schema",
3 | "title": "@ocwg/rel/parent-child",
4 | "description": "Parent-child relationship extension. A parent-child relation models a hierarchical relationship between nodes. It can be used to model inheritance, containment, or other hierarchical relationships.",
5 | "type": "object",
6 | "properties": {
7 | "parent": {
8 | "type": "string",
9 | "description": "ID of the parent node. There SHOULD be only one parent per child."
10 | },
11 | "child": {
12 | "type": "string",
13 | "description": "ID of the child node. A parent can have multiple children (expressed my multiple parent-child relations)."
14 | },
15 | "inherit": {
16 | "type": "boolean",
17 | "description": "Inherit properties. A boolean flag indicating if the child should inherit properties from the parent. Default is false. The exact semantics of inheritance are defined by the application. In general, when looking for JSON properties of a node and finding them undefined, an app should look for the same value in the parent node. This chain of parents should be followed until root is reached or a cycle is detected."
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/spec/v0.4/extensions/parent-child-rel.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json-schema.org/draft/2020-12/schema",
3 | "title": "@ocif/rel/parent-child",
4 | "description": "Parent-child relationship extension. A parent-child relation models a hierarchical relationship between nodes. It can be used to model inheritance, containment, or other hierarchical relationships.",
5 | "type": "object",
6 | "properties": {
7 | "parent": {
8 | "type": "string",
9 | "description": "ID of the parent node. There SHOULD be only one parent per child."
10 | },
11 | "child": {
12 | "type": "string",
13 | "description": "ID of the child node. A parent can have multiple children (expressed my multiple parent-child relations)."
14 | },
15 | "inherit": {
16 | "type": "boolean",
17 | "description": "Inherit properties. A boolean flag indicating if the child should inherit properties from the parent. Default is false. The exact semantics of inheritance are defined by the application. In general, when looking for JSON properties of a node and finding them undefined, an app should look for the same value in the parent node. This chain of parents should be followed until root is reached or a cycle is detected."
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/.github/workflows/file_format.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 |
4 | import sys
5 |
6 | if len(sys.argv) < 2:
7 | print("Invalid usage of file_format.py, it should be called with a path to one or multiple files.")
8 | sys.exit(1)
9 |
10 | BOM = b"\xef\xbb\xbf"
11 |
12 | changed = []
13 | invalid = []
14 |
15 | for file in sys.argv[1:]:
16 | try:
17 | with open(file, "rt", encoding="utf-8") as f:
18 | original = f.read()
19 | except UnicodeDecodeError:
20 | invalid.append(file)
21 | continue
22 |
23 | if original == "":
24 | continue
25 |
26 | EOL = "\n"
27 |
28 | revamp = EOL.join([line.rstrip("\n\r\t ") for line in original.splitlines(True)]).rstrip(EOL) + EOL
29 |
30 | new_raw = revamp.encode(encoding="utf-8")
31 | if new_raw.startswith(BOM):
32 | new_raw = new_raw[len(BOM) :]
33 |
34 | with open(file, "rb") as f:
35 | old_raw = f.read()
36 |
37 | if old_raw != new_raw:
38 | changed.append(file)
39 | with open(file, "wb") as f:
40 | f.write(new_raw)
41 |
42 | if changed:
43 | for file in changed:
44 | print(f"FIXED: {file}")
45 |
46 | if invalid:
47 | for file in invalid:
48 | print(f"REQUIRES MANUAL CHANGES: {file}")
49 | sys.exit(1)
50 |
--------------------------------------------------------------------------------
/spec/v0.5/extensions/transforms-node.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json-schema.org/draft/2020-12/schema",
3 | "title": "@ocif/node/transforms",
4 | "description": "Geometric transforms for nodes",
5 | "type": "object",
6 | "properties": {
7 | "type": {
8 | "type": "string",
9 | "const": "@ocif/node/transforms"
10 | },
11 | "scale": {
12 | "type": "array",
13 | "items": {
14 | "type": "number"
15 | },
16 | "description": "Scale factor for the node. The array contains the scale factor for each dimension."
17 | },
18 | "offset": {
19 | "type": "array",
20 | "items": {
21 | "type": "number"
22 | },
23 | "description": "Relative position of the child node relative to the parent node. The numbers given in the array are vector-added to the position of the source node."
24 | },
25 | "rotation": {
26 | "type": "number",
27 | "description": "Relative angle in degrees. The rotation is added to the rotation of parent coordinate system."
28 | },
29 | "rotationAxis": {
30 | "type": "array",
31 | "items": {
32 | "type": "number"
33 | },
34 | "minItems": 3,
35 | "maxItems": 3,
36 | "description": "Rotation axis for 3D rotation. Default is [0,0,1] (z-axis).",
37 | "default": [0, 0, 1]
38 | }
39 | },
40 | "required": ["type"]
41 | }
42 |
--------------------------------------------------------------------------------
/spec/v0.6/extensions/transforms-node.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json-schema.org/draft/2020-12/schema",
3 | "title": "@ocif/node/transforms",
4 | "description": "Geometric transforms for nodes",
5 | "type": "object",
6 | "properties": {
7 | "type": {
8 | "type": "string",
9 | "const": "@ocif/node/transforms"
10 | },
11 | "scale": {
12 | "type": "array",
13 | "items": {
14 | "type": "number"
15 | },
16 | "description": "Scale factor for the node. The array contains the scale factor for each dimension."
17 | },
18 | "offset": {
19 | "type": "array",
20 | "items": {
21 | "type": "number"
22 | },
23 | "description": "Relative position of the child node relative to the parent node. The numbers given in the array are vector-added to the position of the source node."
24 | },
25 | "rotation": {
26 | "type": "number",
27 | "description": "Relative angle in degrees. The rotation is added to the rotation of parent coordinate system."
28 | },
29 | "rotationAxis": {
30 | "type": "array",
31 | "items": {
32 | "type": "number"
33 | },
34 | "minItems": 3,
35 | "maxItems": 3,
36 | "description": "Rotation axis for 3D rotation. Default is [0,0,1] (z-axis).",
37 | "default": [0, 0, 1]
38 | }
39 | },
40 | "required": ["type"]
41 | }
42 |
--------------------------------------------------------------------------------
/spec/v0.1/schema/node.schema.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json-schema.org/draft/2020-12/schema",
3 | "$id": "node.schema.json",
4 | "title": "node",
5 | "type": "object",
6 | "description": "A node in the node list.",
7 | "required": ["id", "position"],
8 | "properties": {
9 | "id": { "type": "string" },
10 | "position": {
11 | "type": "array",
12 | "items": {
13 | "anyOf": [
14 | {
15 | "type": "number"
16 | }
17 | ]
18 | },
19 | "minItems": 2,
20 | "maxItems": 3
21 | },
22 | "size": {
23 | "type": "array",
24 | "items": {
25 | "anyOf": [
26 | {
27 | "type": "number"
28 | }
29 | ]
30 | },
31 | "minItems": 2,
32 | "maxItems": 3
33 | },
34 | "rotation": { "type": "number" },
35 | "properties": {
36 | "type": "array",
37 | "contains": {
38 | "type": "object",
39 | "properties": {
40 | "schema": {
41 | "type": "string"
42 | },
43 | "schema_version": {
44 | "type": "string"
45 | }
46 | },
47 | "required": ["schema", "schema_version"]
48 | },
49 | "items": {
50 | "anyOf": [
51 | {
52 | "type": "object"
53 | }
54 | ]
55 | }
56 | }
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/spec/v0.6.1-draft/extensions/transforms-node.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json-schema.org/draft/2020-12/schema",
3 | "title": "@ocif/node/transforms",
4 | "description": "Geometric transforms for nodes",
5 | "type": "object",
6 | "properties": {
7 | "type": {
8 | "type": "string",
9 | "const": "@ocif/node/transforms"
10 | },
11 | "scale": {
12 | "type": "array",
13 | "items": {
14 | "type": "number"
15 | },
16 | "description": "Scale factor for the node. The array contains the scale factor for each dimension."
17 | },
18 | "offset": {
19 | "type": "array",
20 | "items": {
21 | "type": "number"
22 | },
23 | "description": "Relative position of the child node relative to the parent node. The numbers given in the array are vector-added to the position of the source node."
24 | },
25 | "rotation": {
26 | "type": "number",
27 | "description": "Relative angle in degrees. The rotation is added to the rotation of parent coordinate system."
28 | },
29 | "rotationAxis": {
30 | "type": "array",
31 | "items": {
32 | "type": "number"
33 | },
34 | "minItems": 3,
35 | "maxItems": 3,
36 | "description": "Rotation axis for 3D rotation. Default is [0,0,1] (z-axis).",
37 | "default": [0, 0, 1]
38 | }
39 | },
40 | "required": ["type"]
41 | }
42 |
--------------------------------------------------------------------------------
/spec/v0.5/extensions/textstyle-node.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json-schema.org/draft/2020-12/schema",
3 | "$id": "https://spec.canvasprotocol.org/v0.5/extensions/textstyle-node.json",
4 | "title": "@ocif/node/textstyle",
5 | "description": "Text style extension for rendering plain text and structured text",
6 | "type": "object",
7 | "properties": {
8 | "type": {
9 | "type": "string",
10 | "const": "@ocif/node/textstyle"
11 | },
12 | "fontSizePx": {
13 | "description": "Font size in px, as used in CSS",
14 | "type": "number",
15 | "default": 12
16 | },
17 | "fontFamily": {
18 | "description": "Font family, as used in CSS",
19 | "type": "string",
20 | "default": "sans-serif"
21 | },
22 | "color": {
23 | "description": "Text color",
24 | "type": "string",
25 | "pattern": "^#[0-9a-fA-F]{6}([0-9a-fA-F]{2})?$",
26 | "default": "#000000"
27 | },
28 | "align": {
29 | "description": "Text alignment",
30 | "type": "string",
31 | "enum": ["left", "right", "center", "justify"],
32 | "default": "left"
33 | },
34 | "bold": {
35 | "description": "Bold text",
36 | "type": "boolean",
37 | "default": false
38 | },
39 | "italic": {
40 | "description": "Italic text",
41 | "type": "boolean",
42 | "default": false
43 | }
44 | },
45 | "required": ["type"]
46 | }
47 |
--------------------------------------------------------------------------------
/spec/v0.6/extensions/textstyle-node.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json-schema.org/draft/2020-12/schema",
3 | "$id": "https://spec.canvasprotocol.org/v0.6/extensions/textstyle-node.json",
4 | "title": "@ocif/node/textstyle",
5 | "description": "Text style extension for rendering plain text and structured text",
6 | "type": "object",
7 | "properties": {
8 | "type": {
9 | "type": "string",
10 | "const": "@ocif/node/textstyle"
11 | },
12 | "fontSizePx": {
13 | "description": "Font size in px, as used in CSS",
14 | "type": "number",
15 | "default": 12
16 | },
17 | "fontFamily": {
18 | "description": "Font family, as used in CSS",
19 | "type": "string",
20 | "default": "sans-serif"
21 | },
22 | "color": {
23 | "description": "Text color",
24 | "type": "string",
25 | "pattern": "^#[0-9a-fA-F]{6}([0-9a-fA-F]{2})?$",
26 | "default": "#000000"
27 | },
28 | "align": {
29 | "description": "Text alignment",
30 | "type": "string",
31 | "enum": ["left", "right", "center", "justify"],
32 | "default": "left"
33 | },
34 | "bold": {
35 | "description": "Bold text",
36 | "type": "boolean",
37 | "default": false
38 | },
39 | "italic": {
40 | "description": "Italic text",
41 | "type": "boolean",
42 | "default": false
43 | }
44 | },
45 | "required": ["type"]
46 | }
47 |
--------------------------------------------------------------------------------
/spec/v0.6.1-draft/extensions/textstyle-node.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json-schema.org/draft/2020-12/schema",
3 | "$id": "https://spec.canvasprotocol.org/v0.6.1/extensions/textstyle-node.json",
4 | "title": "@ocif/node/textstyle",
5 | "description": "Text style extension for rendering plain text and structured text",
6 | "type": "object",
7 | "properties": {
8 | "type": {
9 | "type": "string",
10 | "const": "@ocif/node/textstyle"
11 | },
12 | "fontSizePx": {
13 | "description": "Font size in px, as used in CSS",
14 | "type": "number",
15 | "default": 12
16 | },
17 | "fontFamily": {
18 | "description": "Font family, as used in CSS",
19 | "type": "string",
20 | "default": "sans-serif"
21 | },
22 | "color": {
23 | "description": "Text color",
24 | "type": "string",
25 | "pattern": "^#[0-9a-fA-F]{6}([0-9a-fA-F]{2})?$",
26 | "default": "#000000"
27 | },
28 | "align": {
29 | "description": "Text alignment",
30 | "type": "string",
31 | "enum": ["left", "right", "center", "justify"],
32 | "default": "left"
33 | },
34 | "bold": {
35 | "description": "Bold text",
36 | "type": "boolean",
37 | "default": false
38 | },
39 | "italic": {
40 | "description": "Italic text",
41 | "type": "boolean",
42 | "default": false
43 | }
44 | },
45 | "required": ["type"]
46 | }
47 |
--------------------------------------------------------------------------------
/spec/v0.4/extensions/hyperedge-rel.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json-schema.org/draft/2020-12/schema",
3 | "title": "@ocif/rel/hyperedge",
4 | "description": "Hyperedge relation extension",
5 | "type": "object",
6 | "properties": {
7 | "endpoints": {
8 | "type": "array",
9 | "items": {
10 | "type": "object",
11 | "properties": {
12 | "id": {
13 | "type": "string",
14 | "description": "ID of attached entity (node or relation)"
15 | },
16 | "direction": {
17 | "type": "string",
18 | "description": "Direction of the connection: 'in' (edge is going into the hyper-edge), 'out' (edge is going out from thy hyper-edge), 'undir' (edge is attached undirected). This is the default.",
19 | "enum": ["in", "out", "undir"]
20 | },
21 | "weight": {
22 | "type": "number",
23 | "description": "Weight of the edge"
24 | }
25 | },
26 | "required": ["id"]
27 | }
28 | },
29 | "weight": {
30 | "type": "number",
31 | "description": "Weight of the edge. A floating-point number, which can be used to model the strength of the connection, as a whole. More general than endpoint-specific weights, and often sufficient."
32 | },
33 | "rel": {
34 | "type": "string",
35 | "description": "Represented relation type"
36 | }
37 | },
38 | "required": ["members"]
39 | }
40 |
--------------------------------------------------------------------------------
/spec/v0.4/core/arrow-node.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json-schema.org/draft/2020-12/schema",
3 | "title": "@ocif/node/arrow",
4 | "description": "Arrow node extension",
5 | "type": "object",
6 | "properties": {
7 | "strokeWidth": {
8 | "type": "number",
9 | "description": "The line width."
10 | },
11 | "strokeColor": {
12 | "type": "string",
13 | "description": "The color of the stroke."
14 | },
15 | "fillColor": {
16 | "type": "string",
17 | "description": "The color of the fill."
18 | },
19 | "start": {
20 | "type": "array",
21 | "items": {
22 | "type": "number"
23 | },
24 | "minItems": 2,
25 | "maxItems": 2,
26 | "description": "The start point."
27 | },
28 | "end": {
29 | "type": "array",
30 | "items": {
31 | "type": "number"
32 | },
33 | "minItems": 2,
34 | "maxItems": 2,
35 | "description": "The end point."
36 | },
37 | "startMarker": {
38 | "type": "string",
39 | "enum": ["none", "arrowhead"],
40 | "description": "The marker at the start of the arrow."
41 | },
42 | "endMarker": {
43 | "type": "string",
44 | "enum": ["none", "arrowhead"],
45 | "description": "The marker at the end of the arrow."
46 | },
47 | "relation": {
48 | "type": "string",
49 | "description": "The ID of the relation defining the semantics of the arrow."
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/spec/v0.3/core/arrow-node.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json-schema.org/draft/2020-12/schema",
3 | "title": "@ocwg/node/arrow",
4 | "description": "Arrow node extension",
5 | "type": "object",
6 | "properties": {
7 | "strokeWidth": {
8 | "type": "number",
9 | "description": "The line width."
10 | },
11 | "strokeColor": {
12 | "type": "string",
13 | "description": "The color of the stroke."
14 | },
15 | "fillColor": {
16 | "type": "string",
17 | "description": "The color of the fill."
18 | },
19 | "start": {
20 | "type": "array",
21 | "items": {
22 | "type": "number"
23 | },
24 | "minItems": 2,
25 | "maxItems": 2,
26 | "description": "The start point."
27 | },
28 | "end": {
29 | "type": "array",
30 | "items": {
31 | "type": "number"
32 | },
33 | "minItems": 2,
34 | "maxItems": 2,
35 | "description": "The end point."
36 | },
37 | "startMarker": {
38 | "type": "string",
39 | "enum": [
40 | "none",
41 | "arrowhead"
42 | ],
43 | "description": "The marker at the start of the arrow."
44 | },
45 | "endMarker": {
46 | "type": "string",
47 | "enum": [
48 | "none",
49 | "arrowhead"
50 | ],
51 | "description": "The marker at the end of the arrow."
52 | },
53 | "relation" : {
54 | "type": "string",
55 | "description": "The ID of the relation defining the semantics of the arrow."
56 | }
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/spec/v0.5/extensions/hyperedge-rel.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json-schema.org/draft/2020-12/schema",
3 | "title": "@ocif/rel/hyperedge",
4 | "description": "Hyperedge relation extension",
5 | "type": "object",
6 | "properties": {
7 | "type": {
8 | "type": "string",
9 | "const": "@ocif/rel/hyperedge"
10 | },
11 | "endpoints": {
12 | "type": "array",
13 | "items": {
14 | "type": "object",
15 | "properties": {
16 | "id": {
17 | "type": "string",
18 | "description": "ID of attached entity (node or relation)"
19 | },
20 | "direction": {
21 | "type": "string",
22 | "description": "Direction of the connection: 'in' (edge is going into the hyper-edge), 'out' (edge is going out from thy hyper-edge), 'undir' (edge is attached undirected). This is the default.",
23 | "enum": ["in", "out", "undir"]
24 | },
25 | "weight": {
26 | "type": "number",
27 | "description": "Weight of the edge"
28 | }
29 | },
30 | "required": ["id"]
31 | }
32 | },
33 | "weight": {
34 | "type": "number",
35 | "description": "Weight of the edge. A floating-point number, which can be used to model the strength of the connection, as a whole. More general than endpoint-specific weights, and often sufficient."
36 | },
37 | "rel": {
38 | "type": "string",
39 | "description": "Represented relation type"
40 | }
41 | },
42 | "required": ["type", "endpoints"]
43 | }
44 |
--------------------------------------------------------------------------------
/spec/v0.6/extensions/hyperedge-rel.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json-schema.org/draft/2020-12/schema",
3 | "title": "@ocif/rel/hyperedge",
4 | "description": "Hyperedge relation extension",
5 | "type": "object",
6 | "properties": {
7 | "type": {
8 | "type": "string",
9 | "const": "@ocif/rel/hyperedge"
10 | },
11 | "endpoints": {
12 | "type": "array",
13 | "items": {
14 | "type": "object",
15 | "properties": {
16 | "id": {
17 | "type": "string",
18 | "description": "ID of attached entity (node or relation)"
19 | },
20 | "direction": {
21 | "type": "string",
22 | "description": "Direction of the connection: 'in' (edge is going into the hyper-edge), 'out' (edge is going out from thy hyper-edge), 'undir' (edge is attached undirected). This is the default.",
23 | "enum": ["in", "out", "undir"]
24 | },
25 | "weight": {
26 | "type": "number",
27 | "description": "Weight of the edge"
28 | }
29 | },
30 | "required": ["id"]
31 | }
32 | },
33 | "weight": {
34 | "type": "number",
35 | "description": "Weight of the edge. A floating-point number, which can be used to model the strength of the connection, as a whole. More general than endpoint-specific weights, and often sufficient."
36 | },
37 | "rel": {
38 | "type": "string",
39 | "description": "Represented relation type"
40 | }
41 | },
42 | "required": ["type", "endpoints"]
43 | }
44 |
--------------------------------------------------------------------------------
/spec/v0.6.1-draft/extensions/hyperedge-rel.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json-schema.org/draft/2020-12/schema",
3 | "title": "@ocif/rel/hyperedge",
4 | "description": "Hyperedge relation extension",
5 | "type": "object",
6 | "properties": {
7 | "type": {
8 | "type": "string",
9 | "const": "@ocif/rel/hyperedge"
10 | },
11 | "endpoints": {
12 | "type": "array",
13 | "items": {
14 | "type": "object",
15 | "properties": {
16 | "id": {
17 | "type": "string",
18 | "description": "ID of attached entity (node or relation)"
19 | },
20 | "direction": {
21 | "type": "string",
22 | "description": "Direction of the connection: 'in' (edge is going into the hyper-edge), 'out' (edge is going out from thy hyper-edge), 'undir' (edge is attached undirected). This is the default.",
23 | "enum": ["in", "out", "undir"]
24 | },
25 | "weight": {
26 | "type": "number",
27 | "description": "Weight of the edge"
28 | }
29 | },
30 | "required": ["id"]
31 | }
32 | },
33 | "weight": {
34 | "type": "number",
35 | "description": "Weight of the edge. A floating-point number, which can be used to model the strength of the connection, as a whole. More general than endpoint-specific weights, and often sufficient."
36 | },
37 | "rel": {
38 | "type": "string",
39 | "description": "Represented relation type"
40 | }
41 | },
42 | "required": ["type", "endpoints"]
43 | }
44 |
--------------------------------------------------------------------------------
/spec/v0.3/extensions/hyperedge-rel.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json-schema.org/draft/2020-12/schema",
3 | "title": "@ocwg/rel/hyperedge",
4 | "description": "Hyperedge relation extension",
5 | "type": "object",
6 | "properties": {
7 | "endpoints": {
8 | "type": "array",
9 | "items": {
10 | "type": "object",
11 | "properties": {
12 | "id": {
13 | "type": "string",
14 | "description": "ID of attached entity (node or relation)"
15 | },
16 | "direction": {
17 | "type": "string",
18 | "description": "Direction of the connection: 'in' (edge is going into the hyper-edge), 'out' (edge is going out from thy hyper-edge), 'undir' (edge is attached undirected). This is the default.",
19 | "enum": [
20 | "in",
21 | "out",
22 | "undir"
23 | ]
24 | },
25 | "weight": {
26 | "type": "number",
27 | "description": "Weight of the edge"
28 | }
29 | },
30 | "required": [
31 | "id"
32 | ]
33 | }
34 | },
35 | "weight": {
36 | "type": "number",
37 | "description": "Weight of the edge. A floating-point number, which can be used to model the strength of the connection, as a whole. More general than endpoint-specific weights, and often sufficient."
38 | },
39 | "rel": {
40 | "type": "string",
41 | "description": "Represented relation type"
42 | }
43 | },
44 | "required": [
45 | "members"
46 | ]
47 | }
48 |
--------------------------------------------------------------------------------
/spec/v0.5/core/arrow-node.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json-schema.org/draft/2020-12/schema",
3 | "title": "@ocif/node/arrow",
4 | "description": "Arrow node extension",
5 | "type": "object",
6 | "properties": {
7 | "type": {
8 | "type": "string",
9 | "const": "@ocif/node/arrow"
10 | },
11 | "strokeWidth": {
12 | "type": "number",
13 | "description": "The line width."
14 | },
15 | "strokeColor": {
16 | "type": "string",
17 | "description": "The color of the stroke."
18 | },
19 | "fillColor": {
20 | "type": "string",
21 | "description": "The color of the fill."
22 | },
23 | "start": {
24 | "type": "array",
25 | "items": {
26 | "type": "number"
27 | },
28 | "minItems": 2,
29 | "maxItems": 2,
30 | "description": "The start point."
31 | },
32 | "end": {
33 | "type": "array",
34 | "items": {
35 | "type": "number"
36 | },
37 | "minItems": 2,
38 | "maxItems": 2,
39 | "description": "The end point."
40 | },
41 | "startMarker": {
42 | "type": "string",
43 | "enum": ["none", "arrowhead"],
44 | "description": "The marker at the start of the arrow."
45 | },
46 | "endMarker": {
47 | "type": "string",
48 | "enum": ["none", "arrowhead"],
49 | "description": "The marker at the end of the arrow."
50 | },
51 | "relation": {
52 | "type": "string",
53 | "description": "The ID of the relation defining the semantics of the arrow."
54 | }
55 | },
56 | "required": ["type", "start", "end"]
57 | }
58 |
--------------------------------------------------------------------------------
/spec/v0.5/extensions/parent-child-rel.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json-schema.org/draft/2020-12/schema",
3 | "title": "@ocif/rel/parent-child",
4 | "description": "Parent-child relationship extension. A parent-child relation models a hierarchical relationship between nodes. It can be used to model inheritance, containment, or other hierarchical relationships.",
5 | "type": "object",
6 | "properties": {
7 | "type": {
8 | "type": "string",
9 | "const": "@ocif/rel/parent-child"
10 | },
11 | "parent": {
12 | "type": "string",
13 | "description": "ID of the parent node. There SHOULD be only one parent per child."
14 | },
15 | "child": {
16 | "type": "string",
17 | "description": "ID of the child node. A parent can have multiple children (expressed my multiple parent-child relations)."
18 | },
19 | "inherit": {
20 | "type": "boolean",
21 | "description": "Inherit properties. A boolean flag indicating if the child should inherit properties from the parent. Default is false. The exact semantics of inheritance are defined by the application. In general, when looking for JSON properties of a node and finding them undefined, an app should look for the same value in the parent node. This chain of parents should be followed until root is reached or a cycle is detected.",
22 | "default": false
23 | },
24 | "cascadeDelete": {
25 | "type": "boolean",
26 | "description": "A boolean flag indicating if the children should be deleted when the parent is deleted.",
27 | "default": true
28 | }
29 | },
30 | "required": ["type", "child"]
31 | }
32 |
--------------------------------------------------------------------------------
/spec/v0.6/extensions/arrow-node.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json-schema.org/draft/2020-12/schema",
3 | "title": "@ocif/node/arrow",
4 | "description": "Arrow node extension",
5 | "type": "object",
6 | "properties": {
7 | "type": {
8 | "type": "string",
9 | "const": "@ocif/node/arrow"
10 | },
11 | "strokeWidth": {
12 | "type": "number",
13 | "description": "The line width."
14 | },
15 | "strokeColor": {
16 | "type": "string",
17 | "description": "The color of the stroke."
18 | },
19 | "fillColor": {
20 | "type": "string",
21 | "description": "The color of the fill."
22 | },
23 | "start": {
24 | "type": "array",
25 | "items": {
26 | "type": "number"
27 | },
28 | "minItems": 2,
29 | "maxItems": 2,
30 | "description": "The start point."
31 | },
32 | "end": {
33 | "type": "array",
34 | "items": {
35 | "type": "number"
36 | },
37 | "minItems": 2,
38 | "maxItems": 2,
39 | "description": "The end point."
40 | },
41 | "startMarker": {
42 | "type": "string",
43 | "enum": ["none", "arrowhead"],
44 | "description": "The marker at the start of the arrow."
45 | },
46 | "endMarker": {
47 | "type": "string",
48 | "enum": ["none", "arrowhead"],
49 | "description": "The marker at the end of the arrow."
50 | },
51 | "relation": {
52 | "type": "string",
53 | "description": "The ID of the relation defining the semantics of the arrow."
54 | }
55 | },
56 | "required": ["type", "start", "end"]
57 | }
58 |
--------------------------------------------------------------------------------
/spec/v0.6/extensions/parent-child-rel.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json-schema.org/draft/2020-12/schema",
3 | "title": "@ocif/rel/parent-child",
4 | "description": "Parent-child relationship extension. A parent-child relation models a hierarchical relationship between nodes. It can be used to model inheritance, containment, or other hierarchical relationships.",
5 | "type": "object",
6 | "properties": {
7 | "type": {
8 | "type": "string",
9 | "const": "@ocif/rel/parent-child"
10 | },
11 | "parent": {
12 | "type": "string",
13 | "description": "ID of the parent node. There SHOULD be only one parent per child."
14 | },
15 | "child": {
16 | "type": "string",
17 | "description": "ID of the child node. A parent can have multiple children (expressed my multiple parent-child relations)."
18 | },
19 | "inherit": {
20 | "type": "boolean",
21 | "description": "Inherit properties. A boolean flag indicating if the child should inherit properties from the parent. Default is false. The exact semantics of inheritance are defined by the application. In general, when looking for JSON properties of a node and finding them undefined, an app should look for the same value in the parent node. This chain of parents should be followed until root is reached or a cycle is detected.",
22 | "default": false
23 | },
24 | "cascadeDelete": {
25 | "type": "boolean",
26 | "description": "A boolean flag indicating if the children should be deleted when the parent is deleted.",
27 | "default": true
28 | }
29 | },
30 | "required": ["type", "child"]
31 | }
32 |
--------------------------------------------------------------------------------
/spec/v0.1/schema/relation.schema.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json-schema.org/draft/2020-12/schema",
3 | "$id": "relation.schema.json",
4 | "title": "relation",
5 | "type": "object",
6 | "description": "relation between nodes",
7 | "required": ["name", "id", "properties"],
8 | "properties": {
9 | "name": { "type": "string" },
10 | "id": { "type": "string" },
11 | "properties": {
12 | "type": "array",
13 | "items": {
14 | "anyOf": [
15 | {
16 | "type": "object",
17 | "properties": {
18 | "schema": {
19 | "enum": ["@ocwg/set"]
20 | },
21 | "schema_version": {
22 | "type": "string"
23 | },
24 | "members": {
25 | "type": "array",
26 | "items": {
27 | "type": "string"
28 | }
29 | }
30 | },
31 | "required": ["schema", "schema_version", "members"]
32 | },
33 | {
34 | "type": "object",
35 | "properties": {
36 | "schema": {
37 | "enum": ["@ocwg/edge"]
38 | },
39 | "schema_version": {
40 | "type": "string"
41 | },
42 | "from": {
43 | "type": "string"
44 | },
45 | "to": {
46 | "type": "string"
47 | }
48 | },
49 | "required": ["schema", "schema_version", "from", "to"]
50 | }
51 | ]
52 | }
53 | }
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/spec/v0.6.1-draft/extensions/arrow-node.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json-schema.org/draft/2020-12/schema",
3 | "title": "@ocif/node/arrow",
4 | "description": "Arrow node extension",
5 | "type": "object",
6 | "properties": {
7 | "type": {
8 | "type": "string",
9 | "const": "@ocif/node/arrow"
10 | },
11 | "strokeWidth": {
12 | "type": "number",
13 | "description": "The line width."
14 | },
15 | "strokeColor": {
16 | "type": "string",
17 | "description": "The color of the stroke."
18 | },
19 | "fillColor": {
20 | "type": "string",
21 | "description": "The color of the fill."
22 | },
23 | "start": {
24 | "type": "array",
25 | "items": {
26 | "type": "number"
27 | },
28 | "minItems": 2,
29 | "maxItems": 2,
30 | "description": "The start point."
31 | },
32 | "end": {
33 | "type": "array",
34 | "items": {
35 | "type": "number"
36 | },
37 | "minItems": 2,
38 | "maxItems": 2,
39 | "description": "The end point."
40 | },
41 | "startMarker": {
42 | "type": "string",
43 | "enum": ["none", "arrowhead"],
44 | "description": "The marker at the start of the arrow."
45 | },
46 | "endMarker": {
47 | "type": "string",
48 | "enum": ["none", "arrowhead"],
49 | "description": "The marker at the end of the arrow."
50 | },
51 | "relation": {
52 | "type": "string",
53 | "description": "The ID of the relation defining the semantics of the arrow."
54 | }
55 | },
56 | "required": ["type", "start", "end"]
57 | }
58 |
--------------------------------------------------------------------------------
/spec/v0.6.1-draft/extensions/parent-child-rel.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json-schema.org/draft/2020-12/schema",
3 | "title": "@ocif/rel/parent-child",
4 | "description": "Parent-child relationship extension. A parent-child relation models a hierarchical relationship between nodes. It can be used to model inheritance, containment, or other hierarchical relationships.",
5 | "type": "object",
6 | "properties": {
7 | "type": {
8 | "type": "string",
9 | "const": "@ocif/rel/parent-child"
10 | },
11 | "parent": {
12 | "type": "string",
13 | "description": "ID of the parent node. There SHOULD be only one parent per child."
14 | },
15 | "child": {
16 | "type": "string",
17 | "description": "ID of the child node. A parent can have multiple children (expressed my multiple parent-child relations)."
18 | },
19 | "inherit": {
20 | "type": "boolean",
21 | "description": "Inherit properties. A boolean flag indicating if the child should inherit properties from the parent. Default is false. The exact semantics of inheritance are defined by the application. In general, when looking for JSON properties of a node and finding them undefined, an app should look for the same value in the parent node. This chain of parents should be followed until root is reached or a cycle is detected.",
22 | "default": false
23 | },
24 | "cascadeDelete": {
25 | "type": "boolean",
26 | "description": "A boolean flag indicating if the children should be deleted when the parent is deleted.",
27 | "default": true
28 | }
29 | },
30 | "required": ["type", "child"]
31 | }
32 |
--------------------------------------------------------------------------------
/making-of.md:
--------------------------------------------------------------------------------
1 | Making Of
2 | =========
3 |
4 |
5 | ## Markdown
6 |
7 | This document is written in Markdown, in the _GitHub-flavored_ variant.
8 |
9 | - https://github.github.com/gfm/
10 |
11 |
12 |
13 |
14 | ## Other Specs as Inspiration for Structuring this Document
15 |
16 | - https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/[]
17 | via Maikel in https://github.com/orgs/ocwg/discussions/32
18 |
19 |
20 |
21 | ## W3C Tools
22 |
23 | - Sample spec (outline)
24 | - https://www.w3.org/StyleSheets/TR/2016/README (HTML)
25 | - https://w3c-ccg.github.io/markdown-to-spec/ (Markdown)
26 |
27 | - Converter from Markdown to ReSpec https://github.com/w3c-ccg/markdown-to-spec
28 | - Bikeshed: https://github.com/speced/bikeshed
29 |
30 | # Editor
31 | - IntelliJ with https://plugins.jetbrains.com/plugin/14683-gfma
32 |
33 | In IntelliJ, the markdown preview produces HTML, which allows this snippet of CSS to be used to style markdown checkboxes (a valid GitHub flavored Markdown extension). Just copy it into the spec.md file, while you work on it.
34 |
35 | ```css
36 |
37 |
58 | ```
59 |
--------------------------------------------------------------------------------
/spec/v0.5/how-to-spec.md:
--------------------------------------------------------------------------------
1 | # How To Edit The Spec
2 |
3 | * [How To Edit The Spec](#how-to-edit-the-spec)
4 | * [Release Instructions](#release-instructions)
5 | * [How To Typeset Issues](#how-to-typeset-issues)
6 |
7 |
8 | - All URIs should have the same, consistent structure
9 | - Property tables should follow these conventions
10 | - **required** is always bold, other entries are not
11 | - JSON types are set in `monospace`
12 | - OCIF types are linked to their definition
13 | - All examples start with `**Example:**`
14 | - Order of columns is always: Property, JSON Type, OCIF Type, Required, Contents, Default
15 | - Empty columns can be omitted
16 |
17 | ## Release Instructions
18 |
19 | When creating a new version of the spec:
20 |
21 | 1. `cp` the current version's directory (`/spec/vX.X`) to the next version number with `-draft` appended
22 | 2. Merge the new directory to the `main` branch.
23 | 3. Create a new branch named `vX.X-draft`.
24 | 4. Create pull requests against the new `vX.X-draft` folder until satisfied with release. These will have nice, small diffs that just highlight the major changes.
25 | - Alternatively, you can leave the new directory on a branch and create pull requests against that branch until it's ready to be merged.
26 | 5. Open a pull request to update all of the "current" version pointers in the `spec` repo:
27 | - rename the `vX.X-draft` folder to `vX.X`
28 | - update `/public/_redirects` line 2 to point spec.canvasprotocol.org to:
29 | ```
30 | / https://github.com/ocwg/spec/blob/main/spec/vX.X/spec.md 302
31 | ```
32 | - Excluding the `/spec` directory, find and replace the previous version with the new version (i.e., replace `v0.5` with `v0.5.1`).
33 | - That will update the Cookbook, Catalog, Examples, and README.md
34 | 6. Update the version numbers on the [website](https://github.com/ocwg/canvasprotocol.org/blob/main/index.html).
35 | 7. Consider notifying people in Discord and sending a Newsletter update.
36 |
37 | ## How To Typeset Issues
38 | - Issues are temporary TODOs, which should be resolved before the final version. The `@@` makes them easy to search in an editor.
39 |
40 | **Issue Example:**
41 |
42 | - [ ] @@ This is an issue
43 |
--------------------------------------------------------------------------------
/design/requirements.md:
--------------------------------------------------------------------------------
1 | # Requirements
2 | These are requirements and goals which we defined for a canvas interchange format.
3 |
4 |
5 | * [Requirements](#requirements)
6 | * [Offline](#offline)
7 | * [Dynamic](#dynamic)
8 | * [Graceful Degradation](#graceful-degradation)
9 | * [Extensible](#extensible)
10 | * [Human Friendly](#human-friendly)
11 | * [Re-use](#re-use)
12 | * [Precise](#precise)
13 | * [Visual Fidelity](#visual-fidelity)
14 |
15 |
16 | ## Offline
17 | The format should allow to be opened in an offline scenario, i.e. all
18 | content needs to be present locally.
19 |
20 | ## Dynamic
21 | We don’t want to force a canvas to contain only offline content. Some
22 | resource are (a) dynamic (e.g. a weather forecast), or (b) large (e.g.
23 | videos) so that downloading is not always the best strategy.
24 |
25 | ## Graceful Degradation
26 | We want fall-backs for difficult content types. In email, HTML mails
27 | usually have a plain-text fall-back.
28 |
29 | ## Extensible
30 | We want a number of apps, including those not known at time of writing
31 | this spec, be able to use the export format to round-trip data. Thus,
32 |
33 | - an app may write arbitrary data into the export (given it adheres to
34 | some rules)
35 | - an app may not alter or delete data written be another app (unless the
36 | whole node is deleted)
37 |
38 | ## Human Friendly
39 | We want to make it easy for humans to inspect and understand an export
40 | files' content. And, if possible, make changes on the source file.
41 |
42 | ## Re-use
43 | When possible, we want to re-use existing standards and mechanisms, in
44 | order to not reinvent the wheel.
45 |
46 | ## Precise
47 | We want the spec to clearly define which OCWG files are valid, which are
48 | not, and especially, how to interpret the valid ones.
49 |
50 | For numeric precision, which becomes relevant in an "infinite" zoom-able
51 | canvas, see [13](https://github.com/orgs/ocwg/discussions/13).
52 |
53 | Text rendering and container dimensions have a complex interplay. See
54 | [5](https://github.com/orgs/ocwg/discussions/5).
55 |
56 | ## Visual Fidelity
57 | Although we don’t strive for 100% visual fidelity (see
58 | [11](https://github.com/orgs/ocwg/discussions/11)), there are some areas
59 | we need to look at:
60 |
61 | - More Relevant
62 | - text rendering (e.g. different browsers have different default line
63 | heights; installed fonts differ, hyphenation differs, text direction,
64 | Unicode, …)
65 | - transparency & z-Index: What is in effect visible and whats hidden?
66 | - Minor
67 | - color spaces?
68 | - path stroking (rendering a path is not 100% defined)
69 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # OCIF (Open Canvas Interchange Format) Spec
2 |
3 | ## ⚒️ Status: Seeking Implementation Feedback
4 | The [spec](./spec/v0.6/spec.md) is in the *"Candidate Recommendation"* stage, which means it has been widely reviewed and satisfies OCWG's technical requirements. We are currently gathering implementation experience.
5 |
6 | We welcome any feedback and implementations.
7 |
8 | 📚 Read more at [canvasprotocol.org](https://canvasprotocol.org).
9 |
10 | ## Release Process
11 |
12 | When releasing a new version of the OCIF specification, follow these steps:
13 |
14 | ### 1. Prepare the Release
15 |
16 | See [detailed technical release instructions](./spec/v0.5/how-to-spec.md#release-instructions) for the complete Git workflow, including:
17 | - Creating and managing draft versions
18 | - Updating `public/_redirects` to point spec.canvasprotocol.org to the new version
19 | - Updating version numbers across Cookbook, Catalog, Examples, and README
20 |
21 | Quick checklist:
22 | - [ ] Follow the technical release steps in how-to-spec.md
23 | - [ ] Create a git tag for the release (e.g., `v0.6`)
24 | - [ ] Push the tag to GitHub: `git push origin v0.6`
25 |
26 | ### 2. Update Website
27 |
28 | - [ ] Update [canvasprotocol.org](https://canvasprotocol.org) to list the new version
29 | - Note: The current website does not list version numbers, consider if this should change
30 |
31 | ### 3. Announce on GitHub
32 |
33 | - [ ] Create a new announcement post in [GitHub Discussions](https://github.com/orgs/ocwg/discussions)
34 | - Use the Announcements category
35 | - Title format: "OCIF [version] is released"
36 | - Include: version number, status, key features/changes, call to action for implementers
37 | - Reference [example 0.5 announcement](https://github.com/orgs/ocwg/discussions/57)
38 |
39 | ### 4. Send Email Announcement
40 |
41 | - [ ] Draft email announcement via [Buttondown](https://buttondown.com/ocwg)
42 | - Subject format: "OCIF [version] is released"
43 | - Include TL;DR summary
44 | - Link to the GitHub Discussion post
45 | - Link to https://spec.canvasprotocol.org
46 | - Review [past announcements](https://buttondown.com/ocwg/archive/) for tone and format
47 |
48 | ### 5. Social Media & Community
49 |
50 | - [ ] Post in OCWG Discord #general channel
51 | - Link to GitHub Discussion post
52 | - Brief summary of key changes
53 | - [ ] Post to Twitter and Bluesky (@jessmartin or designated account)
54 | - Link to GitHub Discussion post
55 | - Link to https://spec.canvasprotocol.org
56 | - Use consistent messaging with other channels
57 |
58 | ### 6. Post-Release
59 |
60 | - [ ] Create a new draft branch for the next version
61 | - [ ] Update README to reflect "seeking implementation feedback" status if applicable
62 | - [ ] Monitor GitHub Discussions and Discord for questions and feedback
63 |
--------------------------------------------------------------------------------
/spec/v0.5/extensions/anchored-node.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json-schema.org/draft/2020-12/schema",
3 | "$id": "https://spec.canvasprotocol.org/v0.5/extensions/anchored-node.json",
4 | "title": "@ocif/node/anchored",
5 | "description": "Anchored node extension for relative positioning anchored to a parent item",
6 | "type": "object",
7 | "properties": {
8 | "type": {
9 | "type": "string",
10 | "const": "@ocif/node/anchored"
11 | },
12 | "topLeftPosition": {
13 | "description": "Top left anchor as percentage coordinates",
14 | "oneOf": [
15 | {
16 | "type": "array",
17 | "items": {
18 | "type": "number"
19 | },
20 | "minItems": 2,
21 | "maxItems": 2
22 | },
23 | {
24 | "type": "array",
25 | "items": {
26 | "type": "number"
27 | },
28 | "minItems": 3,
29 | "maxItems": 3
30 | }
31 | ],
32 | "default": [0, 0]
33 | },
34 | "bottomRightPosition": {
35 | "description": "Bottom-right anchor as percentage coordinates",
36 | "oneOf": [
37 | {
38 | "type": "array",
39 | "items": {
40 | "type": "number"
41 | },
42 | "minItems": 2,
43 | "maxItems": 2
44 | },
45 | {
46 | "type": "array",
47 | "items": {
48 | "type": "number"
49 | },
50 | "minItems": 3,
51 | "maxItems": 3
52 | }
53 | ],
54 | "default": [1, 1]
55 | },
56 | "topLeftOffset": {
57 | "description": "Top left offset as absolute coordinates",
58 | "oneOf": [
59 | {
60 | "type": "array",
61 | "items": {
62 | "type": "number"
63 | },
64 | "minItems": 2,
65 | "maxItems": 2
66 | },
67 | {
68 | "type": "array",
69 | "items": {
70 | "type": "number"
71 | },
72 | "minItems": 3,
73 | "maxItems": 3
74 | }
75 | ],
76 | "default": [0, 0]
77 | },
78 | "bottomRightOffset": {
79 | "description": "Bottom-right offset as absolute coordinates",
80 | "oneOf": [
81 | {
82 | "type": "array",
83 | "items": {
84 | "type": "number"
85 | },
86 | "minItems": 2,
87 | "maxItems": 2
88 | },
89 | {
90 | "type": "array",
91 | "items": {
92 | "type": "number"
93 | },
94 | "minItems": 3,
95 | "maxItems": 3
96 | }
97 | ],
98 | "default": [0, 0]
99 | }
100 | },
101 | "required": ["type"]
102 | }
103 |
--------------------------------------------------------------------------------
/spec/v0.6/extensions/anchored-node.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json-schema.org/draft/2020-12/schema",
3 | "$id": "https://spec.canvasprotocol.org/v0.6/extensions/anchored-node.json",
4 | "title": "@ocif/node/anchored",
5 | "description": "Anchored node extension for relative positioning anchored to a parent item",
6 | "type": "object",
7 | "properties": {
8 | "type": {
9 | "type": "string",
10 | "const": "@ocif/node/anchored"
11 | },
12 | "topLeftAnchor": {
13 | "description": "Top left anchor as percentage coordinates",
14 | "oneOf": [
15 | {
16 | "type": "array",
17 | "items": {
18 | "type": "number"
19 | },
20 | "minItems": 2,
21 | "maxItems": 2
22 | },
23 | {
24 | "type": "array",
25 | "items": {
26 | "type": "number"
27 | },
28 | "minItems": 3,
29 | "maxItems": 3
30 | }
31 | ],
32 | "default": [0.0, 0.0]
33 | },
34 | "bottomRightAnchor": {
35 | "description": "Bottom-right anchor as percentage coordinates",
36 | "oneOf": [
37 | {
38 | "type": "array",
39 | "items": {
40 | "type": "number"
41 | },
42 | "minItems": 2,
43 | "maxItems": 2
44 | },
45 | {
46 | "type": "array",
47 | "items": {
48 | "type": "number"
49 | },
50 | "minItems": 3,
51 | "maxItems": 3
52 | }
53 | ],
54 | "default": [1.0, 1.0]
55 | },
56 | "topLeftOffset": {
57 | "description": "Top left offset as absolute coordinates",
58 | "oneOf": [
59 | {
60 | "type": "array",
61 | "items": {
62 | "type": "number"
63 | },
64 | "minItems": 2,
65 | "maxItems": 2
66 | },
67 | {
68 | "type": "array",
69 | "items": {
70 | "type": "number"
71 | },
72 | "minItems": 3,
73 | "maxItems": 3
74 | }
75 | ],
76 | "default": [0.0, 0.0]
77 | },
78 | "bottomRightOffset": {
79 | "description": "Bottom-right offset as absolute coordinates",
80 | "oneOf": [
81 | {
82 | "type": "array",
83 | "items": {
84 | "type": "number"
85 | },
86 | "minItems": 2,
87 | "maxItems": 2
88 | },
89 | {
90 | "type": "array",
91 | "items": {
92 | "type": "number"
93 | },
94 | "minItems": 3,
95 | "maxItems": 3
96 | }
97 | ],
98 | "default": [0.0, 0.0]
99 | }
100 | },
101 | "required": ["type"]
102 | }
103 |
--------------------------------------------------------------------------------
/spec/v0.6.1-draft/extensions/anchored-node.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json-schema.org/draft/2020-12/schema",
3 | "$id": "https://spec.canvasprotocol.org/v0.6.1/extensions/anchored-node.json",
4 | "title": "@ocif/node/anchored",
5 | "description": "Anchored node extension for relative positioning anchored to a parent item",
6 | "type": "object",
7 | "properties": {
8 | "type": {
9 | "type": "string",
10 | "const": "@ocif/node/anchored"
11 | },
12 | "topLeftAnchor": {
13 | "description": "Top left anchor as percentage coordinates",
14 | "oneOf": [
15 | {
16 | "type": "array",
17 | "items": {
18 | "type": "number"
19 | },
20 | "minItems": 2,
21 | "maxItems": 2
22 | },
23 | {
24 | "type": "array",
25 | "items": {
26 | "type": "number"
27 | },
28 | "minItems": 3,
29 | "maxItems": 3
30 | }
31 | ],
32 | "default": [0.0, 0.0]
33 | },
34 | "bottomRightAnchor": {
35 | "description": "Bottom-right anchor as percentage coordinates",
36 | "oneOf": [
37 | {
38 | "type": "array",
39 | "items": {
40 | "type": "number"
41 | },
42 | "minItems": 2,
43 | "maxItems": 2
44 | },
45 | {
46 | "type": "array",
47 | "items": {
48 | "type": "number"
49 | },
50 | "minItems": 3,
51 | "maxItems": 3
52 | }
53 | ],
54 | "default": [1.0, 1.0]
55 | },
56 | "topLeftOffset": {
57 | "description": "Top left offset as absolute coordinates",
58 | "oneOf": [
59 | {
60 | "type": "array",
61 | "items": {
62 | "type": "number"
63 | },
64 | "minItems": 2,
65 | "maxItems": 2
66 | },
67 | {
68 | "type": "array",
69 | "items": {
70 | "type": "number"
71 | },
72 | "minItems": 3,
73 | "maxItems": 3
74 | }
75 | ],
76 | "default": [0.0, 0.0]
77 | },
78 | "bottomRightOffset": {
79 | "description": "Bottom-right offset as absolute coordinates",
80 | "oneOf": [
81 | {
82 | "type": "array",
83 | "items": {
84 | "type": "number"
85 | },
86 | "minItems": 2,
87 | "maxItems": 2
88 | },
89 | {
90 | "type": "array",
91 | "items": {
92 | "type": "number"
93 | },
94 | "minItems": 3,
95 | "maxItems": 3
96 | }
97 | ],
98 | "default": [0.0, 0.0]
99 | }
100 | },
101 | "required": ["type"]
102 | }
103 |
--------------------------------------------------------------------------------
/spec/v0.6/how-to-spec.md:
--------------------------------------------------------------------------------
1 | # How To Edit The Spec
2 |
3 | * [How To Edit The Spec](#how-to-edit-the-spec)
4 | * [Release Instructions](#release-instructions)
5 | * [How To Typeset Issues](#how-to-typeset-issues)
6 |
7 |
8 | - All URIs should have the same, consistent structure
9 | - Property tables should follow these conventions
10 | - **required** is always bold, other entries are not
11 | - JSON types are set in `monospace`
12 | - OCIF types are linked to their definition
13 | - All examples start with `**Example:**`
14 | - Order of columns is always: Property, JSON Type, OCIF Type, Required, Contents, Default
15 | - Empty columns can be omitted
16 |
17 | ## Release Instructions
18 |
19 | See the [Release Process section in the README](../../../README.md#release-process) for the complete release checklist, including communication and announcement steps.
20 |
21 | ### Technical Release Workflow
22 |
23 | When creating a new version of the spec:
24 |
25 | 1. **Create draft version**: `cp` the current version's directory (`/spec/vX.X`) to the next version number with `-draft` appended
26 | 2. **Merge to main**: Merge the new directory to the `main` branch
27 | 3. **Create draft branch**: Create a new branch named `vX.X-draft`
28 | 4. **Iterate on draft**: Create pull requests against the new `vX.X-draft` folder until satisfied with release. These will have nice, small diffs that just highlight the major changes
29 | - Alternatively, you can leave the new directory on a branch and create pull requests against that branch until it's ready to be merged
30 |
31 | 5. **Finalize release**: Open a pull request to update all of the "current" version pointers in the `spec` repo:
32 | - Rename the `vX.X-draft` folder to `vX.X`
33 | - Update `/public/_redirects` line 2 to point spec.canvasprotocol.org to:
34 | ```
35 | / https://github.com/ocwg/spec/blob/main/spec/vX.X/spec.md 302
36 | ```
37 | - Excluding the `/spec` directory, find and replace the previous version with the new version (e.g., replace `v0.5` with `v0.6`)
38 | - This will update the Cookbook, Catalog, Examples, and README.md
39 |
40 | 6. **Create git tag**: After merging, create and push a git tag for the release (e.g., `git tag v0.6 && git push origin v0.6`)
41 |
42 | 7. **Update website**: Update the version numbers on the [canvasprotocol.org website](https://github.com/ocwg/canvasprotocol.org/blob/main/index.html)
43 |
44 | 8. **Announce**: Follow the communication steps in the [README Release Process](../../../README.md#release-process) to notify the community via GitHub Discussions, Buttondown newsletter, Discord, and social media
45 |
46 | ## How To Typeset Issues
47 | - Issues are temporary TODOs, which should be resolved before the final version. The `@@` makes them easy to search in an editor.
48 |
49 | **Issue Example:**
50 |
51 | - [ ] @@ This is an issue
52 |
--------------------------------------------------------------------------------
/spec/v0.6.1-draft/how-to-spec.md:
--------------------------------------------------------------------------------
1 | # How To Edit The Spec
2 |
3 |
4 |
5 | - [How To Edit The Spec](#how-to-edit-the-spec)
6 | - [Release Instructions](#release-instructions)
7 | - [How To Typeset Issues](#how-to-typeset-issues)
8 |
9 |
10 | * All URIs should have the same, consistent structure
11 | * Property tables should follow these conventions
12 | - **required** is always bold, other entries are not
13 | - JSON types are set in `monospace`
14 | - OCIF types are linked to their definition
15 | - All examples start with `**Example:**`
16 | - Order of columns is always: Property, JSON Type, OCIF Type, Required, Contents, Default
17 | - Empty columns can be omitted
18 |
19 | ## Release Instructions
20 |
21 | See the [Release Process section in the README](../../../README.md#release-process) for the complete release checklist, including communication and announcement steps.
22 |
23 | ### Technical Release Workflow
24 |
25 | When creating a new version of the spec:
26 |
27 | 1. **Create draft version**: `cp` the current version's directory (`/spec/vX.X`) to the next version number with `-draft` appended
28 | 2. **Merge to main**: Merge the new directory to the `main` branch
29 | 3. **Create draft branch**: Create a new branch named `vX.X-draft`
30 | 4. **Iterate on draft**: Create pull requests against the new `vX.X-draft` folder until satisfied with release. These will have nice, small diffs that just highlight the major changes
31 |
32 | - Alternatively, you can leave the new directory on a branch and create pull requests against that branch until it's ready to be merged
33 |
34 | 5. **Finalize release**: Open a pull request to update all of the "current" version pointers in the `spec` repo:
35 |
36 | - Rename the `vX.X-draft` folder to `vX.X`
37 | - Update `/public/_redirects` line 2 to point spec.canvasprotocol.org to:
38 | ```
39 | / https://github.com/ocwg/spec/blob/main/spec/vX.X/spec.md 302
40 | ```
41 | - Excluding the `/spec` directory, find and replace the previous version with the new version (e.g., replace `v0.5` with `v0.6.1`)
42 | - This will update the Cookbook, Catalog, Examples, and README.md
43 |
44 | 6. **Create git tag**: After merging, create and push a git tag for the release (e.g., `git tag v0.6.1 && git push origin v0.6.1`)
45 |
46 | 7. **Update website**: Update the version numbers on the [canvasprotocol.org website](https://github.com/ocwg/canvasprotocol.org/blob/main/index.html)
47 |
48 | 8. **Announce**: Follow the communication steps in the [README Release Process](../../../README.md#release-process) to notify the community via GitHub Discussions, Buttondown newsletter, Discord, and social media
49 |
50 | ## How To Typeset Issues
51 |
52 | - Issues are temporary TODOs, which should be resolved before the final version. The `@@` makes them easy to search in an editor.
53 |
54 | **Issue Example:**
55 |
56 | - [ ] @@ This is an issue
57 |
--------------------------------------------------------------------------------
/design/adr-000-TEMPLATE.md:
--------------------------------------------------------------------------------
1 | ---
2 | # These are optional metadata elements. Feel free to remove any of them.
3 | status: "{proposed | rejected | accepted | deprecated | … | superseded by ADR-0123"
4 | date: {YYYY-MM-DD when the decision was last updated}
5 | decision-makers: {list everyone involved in the decision}
6 | consulted: {list everyone whose opinions are sought (typically subject-matter experts); and with whom there is a two-way communication}
7 | informed: {list everyone who is kept up-to-date on progress; and with whom there is a one-way communication}
8 | ---
9 |
10 | # {short title, representative of solved problem and found solution}
11 |
12 | ## Context and Problem Statement
13 |
14 | {Describe the context and problem statement, e.g., in free form using two to three sentences or in the form of an illustrative story. You may want to articulate the problem in form of a question and add links to collaboration boards or issue management systems.}
15 |
16 |
17 | ## Decision Drivers
18 |
19 | * {decision driver 1, e.g., a force, facing concern, …}
20 | * {decision driver 2, e.g., a force, facing concern, …}
21 | * …
22 |
23 | ## Considered Options
24 |
25 | * {title of option 1}
26 | * {title of option 2}
27 | * {title of option 3}
28 | * …
29 |
30 | ## Decision Outcome
31 |
32 | Chosen option: "{title of option 1}", because {justification. e.g., only option, which meets k.o. criterion decision driver | which resolves force {force} | … | comes out best (see below)}.
33 |
34 |
35 | ### Consequences
36 |
37 | * Good, because {positive consequence, e.g., improvement of one or more desired qualities, …}
38 | * Bad, because {negative consequence, e.g., compromising one or more desired qualities, …}
39 | * …
40 |
41 |
42 | ### Confirmation
43 |
44 | {Describe how the implementation of/compliance with the ADR can/will be confirmed. Is the chosen design and its implementation in line with the decision? E.g., a design/code review or a test with a library such as ArchUnit can help validate this. Note that although we classify this element as optional, it is included in many ADRs.}
45 |
46 |
47 | ## Pros and Cons of the Options
48 |
49 | ### {title of option 1}
50 |
51 |
52 | {example | description | pointer to more information | …}
53 |
54 | * Good, because {argument a}
55 | * Good, because {argument b}
56 |
57 | * Neutral, because {argument c}
58 | * Bad, because {argument d}
59 | * …
60 |
61 | ### {title of other option}
62 |
63 | {example | description | pointer to more information | …}
64 |
65 | * Good, because {argument a}
66 | * Good, because {argument b}
67 | * Neutral, because {argument c}
68 | * Bad, because {argument d}
69 | * …
70 |
71 |
72 | ## More Information
73 |
74 | {You might want to provide additional evidence/confidence for the decision outcome here and/or document the team agreement on the decision and/or define when/how this decision the decision should be realized and if/when it should be re-visited. Links to other decisions and resources might appear here as well.}
75 |
--------------------------------------------------------------------------------
/design/goals.md:
--------------------------------------------------------------------------------
1 | # Goals
2 | The high-level goals of the OCWG interchange format (OCIF).
3 |
4 | We want to allow a number of apps, including some not known at time of writing this spec, to be able to use the format to interchange data.
5 | There are several scenarios, which we want to support:
6 |
7 | - Allow an application A to export data,
8 | - Let application B import it,
9 | - Let application B see the data, which reasonably resembles what a user in A saw (goal: visual resemblance),
10 | - Let application B make changes to the data (i.e. add/removed/change things), export it again,
11 | - Let application A import the data, and have its special data still be there (goal: round-tripping).
12 |
13 | ## Visual Resemblance
14 | Data can be authored in any application, exported in OCIF, imported in another compliant application, and should visually resemble the original data.
15 |
16 | ## Round-Tripping
17 | An app A can export to OCIF and embed its native data structures within, so that any app B can work without, re-export, and allow app A to reconstruct its native data structures.
18 |
19 | - If a node has been deleted in app B, it should also be deleted for app A.
20 | - If a node has been added in B, no native data for app A can be present. However, app A can import the node and add some defaults.
21 |
22 | ## Useful out of the Box
23 | Balance Extensibility and Usefulness
24 | On one extreme, we could define an ultra-extensible format for nodes devoid any built-in structure or semantics, delegating all this to extensions.
25 | On the other hand, we could try to specify the super-set of all structures present in relevant apps. This would be a huge effort and likely to fail.
26 |
27 | Therefore, we must carefully balance these two forces and strive for a simple, extensible, but out-of-the-box useful format.
28 |
29 | ## Canvas Apps
30 | To state our goals, we need to define, what a canvas app is.
31 | Obviously, an application with the notion of an (infinite?) canvas is a canvas app.
32 | So a canvas app is characterized by
33 |
34 | - a visual (typically 2D) workspace, where nodes can be placed and connected.
35 | - Nodes are often rectangles, but can be of other shapes.
36 | - Nodes can contain text, scribbles, images, or other media.
37 | - Node can be connected by arrows or other connectors.
38 |
39 | However, there are also a lot of slightly more specialized apps, which should also be able to profit from OCIF:
40 | - A knowledge management tool (Hi Obsidian!)
41 | - A freeform drawing tool (Hi Excalidraw!)
42 | - A 3D modeling tool (Hi Aaron! Hi glTF!)
43 | - A flow-based programming tool (Hi Maikel!)
44 | - A mind map tool
45 | - A (semantic) wiki
46 | - A graph database / knowledge graph visualizer
47 |
48 | Less clearly in focus (although not impossible to export to OCIF) are:
49 | - A game level editor
50 | - A CAD tool
51 | - A UML tool
52 | - A music notation tool
53 | - A text editor
54 | - A spreadsheet
55 | - A video editor
56 | - A photo/raster graphics editor
57 |
58 | To make it even more clear, here is a list of apps known to us, which in our view are canvas apps:
59 | * Draw.io
60 | * Excalidraw
61 | * Heptabase
62 | * infinitymaps.io
63 | * Kinopio
64 | * Miro
65 | * Muse
66 | * Nodebook.io
67 | * Notion
68 | * Obsidian
69 | * Roam
70 | * TLDraw
71 | * Zwibbler
72 | * yEd
73 |
74 | And here is another list of apps inspected by us, to understand the space:
75 | * Blender Nodes
76 | * ReactFlow
77 | * Kosmik
78 | * Stately
79 | * Everyone Draw
80 | * Sage-3
81 | * Bezi
82 | * Dgrm
83 | * Vizio
84 | * GraphViz DOT
85 | * Breadcrumbs
86 |
87 |
88 | More are listed https://infinitecanvas.tools/
89 |
--------------------------------------------------------------------------------
/design/resource-structure.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/spec/v0.4/schema.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json-schema.org/draft/2020-12/schema",
3 | "title": "OCIF core 0.4",
4 | "description": "The schema for the Open Canvas Interchange Format (OCIF) Core document structure.",
5 | "type": "object",
6 | "properties": {
7 | "ocif": {
8 | "type": "string",
9 | "description": "The URI of the OCIF schema"
10 | },
11 | "nodes": {
12 | "type": "array",
13 | "description": "A list of nodes",
14 | "items": {
15 | "$ref": "#/$defs/node"
16 | }
17 | },
18 | "relations": {
19 | "type": "array",
20 | "description": "A list of relations",
21 | "items": {
22 | "$ref": "#/$defs/relation"
23 | }
24 | },
25 | "resources": {
26 | "type": "array",
27 | "description": "A list of resources",
28 | "items": {
29 | "$ref": "#/$defs/resource"
30 | }
31 | },
32 | "schemas": {
33 | "type": "array",
34 | "description": "Declared schemas",
35 | "items": {
36 | "$ref": "#/$defs/schema"
37 | }
38 | }
39 | },
40 | "$defs": {
41 | "node": {
42 | "type": "object",
43 | "description": "A node in the OCIF document",
44 | "properties": {
45 | "id": {
46 | "type": "string",
47 | "description": "A unique identifier for the node."
48 | },
49 | "position": {
50 | "type": "array",
51 | "description": "Coordinate as (x,y) or (x,y,z).",
52 | "items": {
53 | "type": "number"
54 | }
55 | },
56 | "size": {
57 | "type": "array",
58 | "description": "The size of the node per dimension.",
59 | "items": {
60 | "type": "number"
61 | }
62 | },
63 | "resource": {
64 | "type": "string",
65 | "description": "The resource to display"
66 | },
67 | "data": {
68 | "type": "array",
69 | "description": "Extended node data"
70 | },
71 | "rotation": {
72 | "type": "number",
73 | "description": "+/- 360 degrees"
74 | },
75 | "scale": {
76 | "type": "array",
77 | "description": "Scale factors to resize nodes",
78 | "items": {
79 | "type": "number"
80 | }
81 | }
82 | },
83 | "required": ["id"]
84 | },
85 | "relation": {
86 | "type": "object",
87 | "description": "A relation between nodes",
88 | "properties": {
89 | "id": {
90 | "type": "string",
91 | "description": "A unique identifier for the relation."
92 | },
93 | "data": {
94 | "type": "array",
95 | "description": "Additional data for the relation."
96 | }
97 | },
98 | "required": ["id"]
99 | },
100 | "resource": {
101 | "type": "object",
102 | "description": "A resource in the OCIF document",
103 | "properties": {
104 | "id": {
105 | "type": "string",
106 | "description": "A unique identifier for the resource."
107 | },
108 | "representations": {
109 | "type": "array",
110 | "description": "A list of representations of the resource.",
111 | "items": {
112 | "$ref": "#/$defs/representation"
113 | }
114 | }
115 | },
116 | "required": ["id", "representations"]
117 | },
118 | "representation": {
119 | "type": "object",
120 | "description": "A representation of a resource. Either content or location MUST be present. If content is used, location must be left out and vice versa.",
121 | "properties": {
122 | "location": {
123 | "type": "string",
124 | "description": "The storage location for the resource. This can be a relative URI for an external resource or an absolute URI for a remote resource. If a data: URI is used, the content and mime-type properties are implicitly defined already. Values in content and mime-type are ignored."
125 | },
126 | "mime-type": {
127 | "type": "string",
128 | "description": "The IANA MIME Type of the resource."
129 | },
130 | "content": {
131 | "type": "string",
132 | "description": "The content of the resource. This is the actual data of the resource as a string. Can be base64-encoded."
133 | }
134 | }
135 | },
136 | "schema": {
137 | "type": "object",
138 | "description": "A schema in the OCIF document",
139 | "properties": {
140 | "uri": {
141 | "type": "string",
142 | "description": "The URI of the schema, Identifier (and location) of the schema."
143 | },
144 | "schema": {
145 | "type": "object",
146 | "description": "The actual JSON schema as a JSON object."
147 | },
148 | "location": {
149 | "type": "string",
150 | "description": "The storage location for the schema."
151 | },
152 | "name": {
153 | "type": "string",
154 | "description": "An optional short name for the schema."
155 | }
156 | },
157 | "required": ["uri"]
158 | }
159 | },
160 | "required": ["ocif"]
161 | }
162 |
--------------------------------------------------------------------------------
/spec/v0.1/schema/schema.combined.schema.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json-schema.org/draft/2020-12/schema",
3 | "$id": "open-canvas.schema.json",
4 | "title": "Open Canvas",
5 | "type": "object",
6 | "description": "The root object for a open-canvas file",
7 | "required": ["schema_version", "nodes"],
8 | "properties": {
9 | "schema_version": {
10 | "type": "string"
11 | },
12 | "nodes": {
13 | "type": "array",
14 | "description": "A list of nodes.",
15 | "items": {
16 | "$ref": "#/$defs/node.schema.json"
17 | }
18 | },
19 | "relations": {
20 | "type": "array",
21 | "description": "A list of relations.",
22 | "items": {
23 | "$ref": "#/$defs/relation.schema.json"
24 | }
25 | },
26 | "schemas": {
27 | "type": "object",
28 | "description": "A dictionary of extended schemas",
29 | "$ref": "#/$defs/schemas.schema.json"
30 | },
31 | "resources": {
32 | "type": "object",
33 | "description": "A dictionary of resources which can be referenced by nodes and relations."
34 | }
35 | },
36 | "$defs": {
37 | "relation.schema.json": {
38 | "$schema": "https://json-schema.org/draft/2020-12/schema",
39 | "$id": "relation.schema.json",
40 | "title": "relation",
41 | "type": "object",
42 | "description": "relation between nodes",
43 | "required": ["id", "name", "properties"],
44 | "properties": {
45 | "name": {
46 | "type": "string"
47 | },
48 | "id": {
49 | "type": "string"
50 | },
51 | "properties": {
52 | "type": "array",
53 | "items": {
54 | "anyOf": [
55 | {
56 | "type": "object",
57 | "properties": {
58 | "schema": {
59 | "enum": ["@ocwg/set"]
60 | },
61 | "schema_version": {
62 | "type": "string"
63 | },
64 | "members": {
65 | "type": "array",
66 | "items": {
67 | "type": "string"
68 | }
69 | }
70 | },
71 | "required": ["schema", "schema_version", "members"]
72 | },
73 | {
74 | "type": "object",
75 | "properties": {
76 | "schema": {
77 | "enum": ["@ocwg/edge"]
78 | },
79 | "schema_version": {
80 | "type": "string"
81 | },
82 | "from": {
83 | "type": "string"
84 | },
85 | "to": {
86 | "type": "string"
87 | }
88 | },
89 | "required": ["schema", "schema_version", "from", "to"]
90 | }
91 | ]
92 | }
93 | }
94 | }
95 | },
96 | "schemas.schema.json": {
97 | "$schema": "https://json-schema.org/draft/2020-12/schema",
98 | "$id": "schemas.schema.json",
99 | "title": "schema",
100 | "type": "object",
101 | "description": "inline definition of extended node schemas",
102 | "required": [],
103 | "propertyNames": {
104 | "pattern": "^@"
105 | },
106 | "patternProperties": {
107 | "^@": {
108 | "type": "object",
109 | "properties": {
110 | "$schema": {
111 | "type": "string"
112 | }
113 | },
114 | "required": ["$schema"]
115 | }
116 | }
117 | },
118 | "node.schema.json": {
119 | "$schema": "https://json-schema.org/draft/2020-12/schema",
120 | "$id": "node.schema.json",
121 | "required": ["id", "position"],
122 | "properties": {
123 | "id": {
124 | "type": "string"
125 | },
126 | "position": {
127 | "type": "array",
128 | "items": {
129 | "anyOf": [
130 | {
131 | "type": "number"
132 | }
133 | ]
134 | },
135 | "minItems": 2,
136 | "maxItems": 3
137 | },
138 | "size": {
139 | "type": "array",
140 | "items": {
141 | "anyOf": [
142 | {
143 | "type": "number"
144 | }
145 | ]
146 | },
147 | "minItems": 2,
148 | "maxItems": 3
149 | },
150 | "rotation": {
151 | "type": "number"
152 | },
153 | "properties": {
154 | "type": "array",
155 | "contains": {
156 | "type": "object",
157 | "properties": {
158 | "schema": {
159 | "type": "string"
160 | },
161 | "schema_version": {
162 | "type": "string"
163 | }
164 | },
165 | "required": ["schema", "schema_version"]
166 | },
167 | "items": {
168 | "anyOf": [
169 | {
170 | "type": "object"
171 | }
172 | ]
173 | }
174 | }
175 | }
176 | }
177 | }
178 | }
179 |
--------------------------------------------------------------------------------
/spec/v0.3/schema.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json-schema.org/draft/2020-12/schema",
3 | "title": "OCIF core 0.3",
4 | "description": "The schema for the Open Canvas Interchange Format (OCIF) Core document structure.",
5 | "type": "object",
6 | "properties": {
7 | "ocif": {
8 | "type": "string",
9 | "description": "The URI of the OCIF schema"
10 | },
11 | "nodes": {
12 | "type": "array",
13 | "description": "A list of nodes",
14 | "items": {
15 | "$ref": "#/$defs/node"
16 | }
17 | },
18 | "relations": {
19 | "type": "array",
20 | "description": "A list of relations",
21 | "items": {
22 | "$ref": "#/$defs/relation"
23 | }
24 | },
25 | "resources": {
26 | "type": "array",
27 | "description": "A list of resources",
28 | "items": {
29 | "$ref": "#/$defs/resource"
30 | }
31 | },
32 | "schemas": {
33 | "type": "array",
34 | "description": "Declared schemas",
35 | "items": {
36 | "$ref": "#/$defs/schema"
37 | }
38 | }
39 | },
40 | "$defs": {
41 | "node": {
42 | "type": "object",
43 | "description": "A node in the OCIF document",
44 | "properties": {
45 | "id": {
46 | "type": "string",
47 | "description": "A unique identifier for the node."
48 | },
49 | "position": {
50 | "type": "array",
51 | "description": "Coordinate as (x,y) or (x,y,z).",
52 | "items": {
53 | "type": "number"
54 | }
55 | },
56 | "size": {
57 | "type": "array",
58 | "description": "The size of the node per dimension.",
59 | "items": {
60 | "type": "number"
61 | }
62 | },
63 | "resource": {
64 | "type": "string",
65 | "description": "The resource to display"
66 | },
67 | "data": {
68 | "type": "array",
69 | "description": "Extended node data"
70 | },
71 | "rotation": {
72 | "type": "number",
73 | "description": "+/- 360 degrees"
74 | },
75 | "scale": {
76 | "type": "array",
77 | "description": "Scale factors to resize nodes",
78 | "items": {
79 | "type": "number"
80 | }
81 | }
82 | },
83 | "required": [
84 | "id"
85 | ]
86 | },
87 | "relation": {
88 | "type": "object",
89 | "description": "A relation between nodes",
90 | "properties": {
91 | "id": {
92 | "type": "string",
93 | "description": "A unique identifier for the relation."
94 | },
95 | "data": {
96 | "type": "array",
97 | "description": "Additional data for the relation."
98 | }
99 | },
100 | "required": [
101 | "id"
102 | ]
103 | },
104 | "resource": {
105 | "type": "object",
106 | "description": "A resource in the OCIF document",
107 | "properties": {
108 | "id": {
109 | "type": "string",
110 | "description": "A unique identifier for the resource."
111 | },
112 | "representations": {
113 | "type": "array",
114 | "description": "A list of representations of the resource.",
115 | "items": {
116 | "$ref": "#/$defs/representation"
117 | }
118 | }
119 | },
120 | "required": [
121 | "id",
122 | "representations"
123 | ]
124 | },
125 | "representation": {
126 | "type": "object",
127 | "description": "A representation of a resource. Either content or location MUST be present. If content is used, location must be left out and vice versa.",
128 | "properties": {
129 | "location": {
130 | "type": "string",
131 | "description": "The storage location for the resource. This can be a relative URI for an external resource or an absolute URI for a remote resource. If a data: URI is used, the content and mime-type properties are implicitly defined already. Values in content and mime-type are ignored."
132 | },
133 | "mime-type": {
134 | "type": "string",
135 | "description": "The IANA MIME Type of the resource."
136 | },
137 | "content": {
138 | "type": "string",
139 | "description": "The content of the resource. This is the actual data of the resource as a string. Can be base64-encoded."
140 | }
141 | }
142 | },
143 | "schema": {
144 | "type": "object",
145 | "description": "A schema in the OCIF document",
146 | "properties": {
147 | "uri": {
148 | "type": "string",
149 | "description": "The URI of the schema, Identifier (and location) of the schema."
150 | },
151 | "schema": {
152 | "type": "object",
153 | "description": "The actual JSON schema as a JSON object."
154 | },
155 | "location": {
156 | "type": "string",
157 | "description": "The storage location for the schema."
158 | },
159 | "name": {
160 | "type": "string",
161 | "description": "An optional short name for the schema."
162 | }
163 | },
164 | "required": [
165 | "uri"
166 | ]
167 | }
168 | },
169 | "required": [
170 | "ocif"
171 | ]
172 | }
173 |
--------------------------------------------------------------------------------
/spec/v0.5/schema.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json-schema.org/draft/2020-12/schema",
3 | "title": "OCIF core 0.5",
4 | "description": "The schema for the Open Canvas Interchange Format (OCIF) Core document structure.",
5 | "type": "object",
6 | "properties": {
7 | "ocif": {
8 | "type": "string",
9 | "description": "The URI of the OCIF schema"
10 | },
11 | "nodes": {
12 | "type": "array",
13 | "description": "A list of nodes",
14 | "items": {
15 | "$ref": "#/$defs/node"
16 | }
17 | },
18 | "relations": {
19 | "type": "array",
20 | "description": "A list of relations",
21 | "items": {
22 | "$ref": "#/$defs/relation"
23 | }
24 | },
25 | "resources": {
26 | "type": "array",
27 | "description": "A list of resources",
28 | "items": {
29 | "$ref": "#/$defs/resource"
30 | }
31 | },
32 | "schemas": {
33 | "type": "array",
34 | "description": "Declared schemas",
35 | "items": {
36 | "$ref": "#/$defs/schema"
37 | }
38 | }
39 | },
40 | "$defs": {
41 | "node": {
42 | "type": "object",
43 | "description": "A node in the OCIF document",
44 | "properties": {
45 | "id": {
46 | "type": "string",
47 | "description": "A unique identifier for the node."
48 | },
49 | "position": {
50 | "type": "array",
51 | "description": "Coordinate as (x,y) or (x,y,z).",
52 | "items": {
53 | "type": "number"
54 | }
55 | },
56 | "size": {
57 | "type": "array",
58 | "description": "The size of the node per dimension.",
59 | "items": {
60 | "type": "number"
61 | }
62 | },
63 | "resource": {
64 | "type": "string",
65 | "description": "The resource to display"
66 | },
67 | "resourceFit": {
68 | "type": "string",
69 | "description": "Fitting resource in item",
70 | "enum": ["none", "containX", "containY", "contain", "cover", "fill", "tile"]
71 | },
72 | "data": {
73 | "type": "array",
74 | "description": "Extended node data"
75 | },
76 | "rotation": {
77 | "type": "number",
78 | "description": "+/- 360 degrees"
79 | },
80 | "relation": {
81 | "type": "string",
82 | "description": "ID of a relation"
83 | }
84 | },
85 | "required": ["id"]
86 | },
87 | "relation": {
88 | "type": "object",
89 | "description": "A relation between nodes",
90 | "properties": {
91 | "id": {
92 | "type": "string",
93 | "description": "A unique identifier for the relation."
94 | },
95 | "data": {
96 | "type": "array",
97 | "description": "Additional data for the relation."
98 | },
99 | "node": {
100 | "type": "string",
101 | "description": "ID of a visual node, which represents this relation."
102 | }
103 | },
104 | "required": ["id"]
105 | },
106 | "resource": {
107 | "type": "object",
108 | "description": "A resource in the OCIF document",
109 | "properties": {
110 | "id": {
111 | "type": "string",
112 | "description": "A unique identifier for the resource."
113 | },
114 | "representations": {
115 | "type": "array",
116 | "description": "A list of representations of the resource.",
117 | "items": {
118 | "$ref": "#/$defs/representation"
119 | }
120 | }
121 | },
122 | "required": ["id", "representations"]
123 | },
124 | "representation": {
125 | "type": "object",
126 | "description": "A representation of a resource. Either content or location MUST be present. If content is used, location must be left out and vice versa.",
127 | "properties": {
128 | "location": {
129 | "type": "string",
130 | "description": "The storage location for the resource. This can be a relative URI for an external resource or an absolute URI for a remote resource. If a data: URI is used, the content and MIME-type properties are implicitly defined already. Values in content and mime-type are ignored."
131 | },
132 | "mimeType": {
133 | "type": "string",
134 | "description": "The IANA MIME Type of the resource."
135 | },
136 | "content": {
137 | "type": "string",
138 | "description": "The content of the resource. This is the actual data of the resource as a string. Can be base64-encoded."
139 | }
140 | }
141 | },
142 | "schema": {
143 | "type": "object",
144 | "description": "A schema in the OCIF document",
145 | "properties": {
146 | "uri": {
147 | "type": "string",
148 | "description": "The URI of the schema, Identifier (and location) of the schema."
149 | },
150 | "schema": {
151 | "type": "object",
152 | "description": "The actual JSON schema as a JSON object."
153 | },
154 | "location": {
155 | "type": "string",
156 | "description": "The storage location for the schema."
157 | },
158 | "name": {
159 | "type": "string",
160 | "description": "An optional short name for the schema."
161 | }
162 | },
163 | "required": ["uri"]
164 | }
165 | },
166 | "required": ["ocif"]
167 | }
168 |
--------------------------------------------------------------------------------
/spec/v0.6/schema.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json-schema.org/draft/2020-12/schema",
3 | "title": "OCIF core 0.5",
4 | "description": "The schema for the Open Canvas Interchange Format (OCIF) Core document structure.",
5 | "type": "object",
6 | "properties": {
7 | "ocif": {
8 | "type": "string",
9 | "description": "The URI of the OCIF schema"
10 | },
11 | "nodes": {
12 | "type": "array",
13 | "description": "A list of nodes",
14 | "items": {
15 | "$ref": "#/$defs/node"
16 | }
17 | },
18 | "relations": {
19 | "type": "array",
20 | "description": "A list of relations",
21 | "items": {
22 | "$ref": "#/$defs/relation"
23 | }
24 | },
25 | "resources": {
26 | "type": "array",
27 | "description": "A list of resources",
28 | "items": {
29 | "$ref": "#/$defs/resource"
30 | }
31 | },
32 | "schemas": {
33 | "type": "array",
34 | "description": "Declared schemas",
35 | "items": {
36 | "$ref": "#/$defs/schema"
37 | }
38 | }
39 | },
40 | "$defs": {
41 | "node": {
42 | "type": "object",
43 | "description": "A node in the OCIF document",
44 | "properties": {
45 | "id": {
46 | "type": "string",
47 | "description": "A unique identifier for the node."
48 | },
49 | "position": {
50 | "type": "array",
51 | "description": "Coordinate as (x,y) or (x,y,z).",
52 | "items": {
53 | "type": "number"
54 | }
55 | },
56 | "size": {
57 | "type": "array",
58 | "description": "The size of the node per dimension.",
59 | "items": {
60 | "type": "number"
61 | }
62 | },
63 | "resource": {
64 | "type": "string",
65 | "description": "The resource to display"
66 | },
67 | "resourceFit": {
68 | "type": "string",
69 | "description": "Fitting resource in item",
70 | "enum": ["none", "containX", "containY", "contain", "cover", "fill", "tile"]
71 | },
72 | "data": {
73 | "type": "array",
74 | "description": "Extended node data"
75 | },
76 | "rotation": {
77 | "type": "number",
78 | "description": "+/- 360 degrees"
79 | },
80 | "relation": {
81 | "type": "string",
82 | "description": "ID of a relation"
83 | }
84 | },
85 | "required": ["id"]
86 | },
87 | "relation": {
88 | "type": "object",
89 | "description": "A relation between nodes",
90 | "properties": {
91 | "id": {
92 | "type": "string",
93 | "description": "A unique identifier for the relation."
94 | },
95 | "data": {
96 | "type": "array",
97 | "description": "Additional data for the relation."
98 | },
99 | "node": {
100 | "type": "string",
101 | "description": "ID of a visual node, which represents this relation."
102 | }
103 | },
104 | "required": ["id"]
105 | },
106 | "resource": {
107 | "type": "object",
108 | "description": "A resource in the OCIF document",
109 | "properties": {
110 | "id": {
111 | "type": "string",
112 | "description": "A unique identifier for the resource."
113 | },
114 | "representations": {
115 | "type": "array",
116 | "description": "A list of representations of the resource.",
117 | "items": {
118 | "$ref": "#/$defs/representation"
119 | }
120 | }
121 | },
122 | "required": ["id", "representations"]
123 | },
124 | "representation": {
125 | "type": "object",
126 | "description": "A representation of a resource. Either content or location MUST be present. If content is used, location must be left out and vice versa.",
127 | "properties": {
128 | "location": {
129 | "type": "string",
130 | "description": "The storage location for the resource. This can be a relative URI for an external resource or an absolute URI for a remote resource. If a data: URI is used, the content and MIME-type properties are implicitly defined already. Values in content and mime-type are ignored."
131 | },
132 | "mimeType": {
133 | "type": "string",
134 | "description": "The IANA MIME Type of the resource."
135 | },
136 | "content": {
137 | "type": "string",
138 | "description": "The content of the resource. This is the actual data of the resource as a string. Can be base64-encoded."
139 | }
140 | },
141 | "oneOf": [
142 | {
143 | "required": ["content"]
144 | },
145 | {
146 | "required": ["location"]
147 | }
148 | ]
149 | },
150 | "schema": {
151 | "type": "object",
152 | "description": "A schema in the OCIF document",
153 | "properties": {
154 | "uri": {
155 | "type": "string",
156 | "description": "The URI of the schema, Identifier (and location) of the schema."
157 | },
158 | "schema": {
159 | "type": "object",
160 | "description": "The actual JSON schema as a JSON object."
161 | },
162 | "location": {
163 | "type": "string",
164 | "description": "The storage location for the schema."
165 | },
166 | "name": {
167 | "type": "string",
168 | "description": "An optional short name for the schema."
169 | }
170 | },
171 | "required": ["uri"]
172 | }
173 | },
174 | "required": ["ocif"]
175 | }
176 |
--------------------------------------------------------------------------------
/catalog.md:
--------------------------------------------------------------------------------
1 | # OCIF Extensions List
2 |
3 | This document lists known OCIF extensions ([see OCIF spec](spec/v0.6/spec.md)) as service to the community.
4 | Getting listed in this file is not a requirement for an extension to be used in an OCIF document.
5 | Extensions can be defined within an OCIF document, without an external schema file.
6 | It is common practice for reusable extensions to publish a JSON schema file and a README.
7 | All this is described in the OCIF spec.
8 |
9 | Listing in this file is not an endorsement of an extension.
10 | This list is merely a convenience for developers and users to find extensions that might be useful for their use case.
11 |
12 | Mandatory fields in this catalog:
13 |
14 | - **URI**: The URI of the extension (its unique id).
15 | - **Schema**: Link to JSON schema.
16 | - Can be omitted if URI points to a JSON schema.
17 | - **Documentation**: A link to the documentation, describing the semantics and usage of the extension.
18 |
19 | Optional fields:
20 |
21 | - **Name**: The proposed short name of the extension.
22 | * [OCIF Extensions List](#ocif-extensions-list)
23 | * [Node Extensions](#node-extensions)
24 | * [Arrow Node](#arrow-node)
25 | * [Oval Node](#oval-node)
26 | * [Path Node](#path-node)
27 | * [Rectangle Node](#rectangle-node)
28 | * [Ports Node](#ports-node)
29 | * [Anchored Node](#anchored-node)
30 | * [Text Style Node](#text-style-node)
31 | * [Transforms Node](#transforms-node)
32 | * [Relation Extensions](#relation-extensions)
33 | * [Edge Relation](#edge-relation)
34 | * [Group Relation](#group-relation)
35 | * [Hyperedge Relation](#hyperedge-relation)
36 | * [Parent-Child Relation](#parent-child-relation)
37 | * [Deprecated Extensions](#deprecated-extensions)
38 | peredge-relation)
39 | - [Parent-Child Relation](#parent-child-relation)
40 | - [Deprecated Extensions](#deprecated-extensions)
41 |
42 |
43 | # Node Extensions
44 |
45 | ## Arrow Node
46 |
47 | - Name: `@ocif/node/arrow`
48 | - URI: `https://spec.canvasprotocol.org/v0.5/core/arrow-node.json`
49 | - Doc: [spec/v0.6/spec.md#arrow](spec/v0.6/spec.md#arrow-extension)
50 | - Version: v0.5
51 | - Author: Open Canvas Working Group
52 |
53 | ## Oval Node
54 |
55 | - Name: `@ocif/node/oval`
56 | - URI: `https://spec.canvasprotocol.org/v0.5/core/oval-node.json`
57 | - Doc: [spec/v0.6/spec.md#oval](spec/v0.6/spec.md#oval-extension)
58 | - Version: v0.5
59 | - Author: Open Canvas Working Group
60 |
61 | ## Path Node
62 |
63 | - Name: `@ocif/node/path`
64 | - URI: `https://spec.canvasprotocol.org/v0.5/core/path-node.json`
65 | - Doc: [spec/v0.6/spec.md#path](spec/v0.6/spec.md#path-extension)
66 | - Version: v0.5
67 | - Author: Open Canvas Working Group
68 |
69 | ## Rectangle Node
70 |
71 | - Name: `@ocif/node/rect`
72 | - URI: `https://spec.canvasprotocol.org/v0.5/core/rect-node.json`
73 | - Doc: [spec/v0.6/spec.md#rectangle](spec/v0.6/spec.md#rectangle-extension)
74 | - Version: v0.5
75 | - Author: Open Canvas Working Group
76 |
77 | ## Ports Node
78 |
79 | - Name: `@ocif/node/ports`
80 | - URI: `https://spec.canvasprotocol.org/v0.5/extensions/ports-node.json`
81 | - Doc: [spec/v0.6/spec.md#ports-node](spec/v0.6/spec.md#ports-node-extension)
82 | - Version: v0.5
83 | - Author: Open Canvas Working Group
84 |
85 | ## Anchored Node
86 |
87 | - Name: `@ocif/node/anchored`
88 | - URI: `https://spec.canvasprotocol.org/v0.5/extensions/anchored-node.json`
89 | - Doc: [spec/v0.6/spec.md#anchored-node](spec/v0.6/spec.md#anchored-node-extension)
90 | - Version: v0.5
91 | - Author: Open Canvas Working Group
92 |
93 | ## Text Style Node
94 |
95 | - Name: `@ocif/node/textstyle`
96 | - URI: `https://spec.canvasprotocol.org/v0.5/extensions/textstyle-node.json`
97 | - Doc: [spec/v0.6/spec.md#text-style-node](spec/v0.6/spec.md#text-style-node-extension)
98 | - Version: v0.5
99 | - Author: Open Canvas Working Group
100 |
101 | ## Transforms Node
102 |
103 | - Name: `@ocif/node/transforms`
104 | - URI: `https://spec.canvasprotocol.org/v0.5/extensions/transforms-node.json`
105 | - Doc: [spec/v0.6/spec.md#transforms-node](spec/v0.6/spec.md#node-transforms-extension)
106 | - Version: v0.5
107 | - Author: Open Canvas Working Group
108 |
109 | # Relation Extensions
110 |
111 | ## Edge Relation
112 |
113 | - Name: `@ocif/rel/edge`
114 | - URI: `https://spec.canvasprotocol.org/v0.5/core/edge-rel.json`
115 | - Doc: [spec/v0.6/spec.md#edge-relation](spec/v0.6/spec.md#edge-relation-extension)
116 | - Version: v0.5
117 | - Author: Open Canvas Working Group
118 |
119 | ## Group Relation
120 |
121 | - Name: `@ocif/rel/group`
122 | - URI: `https://spec.canvasprotocol.org/v0.5/core/group-rel.json`
123 | - Doc: [spec/v0.6/spec.md#group-relation](spec/v0.6/spec.md#group-relation-extension)
124 | - Version: v0.5
125 | - Author: Open Canvas Working Group
126 |
127 | ## Hyperedge Relation
128 |
129 | - Name: `@ocif/rel/hyperedge`
130 | - URI: `https://spec.canvasprotocol.org/v0.5/extensions/hyperedge-rel.json`
131 | - Doc: [spec/v0.6/spec.md#hyperedge-relation](spec/v0.6/spec.md#hyperedge-relation-extension)
132 | - Version: v0.5
133 | - Author: Open Canvas Working Group
134 |
135 | ## Parent-Child Relation
136 |
137 | - Name: `@ocif/rel/parent-child`
138 | - URI: `https://spec.canvasprotocol.org/v0.5/extensions/parent-child-rel.json`
139 | - Doc: [spec/v0.6/spec.md#parent-child-relation](spec/v0.6/spec.md#parent-child-relation-extension)
140 | - Version: v0.5
141 | - Author: Open Canvas Working Group
142 |
143 | # Deprecated Extensions
144 |
145 | The authors of these extensions have deprecated them. Often this means that the functionality has been replaced by a newer version of the extension.
146 |
147 | - `https://spec.canvasprotocol.org/node/ports/0.2`
148 | - `https://spec.canvasprotocol.org/rel/hyperedge/0.2`
149 | - `https://spec.canvasprotocol.org/rel/parent-child/0.2`
150 | - `https://spec.canvasprotocol.org/rel/relative/0.2`
151 | - `@ocif/rel/set` - functionality merged into `@ocif/rel/group` in v0.5
152 | - `@ocif/node/relative` - functionality moved to `@ocif/node/transforms` in v0.5
153 |
--------------------------------------------------------------------------------
/cookbook.md:
--------------------------------------------------------------------------------
1 | # OCIF Cookbook
2 | **Open Canvas Interchange Format (OCIF) Cookbook**
3 |
4 |
5 | * [OCIF Cookbook](#ocif-cookbook)
6 | * [Canvas App Users](#canvas-app-users)
7 | * [Canvas Editing in a Text Editor](#canvas-editing-in-a-text-editor)
8 | * [The Simple Sticky Note 📝](#the-simple-sticky-note-)
9 | * [Canvas App Developers](#canvas-app-developers)
10 | * [Using OCIF Core](#using-ocif-core)
11 | * [Using OCIF Extensions](#using-ocif-extensions)
12 | * [Transclusion](#transclusion)
13 | * [Developing OCIF Extensions](#developing-ocif-extensions)
14 |
15 |
16 | A collection of examples and best practices (recipes) for using OCIF.
17 | It is intended to help developers and designers to understand how to use OCIF in their applications.
18 |
19 | ## Canvas App Users
20 |
21 | Your canvas app might have either an OCIF export option or an OCIF import option.
22 | It might even use OCIF as its native format.
23 | You can export OCIF from one canvas app and import it back into another canvas app.
24 | This way, you can share your work with others or use it in different apps.
25 |
26 | OCIF defines no mechanism how the file is transported.
27 | If your canvas app does not support OCIF, consider opening a ticket with the developers to request support for OCIF.
28 | Or be the developer yourself and use the OCIF documentation to implement support for OCIF in your favorite app.
29 |
30 | ## Canvas Editing in a Text Editor
31 | Here are some examples, how to create an OCIF file by hand. This helps to understand OCIF and lets you learn how to edit and tweak existing OCIF files, created by apps.
32 |
33 | ### The Simple Sticky Note 📝
34 | Scenario: You want to represent a single yellow sticky note on a digital canvas with the text "Buy milk". This is the most basic "Hello, World!" example for OCIF.
35 |
36 | OCIF Concepts Illustrated:
37 |
38 | - nodes: A single visual item on the canvas.
39 | - resources: The content for the node (the text).
40 | - position: Where the note is placed on the canvas.
41 | - Core Structure: The basic boilerplate of an OCIF file.
42 |
43 | ```json
44 | {
45 | "ocif": "https://canvasprotocol.org/ocif/v0.6",
46 | "nodes": [
47 | {
48 | "id": "sticky-note-milk",
49 | "position": [ 100, 100 ],
50 | "size": [ 150, 75 ],
51 | "resource": "res_milk",
52 | "data": [
53 | {
54 | "type": "@ocif/node/rect",
55 | "fillColor": "#FFFF00"
56 | }
57 | ]
58 | }
59 | ],
60 | "resources": [
61 | {
62 | "id": "res_milk",
63 | "representations": [
64 | {
65 | "mimeType": "text/plain",
66 | "content": "Buy milk"
67 | }
68 | ]
69 | }
70 | ]
71 | }
72 | ```
73 |
74 | Example Breakdown:
75 | - The file has a nodes array with one node object. This node has an id, a position like [100, 100], and a resource property pointing to the ID `res_milk`.
76 | - The resources array contains an object with the id: `res_milk` and its representations holding the inline content: `{"mimeType": "text/plain", "content": "Buy milk"}`.
77 | - A `@ocif/node/rect` extension has been added to the node's data to give it a yellow `fillColor`.
78 |
79 |
80 |
81 |
82 | ## Canvas App Developers
83 |
84 | If you are developing a canvas app, consider supporting OCIF as an import and export format.
85 | This way, users can share their work with others or use it in different apps.
86 |
87 | ### Using OCIF Core
88 |
89 | The OCIF core provides the building blocks which the working group found to be shared by many canvas apps.
90 | Technically, the OCIF core also uses the node and relation extension mechanism.
91 |
92 | ### Using OCIF Extensions
93 |
94 | Usage of non-core extensions is technically almost identical to the core extensions.
95 | The only difference is the schema.
96 | Non-core extensions SHOULD provide their schema, either within the OCIF document or at the extension URI.
97 | Extensions can be defined and used in the same OCIF document, without any prior announcement or registration process.
98 |
99 | ### Transclusion
100 |
101 | **Goal** \
102 | One visual node shows the [resource](spec/v0.4/spec.md#resources) of another visual node.
103 | This concept is also known as _transclusion_.
104 |
105 | We have two roles:
106 |
107 | - An _original_ node -- just any existing visual node, and
108 | - a _holder_ node -- the node showing the content of the original node.
109 |
110 | **Solution** \
111 | We use a [parent-child relation](spec/v0.6/spec.md#parent-child-relation-extension) to indicate that the holder node is a child of the original node.
112 | The original node (the parent) is included in the holder node (the child).
113 | Or in other words: The child inherits the properties (the resource) of the parent.
114 |
115 | **Example: A shows content of B** \
116 | Given two nodes A and B, we want node A to show the content (resource) of B, but at another position on the canvas.
117 | This is a special case of the parent-child relation, where the child inherits the _resource_ property of the parent.
118 | We use the `inherit` flag to indicate that the child should inherit the properties of the parent.
119 | Node A could additionally specify a different size if it wants to show the content of node B in a different size.
120 |
121 | ```json
122 | {
123 | "nodes": [
124 | {
125 | "id": "A",
126 | "position": [100, 100]
127 | },
128 | {
129 | "id": "B",
130 | "position": [300, 200],
131 | "size": [20, 20],
132 | "resource": "r1"
133 | }
134 | ],
135 | "relations": [
136 | {
137 | "type": "@ocif/rel/parent-child",
138 | "parent": "B",
139 | "child": "A",
140 | "inherit": true
141 | }
142 | ],
143 | "resources": [
144 | {
145 | "id": "r1",
146 | "representations": [
147 | {
148 | "mime-type": "text/plain",
149 | "content": "Hello, World!"
150 | }
151 | ]
152 | }
153 | ]
154 | }
155 | ```
156 |
157 | ### Developing OCIF Extensions
158 |
159 | OCIF extensions ([see spec](https://spec.canvasprotocol.org/)) allow defining custom "property bags" for visual nodes or relations.
160 | If the built-in types (technically also using the extension mechanism) are not enough, you should define your own extension.
161 | Keep in mind that nodes and relations can use several extensions at the same time, so only the missing pieces need to be defined in your extension.
162 | The spec defines how to define custom extensions.
163 |
--------------------------------------------------------------------------------
/spec/v0.6.1-draft/schema.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json-schema.org/draft/2020-12/schema",
3 | "title": "OCIF core v0.6.1",
4 | "description": "The schema for the Open Canvas Interchange Format (OCIF) Core document structure.",
5 | "type": "object",
6 | "allOf": [{ "$ref": "#/$defs/item" }],
7 | "properties": {
8 | "ocif": {
9 | "type": "string",
10 | "description": "The URI of the OCIF schema"
11 | },
12 | "nodes": {
13 | "type": "array",
14 | "description": "A list of nodes",
15 | "items": {
16 | "$ref": "#/$defs/node"
17 | }
18 | },
19 | "relations": {
20 | "type": "array",
21 | "description": "A list of relations",
22 | "items": {
23 | "$ref": "#/$defs/relation"
24 | }
25 | },
26 | "resources": {
27 | "type": "array",
28 | "description": "A list of resources",
29 | "items": {
30 | "$ref": "#/$defs/resource"
31 | }
32 | },
33 | "schemas": {
34 | "type": "array",
35 | "description": "Declared schemas",
36 | "items": {
37 | "$ref": "#/$defs/schema"
38 | }
39 | }
40 | },
41 | "$defs": {
42 | "entity": {
43 | "type": "object",
44 | "description": "Abstract base type for all extensible entities in the OCIF document (extension data and comments).",
45 | "properties": {
46 | "data": {
47 | "type": "array",
48 | "description": "Data for the entity, including data supplied by extension schemas."
49 | },
50 | "comment": {
51 | "type": "string",
52 | "description": "A comment or description of the entity. This MUST NOT be used for any functional purpose by software that processes OCIF files, it is only allowed to be used for the purpose of annotating an example OCIF file with additional information when reading the raw text of the file manually, like a comment in a programming language."
53 | }
54 | }
55 | },
56 | "item": {
57 | "type": "object",
58 | "description": "Abstract base type for identified items in the OCIF document (includes entity properties).",
59 | "allOf": [{ "$ref": "#/$defs/entity" }],
60 | "properties": {
61 | "id": {
62 | "type": "string",
63 | "description": "A unique identifier for the item."
64 | }
65 | }
66 | },
67 | "node": {
68 | "type": "object",
69 | "description": "A node in the OCIF document",
70 | "allOf": [{ "$ref": "#/$defs/item" }],
71 | "properties": {
72 | "position": {
73 | "type": "array",
74 | "description": "Coordinate as (x,y) or (x,y,z).",
75 | "items": {
76 | "type": "number"
77 | }
78 | },
79 | "size": {
80 | "type": "array",
81 | "description": "The size of the node per dimension.",
82 | "items": {
83 | "type": "number"
84 | }
85 | },
86 | "resource": {
87 | "type": "string",
88 | "description": "The resource to display"
89 | },
90 | "resourceFit": {
91 | "type": "string",
92 | "description": "Fitting resource in item",
93 | "enum": ["none", "containX", "containY", "contain", "cover", "fill", "tile"]
94 | },
95 | "rotation": {
96 | "type": "number",
97 | "description": "+/- 360 degrees"
98 | },
99 | "relation": {
100 | "type": "string",
101 | "description": "ID of a relation"
102 | }
103 | },
104 | "required": ["id"]
105 | },
106 | "relation": {
107 | "type": "object",
108 | "description": "A relation between nodes",
109 | "allOf": [{ "$ref": "#/$defs/item" }],
110 | "properties": {
111 | "node": {
112 | "type": "string",
113 | "description": "ID of a visual node, which represents this relation."
114 | }
115 | },
116 | "required": ["id"]
117 | },
118 | "resource": {
119 | "type": "object",
120 | "description": "A resource in the OCIF document",
121 | "allOf": [{ "$ref": "#/$defs/item" }],
122 | "properties": {
123 | "representations": {
124 | "type": "array",
125 | "description": "A list of representations of the resource.",
126 | "items": {
127 | "$ref": "#/$defs/representation"
128 | }
129 | }
130 | },
131 | "required": ["id", "representations"]
132 | },
133 | "representation": {
134 | "type": "object",
135 | "description": "A representation of a resource. Either content or location MUST be present. If content is used, location must be left out and vice versa.",
136 | "allOf": [{ "$ref": "#/$defs/entity" }],
137 | "properties": {
138 | "location": {
139 | "type": "string",
140 | "description": "The storage location for the resource. This can be a relative URI for an external resource or an absolute URI for a remote resource. If a data: URI is used, the content and MIME-type properties are implicitly defined already. Values in content and mime-type are ignored."
141 | },
142 | "mimeType": {
143 | "type": "string",
144 | "description": "The IANA MIME Type of the resource."
145 | },
146 | "content": {
147 | "type": "string",
148 | "description": "The content of the resource. This is the actual data of the resource as a string. Can be base64-encoded."
149 | }
150 | },
151 | "oneOf": [
152 | {
153 | "required": ["content"]
154 | },
155 | {
156 | "required": ["location"]
157 | }
158 | ]
159 | },
160 | "schema": {
161 | "type": "object",
162 | "description": "A schema in the OCIF document",
163 | "properties": {
164 | "uri": {
165 | "type": "string",
166 | "description": "The URI of the schema, Identifier (and location) of the schema."
167 | },
168 | "schema": {
169 | "type": "object",
170 | "description": "The actual JSON schema as a JSON object."
171 | },
172 | "location": {
173 | "type": "string",
174 | "description": "The storage location for the schema."
175 | },
176 | "name": {
177 | "type": "string",
178 | "description": "An optional short name for the schema."
179 | }
180 | },
181 | "required": ["uri"]
182 | }
183 | },
184 | "required": ["ocif"]
185 | }
186 |
--------------------------------------------------------------------------------
/example/4x4-rect-node-grid.ocif.json:
--------------------------------------------------------------------------------
1 | {
2 | "ocif": "https://canvasprotocol.org/ocif/v0.5",
3 | "nodes": [
4 | {
5 | "id": "n1",
6 | "position": [0, 0],
7 | "size": [150, 150],
8 | "resource": "r1",
9 | "data": [
10 | {
11 | "type": "@ocif/node/rect",
12 | "strokeColor": "#FF5733",
13 | "strokeWidth": 4,
14 | "fillColor": "#C13D26"
15 | }
16 | ]
17 | },
18 | {
19 | "id": "n2",
20 | "position": [160, 0],
21 | "size": [150, 150],
22 | "resource": "r2",
23 | "data": [
24 | {
25 | "type": "@ocif/node/rect",
26 | "strokeColor": "#33FF57",
27 | "strokeWidth": 4,
28 | "fillColor": "#269E3D"
29 | }
30 | ]
31 | },
32 | {
33 | "id": "n3",
34 | "position": [320, 0],
35 | "size": [150, 150],
36 | "resource": "r3",
37 | "data": [
38 | {
39 | "type": "@ocif/node/rect",
40 | "strokeColor": "#3357FF",
41 | "strokeWidth": 4,
42 | "fillColor": "#263DA6"
43 | }
44 | ]
45 | },
46 | {
47 | "id": "n4",
48 | "position": [480, 0],
49 | "size": [150, 150],
50 | "resource": "r4",
51 | "data": [
52 | {
53 | "type": "@ocif/node/rect",
54 | "strokeColor": "#FF33A1",
55 | "strokeWidth": 4,
56 | "fillColor": "#A1266E"
57 | }
58 | ]
59 | },
60 | {
61 | "id": "n5",
62 | "position": [0, 160],
63 | "size": [150, 150],
64 | "resource": "r5",
65 | "data": [
66 | {
67 | "type": "@ocif/node/rect",
68 | "strokeColor": "#FFC733",
69 | "strokeWidth": 4,
70 | "fillColor": "#C19B26"
71 | }
72 | ]
73 | },
74 | {
75 | "id": "n6",
76 | "position": [160, 160],
77 | "size": [150, 150],
78 | "resource": "r6",
79 | "data": [
80 | {
81 | "type": "@ocif/node/rect",
82 | "strokeColor": "#8D33FF",
83 | "strokeWidth": 4,
84 | "fillColor": "#6626A6"
85 | }
86 | ]
87 | },
88 | {
89 | "id": "n7",
90 | "position": [320, 160],
91 | "size": [150, 150],
92 | "resource": "r7",
93 | "data": [
94 | {
95 | "type": "@ocif/node/rect",
96 | "strokeColor": "#33FFF6",
97 | "strokeWidth": 4,
98 | "fillColor": "#269EB5"
99 | }
100 | ]
101 | },
102 | {
103 | "id": "n8",
104 | "position": [480, 160],
105 | "size": [150, 150],
106 | "resource": "r8",
107 | "data": [
108 | {
109 | "type": "@ocif/node/rect",
110 | "strokeColor": "#FFB733",
111 | "strokeWidth": 4,
112 | "fillColor": "#C18A26"
113 | }
114 | ]
115 | },
116 | {
117 | "id": "n9",
118 | "position": [0, 320],
119 | "size": [150, 150],
120 | "resource": "r9",
121 | "data": [
122 | {
123 | "type": "@ocif/node/rect",
124 | "strokeColor": "#33FFB7",
125 | "strokeWidth": 4,
126 | "fillColor": "#269E85"
127 | }
128 | ]
129 | },
130 | {
131 | "id": "n10",
132 | "position": [160, 320],
133 | "size": [150, 150],
134 | "resource": "r10",
135 | "data": [
136 | {
137 | "type": "@ocif/node/rect",
138 | "strokeColor": "#A1FF33",
139 | "strokeWidth": 4,
140 | "fillColor": "#6EA626"
141 | }
142 | ]
143 | },
144 | {
145 | "id": "n11",
146 | "position": [320, 320],
147 | "size": [150, 150],
148 | "resource": "r11",
149 | "data": [
150 | {
151 | "type": "@ocif/node/rect",
152 | "strokeColor": "#FF338D",
153 | "strokeWidth": 4,
154 | "fillColor": "#A62663"
155 | }
156 | ]
157 | },
158 | {
159 | "id": "n12",
160 | "position": [480, 320],
161 | "size": [150, 150],
162 | "resource": "r12",
163 | "data": [
164 | {
165 | "type": "@ocif/node/rect",
166 | "strokeColor": "#33A1FF",
167 | "strokeWidth": 4,
168 | "fillColor": "#2672A6"
169 | }
170 | ]
171 | },
172 | {
173 | "id": "n13",
174 | "position": [0, 480],
175 | "size": [150, 150],
176 | "resource": "r13",
177 | "data": [
178 | {
179 | "type": "@ocif/node/rect",
180 | "strokeColor": "#F633FF",
181 | "strokeWidth": 4,
182 | "fillColor": "#B526C1"
183 | }
184 | ]
185 | },
186 | {
187 | "id": "n14",
188 | "position": [160, 480],
189 | "size": [150, 150],
190 | "resource": "r14",
191 | "data": [
192 | {
193 | "type": "@ocif/node/rect",
194 | "strokeColor": "#FF9E33",
195 | "strokeWidth": 4,
196 | "fillColor": "#C17826"
197 | }
198 | ]
199 | },
200 | {
201 | "id": "n15",
202 | "position": [320, 480],
203 | "size": [150, 150],
204 | "resource": "r15",
205 | "data": [
206 | {
207 | "type": "@ocif/node/rect",
208 | "strokeColor": "#33FF73",
209 | "strokeWidth": 4,
210 | "fillColor": "#269E53"
211 | }
212 | ]
213 | },
214 | {
215 | "id": "n16",
216 | "position": [480, 480],
217 | "size": [150, 150],
218 | "resource": "r16",
219 | "data": [
220 | {
221 | "type": "@ocif/node/rect",
222 | "strokeColor": "#FF33E4",
223 | "strokeWidth": 4,
224 | "fillColor": "#A626AB"
225 | }
226 | ]
227 | }
228 | ],
229 | "resources": [
230 | {
231 | "id": "r1",
232 | "representations": [{ "mime-type": "text/plain", "content": "Hello" }]
233 | },
234 | {
235 | "id": "r2",
236 | "representations": [{ "mime-type": "text/plain", "content": "World" }]
237 | },
238 | {
239 | "id": "r3",
240 | "representations": [{ "mime-type": "text/plain", "content": "Fun Day" }]
241 | },
242 | {
243 | "id": "r4",
244 | "representations": [{ "mime-type": "text/plain", "content": "Great" }]
245 | },
246 | {
247 | "id": "r5",
248 | "representations": [{ "mime-type": "text/plain", "content": "Bright" }]
249 | },
250 | {
251 | "id": "r6",
252 | "representations": [{ "mime-type": "text/plain", "content": "Cool" }]
253 | },
254 | {
255 | "id": "r7",
256 | "representations": [{ "mime-type": "text/plain", "content": "Ocean" }]
257 | },
258 | {
259 | "id": "r8",
260 | "representations": [{ "mime-type": "text/plain", "content": "Sunset" }]
261 | },
262 | {
263 | "id": "r9",
264 | "representations": [{ "mime-type": "text/plain", "content": "Grass" }]
265 | },
266 | {
267 | "id": "r10",
268 | "representations": [{ "mime-type": "text/plain", "content": "Fresh" }]
269 | },
270 | {
271 | "id": "r11",
272 | "representations": [{ "mime-type": "text/plain", "content": "Love" }]
273 | },
274 | {
275 | "id": "r12",
276 | "representations": [{ "mime-type": "text/plain", "content": "Sky" }]
277 | },
278 | {
279 | "id": "r13",
280 | "representations": [{ "mime-type": "text/plain", "content": "Dream" }]
281 | },
282 | {
283 | "id": "r14",
284 | "representations": [{ "mime-type": "text/plain", "content": "Orange" }]
285 | },
286 | {
287 | "id": "r15",
288 | "representations": [{ "mime-type": "text/plain", "content": "Leaf" }]
289 | },
290 | {
291 | "id": "r16",
292 | "representations": [{ "mime-type": "text/plain", "content": "Star" }]
293 | }
294 | ],
295 | "relations": [],
296 | "schemas": []
297 | }
298 |
--------------------------------------------------------------------------------
/design/diagram-files.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/spec/v0.4/extensions.md:
--------------------------------------------------------------------------------
1 | # OCIF Extensions
2 |
3 | Copyright © 2025 the Contributors to the Open Canvas Working Group (OCWG).
4 |
5 | Versioning Policy: Currently, all extensions in this document are at version **0.4**.
6 |
7 | ## Abstract
8 |
9 | This document describes extensions to the Open Canvas Interchange Format (OCIF) that are not part of the core specification. Extensions are optional and may be used to add additional functionality to OCIF documents.
10 | See the [OCIF Core Specification](./spec) for the core features of OCIF.
11 |
12 | ## Status of this Document
13 |
14 | This document is an editor's draft and has no official standing. It is a work in progress and may be updated, replaced, or obsoleted by other documents at any time.
15 |
16 | **Legal**:
17 | Open Canvas Interchange Format (OCIF) v0.4 © 2025 by Open Canvas Working Group is licensed under CC BY-SA 4.0. To view a copy of this licence, visit https://creativecommons.org/licenses/by-sa/4.0/
18 |
19 | ### Table of Contents
20 |
21 |
22 |
23 | - [OCIF Extensions](#ocif-extensions)
24 | - [Abstract](#abstract)
25 | - [Status of this Document](#status-of-this-document)
26 | - [Table of Contents](#table-of-contents)
27 | - [Node Extensions](#node-extensions)
28 | - [Ports Node](#ports-node)
29 | - [Relative Node](#relative-node)
30 | - [Relation Extensions](#relation-extensions)
31 | - [Hyperedge Relation](#hyperedge-relation)
32 | - [Parent-Child Relation](#parent-child-relation)
33 | - [Changes](#changes)
34 |
35 |
36 | # Node Extensions
37 |
38 | These are [extension](spec.md#extensions) that can be added to nodes in an OCIF document.
39 | To be placed inside the `data` `array`.
40 |
41 | ## Ports Node
42 |
43 | - Name: `@ocif/node/ports`
44 | - URI: `https://spec.canvasprotocol.org/v0.4/extensions/ports-node.json`
45 |
46 | It provides the familiar concept of _ports_ to a node. A port is a point, which allows geometrically controlling where, e.g., arrows are attached to a shape.
47 |
48 | Any node can act as a port. The 'container' node uses the _Ports Extension_ to define which nodes it uses as ports.
49 | The _Ports Extension_ has the following properties:
50 |
51 | | Property | JSON Type | OCIF Type | Required | Contents | Default |
52 | | -------- | --------- | ---------------- | ------------ | ----------------------------- | ------- |
53 | | `ports` | `array` | [ID](spec.md#id) | **required** | IDs of nodes acting as ports. | |
54 |
55 | Each node SHOULD appear only in **one** ports array.
56 | A port cannot be used by multiple nodes as a port.
57 |
58 | **Example** \
59 | A node (n1) with two ports (p1, p2).
60 | Note that p1 and p2 are normal nodes.
61 | It's node n1 which uses p1 and p2 as its ports.
62 | An arrow can now start at node p1 (which is a port of n1) and end at node n2 (which is not a port in this example).
63 |
64 | ```json
65 | {
66 | "nodes": [
67 | {
68 | "id": "n1",
69 | "position": [100, 100],
70 | "size": [100, 100],
71 | "data": [
72 | {
73 | "type": "@ocif/node/ports",
74 | "ports": ["p1", "p2"]
75 | }
76 | ]
77 | },
78 | {
79 | "id": "p1",
80 | "position": [200, 200]
81 | },
82 | {
83 | "id": "p2",
84 | "position": [100, 200]
85 | },
86 | {
87 | "id": "n2",
88 | "position": [800, 800]
89 | }
90 | ]
91 | }
92 | ```
93 |
94 | JSON schema: [ports-node.json](extensions/ports-node.json)
95 |
96 | ## Relative Node
97 |
98 | - Name: `@ocif/node/relative`
99 | - URI: `https://spec.canvasprotocol.org/v0.4/extensions/relative-node.json`
100 |
101 | Relative constraints are used to define, e.g., the relative positioning of nodes.
102 |
103 | The node on which this extension is placed is the _target node_.
104 | It defines a _source_ node, which is used as a reference for the relative positioning.
105 |
106 | | Property | JSON Type | OCIF Type | Required | Contents | Default |
107 | | ---------- | --------- | ---------------------- | ------------ | -------------------------------- | ------- |
108 | | `source` | `string` | [ID](spec.md#id) | **required** | ID of the source node. | |
109 | | `position` | `array` | number[] | optional | Relative position of the target. | `[0,0]` |
110 | | `rotation` | `number` | [Angle](spec.md#angle) | optional | Relative angle of the target. | `0` |
111 |
112 | - **source**: The ID of the source node, which is used as a reference for the relative positioning.
113 | - **position**: The relative position of the target node to the source node.
114 | The numbers given in the array are vector-added to the position of the source node.
115 | - **rotation**: The relative rotation of the target node to the source node. The rotation is added to the rotation of the source node.
116 |
117 | JSON schema: [relative-node.json](extensions/relative-node.json)
118 |
119 | # Relation Extensions
120 |
121 | ## Hyperedge Relation
122 |
123 | - Name: `@ocif/rel/hyperedge`
124 | - URI: `https://spec.canvasprotocol.org/v0.4/extensions/hyperedge-rel.json`
125 |
126 | A hyperedge is a relation, which connects any number of nodes.
127 | Hyperedges can also be used to model simple bi-edges.
128 |
129 | Conceptually, a hyper-edge is an entity, which has a number of _endpoints_.
130 | For each endpoint, we allow defining the directionality of the connection.
131 | The endpoints are explicitly defined as an ordered list, i.e., endpoints can be addressed by their position in the list.
132 | Such a model allows representing all kinds of hyper-edges, even rather obscure one.
133 |
134 | A hyper-edge in OCIF has the following properties:
135 |
136 | | Property | JSON Type | OCIF Type | Required | Contents | Default |
137 | | ----------- | --------- | --------- | ------------ | ------------------------------------ | ------: |
138 | | `endpoints` | `array` | Endpoint | **required** | List of endpoints of the hyper-edge. | |
139 | | `weight` | `number` | | optional | Weight of the edge | `1.0` |
140 | | `rel` | `string` | | optional | Represented relation type | |
141 |
142 | - **endpoints**: See below.
143 | - **weight**: A floating-point number, which can be used to model the strength of the connection, as a whole. More general than endpoint-specific weights, and often sufficient.
144 |
147 | - **rel**: See [Edge Relation](spec.md#edge-relation)
148 |
149 | **Endpoint** \
150 | Each endpoint is an object with the following properties:
151 |
152 | | Property | JSON Type | OCIF Type | Required | Contents | Default |
153 | | ----------- | --------- | ---------------- | ------------ | ---------------------------------------- | ------- |
154 | | `id` | `string` | [ID](spec.md#id) | **required** | ID of attached entity (node or relation) | |
155 | | `direction` | `string` | Direction | optional | Direction of the connection. | `undir` |
156 | | `weight` | `number` | | optional | Weight of the edge | `1.0` |
157 |
158 | - **id**: states which node (or relation) is attached to the edge
159 | - **direction**: See below
160 | - **weight**: A floating-point number, which can be used to model the strength of the connection, for this endpoint.
161 |
164 |
165 | **Direction** \
166 | An enum with three values:
167 |
168 | - `in` (edge is going **into** the hyper-edge),
169 | - `out` (edge is going **out** from thy hyper-edge),
170 | - `undir` (edge is attached **undirected**). This is the default.
171 |
172 | This allows representing cases such as:
173 |
174 | - An edge with only one endpoint
175 | - An edge with no endpoints
176 | - An edge with only incoming or only outgoing endpoints.
177 |
178 | **Example** \
179 | An hyperedge relation connecting two nodes as input (n1,n2) with one node as output (n3).
180 |
181 | ```json
182 | {
183 | "type": "@ocif/rel/hyperedge",
184 | "endpoints": [
185 | { "id": "n1", "direction": "in" },
186 | { "id": "n2", "direction": "in" },
187 | { "id": "n3", "direction": "out" }
188 | ]
189 | }
190 | ```
191 |
192 | JSON schema: [hyperedge-rel.json](extensions/hyperedge-rel.json)
193 |
194 | ## Parent-Child Relation
195 |
196 | - Name: `@ocif/rel/parent-child`
197 | - URI: `https://spec.canvasprotocol.org/v0.4/extensions/parent-child-rel.json`
198 |
199 | A parent-child relation models a hierarchical relationship between nodes.
200 | It can be used to model inheritance, containment, or other hierarchical relationships.
201 |
202 | | Property | JSON Type | OCIF Type | Required | Contents | Default |
203 | | --------- | --------- | ---------------- | ------------ | ---------------------- | :------ |
204 | | `parent` | `string` | [ID](spec.md#id) | **required** | ID of the parent node. | |
205 | | `child` | `string` | [ID](spec.md#id) | **required** | ID of the child node. | |
206 | | `inherit` | `boolean` | | optional | Inherit properties. | `false` |
207 |
208 | - **parent**: The ID of the parent node. There SHOULD be only one parent per child.
209 |
210 | - **child**: The ID of the child node. A parent can have multiple children (expressed my multiple parent-child relations).
211 |
212 | - **inherit**: A boolean flag indicating if the child should inherit properties from the parent. Default is `false`.
213 | - The Exact semantics of inheritance are defined by the application.
214 | - In general, when looking for JSON properties of a node and finding them undefined, an app should look for the same value in the parent node.
215 | The chain of parents should be followed until a root is reached or a cycle is detected.
216 |
217 | Semantics:
218 |
219 | - If a parent is deleted, all children, which inherit from the parent, SHOULD also be deleted.
220 |
221 | JSON schema: [parent-child-rel.json](extensions/parent-child-rel.json)
222 |
223 | # Changes
224 |
225 | - Simplified language from "Ports Extension" to "Ports Node", from "Relative Constraints Extension" to "Relative Node."
226 | - Moved "freehand-node" to issues https://github.com/ocwg/spec/issues/14
227 | - 2025-01-21: Initial version of the document.
228 |
--------------------------------------------------------------------------------
/spec/v0.3/extensions.md:
--------------------------------------------------------------------------------
1 | # OCIF Extensions
2 |
3 | **OCWG Working Draft, 21 January 2025**
4 |
5 | Copyright © 2025 the Contributors to the Open Canvas Working Group (OCWG).
6 |
7 | Versioning Policy: Currently, all extensions in this document are at version **0.3**.
8 |
9 | ## Abstract
10 |
11 | This document describes extensions to the Open Canvas Interchange Format (OCIF) that are not part of the core specification. Extensions are optional and may be used to add additional functionality to OCIF documents.
12 | See the [OCIF Core Specification](./spec) for the core features of OCIF.
13 |
14 | ## Status of this Document
15 |
16 | This document is an editor's draft and has no official standing. It is a work in progress and may be updated, replaced, or obsoleted by other documents at any time.
17 |
18 | **Legal**:
19 | Open Canvas Interchange Format (OCIF) v0.2 © 2024 by Open Canvas Working Group is licensed under CC BY-SA 4.0. To view a copy of this licence, visit https://creativecommons.org/licenses/by-sa/4.0/
20 |
21 | ### Table of Contents
22 |
23 |
24 |
25 | - [OCIF Extensions](#ocif-extensions)
26 | - [Abstract](#abstract)
27 | - [Status of this Document](#status-of-this-document)
28 | - [Table of Contents](#table-of-contents)
29 | - [Node Extensions](#node-extensions)
30 | - [Ports Node](#ports-node)
31 | - [Relative Node](#relative-node)
32 | - [Relation Extensions](#relation-extensions)
33 | - [Hyperedge Relation](#hyperedge-relation)
34 | - [Parent-Child Relation](#parent-child-relation)
35 | - [Changes](#changes)
36 |
37 |
38 | # Node Extensions
39 |
40 | These are [extension](spec.md#extensions) that can be added to nodes in an OCIF document.
41 | To be placed inside the `data` `array`.
42 |
43 | ## Ports Node
44 |
45 | - Name: `@ocwg/node/ports`
46 | - URI: `https://spec.canvasprotocol.org/v0.3/extensions/ports-node.json`
47 |
48 | It provides the familiar concept of _ports_ to a node. A port is a point, which allows geometrically controlling where, e.g., arrows are attached to a shape.
49 |
50 | Any node can act as a port. The 'container' node uses the _Ports Extension_ to define which nodes it uses as ports.
51 | The _Ports Extension_ has the following properties:
52 |
53 | | Property | JSON Type | OCIF Type | Required | Contents | Default |
54 | | -------- | --------- | ---------------- | ------------ | ----------------------------- | ------- |
55 | | `ports` | `array` | [ID](spec.md#id) | **required** | IDs of nodes acting as ports. | |
56 |
57 | Each node SHOULD appear only in **one** ports array.
58 | A port cannot be used by multiple nodes as a port.
59 |
60 | **Example** \
61 | A node (n1) with two ports (p1, p2).
62 | Note that p1 and p2 are normal nodes.
63 | It's node n1 which uses p1 and p2 as its ports.
64 | An arrow can now start at node p1 (which is a port of n1) and end at node n2 (which is not a port in this example).
65 |
66 | ```json
67 | {
68 | "nodes": [
69 | {
70 | "id": "n1",
71 | "position": [100, 100],
72 | "size": [100, 100],
73 | "data": [
74 | {
75 | "type": "@ocwg/node/ports",
76 | "ports": ["p1", "p2"]
77 | }
78 | ]
79 | },
80 | {
81 | "id": "p1",
82 | "position": [200, 200]
83 | },
84 | {
85 | "id": "p2",
86 | "position": [100, 200]
87 | },
88 | {
89 | "id": "n2",
90 | "position": [800, 800]
91 | }
92 | ]
93 | }
94 | ```
95 |
96 | JSON schema: [ports-node.json](extensions/ports-node.json)
97 |
98 | ## Relative Node
99 |
100 | - Name: `@ocwg/node/relative`
101 | - URI: `https://spec.canvasprotocol.org/v0.3/extensions/relative-node.json`
102 |
103 | Relative constraints are used to define, e.g., the relative positioning of nodes.
104 |
105 | The node on which this extension is placed is the _target node_.
106 | It defines a _source_ node, which is used as a reference for the relative positioning.
107 |
108 | | Property | JSON Type | OCIF Type | Required | Contents | Default |
109 | | ---------- | --------- | ---------------------- | ------------ | -------------------------------- | ------- |
110 | | `source` | `string` | [ID](spec.md#id) | **required** | ID of the source node. | |
111 | | `position` | `array` | number[] | optional | Relative position of the target. | `[0,0]` |
112 | | `rotation` | `number` | [Angle](spec.md#angle) | optional | Relative angle of the target. | `0` |
113 |
114 | - **source**: The ID of the source node, which is used as a reference for the relative positioning.
115 | - **position**: The relative position of the target node to the source node.
116 | The numbers given in the array are vector-added to the position of the source node.
117 | - **rotation**: The relative rotation of the target node to the source node. The rotation is added to the rotation of the source node.
118 |
119 | JSON schema: [relative-node.json](extensions/relative-node.json)
120 |
121 | # Relation Extensions
122 |
123 | ## Hyperedge Relation
124 |
125 | - Name: `@ocwg/rel/hyperedge`
126 | - URI: `https://spec.canvasprotocol.org/v0.3/extensions/hyperedge-rel.json`
127 |
128 | A hyperedge is a relation, which connects any number of nodes.
129 | Hyperedges can also be used to model simple bi-edges.
130 |
131 | Conceptually, a hyper-edge is an entity, which has a number of _endpoints_.
132 | For each endpoint, we allow defining the directionality of the connection.
133 | The endpoints are explicitly defined as an ordered list, i.e., endpoints can be addressed by their position in the list.
134 | Such a model allows representing all kinds of hyper-edges, even rather obscure one.
135 |
136 | A hyper-edge in OCIF has the following properties:
137 |
138 | | Property | JSON Type | OCIF Type | Required | Contents | Default |
139 | | ----------- | --------- | --------- | ------------ | ------------------------------------ | ------: |
140 | | `endpoints` | `array` | Endpoint | **required** | List of endpoints of the hyper-edge. | |
141 | | `weight` | `number` | | optional | Weight of the edge | `1.0` |
142 | | `rel` | `string` | | optional | Represented relation type | |
143 |
144 | - **endpoints**: See below.
145 | - **weight**: A floating-point number, which can be used to model the strength of the connection, as a whole. More general than endpoint-specific weights, and often sufficient.
146 |
149 | - **rel**: See [Edge Relation](spec.md#edge-relation)
150 |
151 | **Endpoint** \
152 | Each endpoint is an object with the following properties:
153 |
154 | | Property | JSON Type | OCIF Type | Required | Contents | Default |
155 | | ----------- | --------- | ---------------- | ------------ | ---------------------------------------- | ------- |
156 | | `id` | `string` | [ID](spec.md#id) | **required** | ID of attached entity (node or relation) | |
157 | | `direction` | `string` | Direction | optional | Direction of the connection. | `undir` |
158 | | `weight` | `number` | | optional | Weight of the edge | `1.0` |
159 |
160 | - **id**: states which node (or relation) is attached to the edge
161 | - **direction**: See below
162 | - **weight**: A floating-point number, which can be used to model the strength of the connection, for this endpoint.
163 |
166 |
167 | **Direction** \
168 | An enum with three values:
169 |
170 | - `in` (edge is going **into** the hyper-edge),
171 | - `out` (edge is going **out** from thy hyper-edge),
172 | - `undir` (edge is attached **undirected**). This is the default.
173 |
174 | This allows representing cases such as:
175 |
176 | - An edge with only one endpoint
177 | - An edge with no endpoints
178 | - An edge with only incoming or only outgoing endpoints.
179 |
180 | **Example** \
181 | An hyperedge relation connecting two nodes as input (n1,n2) with one node as output (n3).
182 |
183 | ```json
184 | {
185 | "type": "@ocwg/rel/hyperedge",
186 | "endpoints": [
187 | { "id": "n1", "direction": "in" },
188 | { "id": "n2", "direction": "in" },
189 | { "id": "n3", "direction": "out" }
190 | ]
191 | }
192 | ```
193 |
194 | JSON schema: [hyperedge-rel.json](extensions/hyperedge-rel.json)
195 |
196 | ## Parent-Child Relation
197 |
198 | - Name: `@ocwg/rel/parent-child`
199 | - URI: `https://spec.canvasprotocol.org/v0.3/extensions/parent-child-rel.json`
200 |
201 | A parent-child relation models a hierarchical relationship between nodes.
202 | It can be used to model inheritance, containment, or other hierarchical relationships.
203 |
204 | | Property | JSON Type | OCIF Type | Required | Contents | Default |
205 | | --------- | --------- | ---------------- | ------------ | ---------------------- | :------ |
206 | | `parent` | `string` | [ID](spec.md#id) | **required** | ID of the parent node. | |
207 | | `child` | `string` | [ID](spec.md#id) | **required** | ID of the child node. | |
208 | | `inherit` | `boolean` | | optional | Inherit properties. | `false` |
209 |
210 | - **parent**: The ID of the parent node. There SHOULD be only one parent per child.
211 |
212 | - **child**: The ID of the child node. A parent can have multiple children (expressed my multiple parent-child relations).
213 |
214 | - **inherit**: A boolean flag indicating if the child should inherit properties from the parent. Default is `false`.
215 | - The Exact semantics of inheritance are defined by the application.
216 | - In general, when looking for JSON properties of a node and finding them undefined, an app should look for the same value in the parent node.
217 | The chain of parents should be followed until a root is reached or a cycle is detected.
218 |
219 | Semantics:
220 |
221 | - If a parent is deleted, all children, which inherit from the parent, SHOULD also be deleted.
222 |
223 | JSON schema: [parent-child-rel.json](extensions/parent-child-rel.json)
224 |
225 | # Changes
226 |
227 | - Simplified language from "Ports Extension" to "Ports Node", from "Relative Constraints Extension" to "Relative Node."
228 | - Moved "freehand-node" to issues https://github.com/ocwg/spec/issues/14
229 | - 2025-01-21: Initial version of the document.
230 |
--------------------------------------------------------------------------------
/spec/v0.1/spec.md:
--------------------------------------------------------------------------------
1 | # Open Canvas Initial Draft Spec
2 |
3 | > **This DEPRECATED version:** \
4 | > https://spec.canvasprotocol.org/v0.1 \
5 | > **Latest version:** \
6 | > https://spec.canvasprotocol.org/v0.2 \
7 | > See [spec 0.2](spec-0.2.md) in this repo.
8 |
9 | ⚒️ Work on the initial spec is underway in the [Initial Draft Spec pull request](https://github.com/ocwg/spec/pull/1).
10 |
11 | 📚 Read more at [canvasprotocol.org](https://canvasprotocol.org).
12 |
13 | The purpose of this doc is to provide a plain-language description of the specification and walk through key concepts of the Open Canvas format.
14 |
15 | ## Motivation and Design Goals
16 |
17 | The goal of the Open Canvas format is to enable interoperability between different [infinite canvas tools](https://infinitecanvas.tools) by being a simple, compact "transmission" format.
18 |
19 | Open Canvas has been designed to meet the following goals:
20 |
21 | - **Balance between visual and conceptual canvases.** Nodes provide the core elements for the canvas. Relations provide rich support for creating conceptual relationships between nodes.
22 | - **Extensibility.** Open Canvas is fully extensibile using custom-defined schemas.
23 | - **Runtime-independence.** Enable different canvases to work together without canvases needing to implement or even understand all aspects of the spec.
24 |
25 | Out of scope:
26 |
27 | - **Backwards compatibility with Obsidian's JSON Canvas.** JSON Canvas isn't designed to support the full gamut of functionality supported by Open Canvas.
28 |
29 | ## Structure: Nodes, Relations, Resources, Schemas
30 |
31 | Conceptually, an infinite canvas can be thought of as `nodes` and `relations`. `nodes` represent all of the visual information on the canvas, while `relations` represent the invisible conceptual relationships between nodes.
32 |
33 | In order to enable asset re-use, Open Canvas distinguishes between a `node` with a location on the canvas and the `resource` that the `node` displays.
34 |
35 | The structure of an Open Canvas JSON consists of `nodes`, `relations`, and `resources`, with a version number that defines the shape of the top-level structure and the shapes of the base classes. The following is a valid, though empty Open Canvas file:
36 |
37 | ```json
38 | {
39 | "schema_version": "1.0", // this could be implied
40 | "nodes": [],
41 | "relations": [],
42 | "resources": [],
43 | "schemas": []
44 | }
45 | ```
46 |
47 | ### Nodes
48 |
49 | All visible items on the canvas are referred to as Nodes.
50 |
51 | **Node Base Class**
52 |
53 | All Nodes must implement the following properties:
54 |
55 | ```ts
56 | type Node = {
57 | id: string;
58 | position: [number, number] | [number, number, number]; // x,y or x,y,z
59 | size?: [number, number] | [number, number, number]; // w,h or w,h,d
60 | rotation?: number; // +/- 360 degrees; if not included, defaults to 0
61 | scale?: number; // defaults to 0
62 | resource?: string; // id of a resource
63 | properties?: [
64 | {
65 | schema: string; // name of a schema listed in schemas
66 | schema_version: string;
67 | [key: string]: any;
68 | }
69 | ];
70 | fallback?: string;
71 | };
72 | ```
73 |
74 | ### Relations
75 |
76 | Relations are used to indicate relationships between Nodes on the canvas. Relations are generally not visible, but rather conceptual. If a relation should be visualized, it should have a corresponding Node.
77 |
78 | **Relation Base class**
79 |
80 | ```ts
81 | type Relation = {
82 | id: string;
83 | name: string;
84 | properties?: [
85 | {
86 | schema: string;
87 | schema_version: string;
88 | [key: string]: any;
89 | }
90 | ];
91 | };
92 | ```
93 |
94 | ### Resources
95 |
96 | Resources are the hypermedia assets that Nodes display. They are stored separately from Nodes to allow for asset reuse and efficiency.
97 |
98 | **Resoruce Base class**
99 |
100 | ```ts
101 | type Resource = {
102 | id: string;
103 | uri: string;
104 | mimeType?: string;
105 | };
106 | ```
107 |
108 | - [ ] What happens if `id`s collide? Should we just use array indexes instead?
109 |
110 | The `uri` for a resource can either be:
111 |
112 | - embedded in the file itself
113 | - a local file relative to the open canvas file
114 | - a remote URL
115 |
116 | **Embedded in the file**
117 |
118 | ```json
119 | {
120 | "resources": {
121 | [
122 | "id": "a-rectangle",
123 | "uri": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAfSURBVHgBpcixEQAACAIxdP8FfxrtOTpSZoCTWQVlPm0NA5S6X7P2AAAAAElFTkSuQmCC", // 5x5 white png
124 | "mimeType": "image/png"
125 | ]
126 | }
127 | }
128 | ```
129 |
130 | - [ ] is `mimeType` duplicative?
131 | - [ ] what if `mimeType` and the mimeType specified in `data:` differ?
132 |
133 | **Local file relative to the Open Canvas file**
134 |
135 | ```json
136 | {
137 | "resources": {
138 | [
139 | "id": "a-rectangle",
140 | "uri": "./a-rectangle.png",
141 | "mimeType": "image/png"
142 | ]
143 | }
144 | }
145 | ```
146 |
147 | **A remote URL**
148 |
149 | ```json
150 | {
151 | "resources": {
152 | [
153 | "id": "a-rectangle",
154 | "uri": "https://somesite.com/a-rectangle.png",
155 | "mimeType": "image/png"
156 | ]
157 | }
158 | }
159 | ```
160 |
161 | - [ ] what if `mimeType` and the mimeType specified in the http response differ?
162 |
163 | ## Extensibility
164 |
165 | One major challenge of an interchange format for infinite canvases is that no two canvases are exactly alike. There are canvases built for informal whiteboarding, formal diagramming, quick visual sketches, node-and-wire programming, and many, many other use cases. Each of these canvases have radically different feature sets. How can we enable these canvases to work together?
166 |
167 | The core of the Open Canvas specification is extensibility and fallbacks. In the case of one canvas supporting a feature that another canvas doesn't support, we need to enable three things:
168 |
169 | 1. Canvases need to be able to read and write to an open canvas file without implementing all of the features of all other canvases and also extend the format with features unique to their canvas without breaking other canvases or changing the main specification.
170 | 2. Canvases that lack a feature need to preserve the data for features that they don't implement.
171 | 3. Canvases need to be able to optionally display reasonable fallbacks for features that they don't implement.
172 |
173 | Put simply: how do we design the specification to be extensible by individual canvas tools and robust against unknown features?
174 |
175 | ### Schemas
176 |
177 | Schemas are used to define the structure of Nodes and Relations. They provide extensibility by supporting different properties for Nodes and Relations.
178 |
179 | Schemas are included inline in the file. In the future, there may be a way to externalize schemas to a remote schema registry.
180 |
181 | Schemas have a version number to support schema evolution.
182 |
183 | ### Extensibility through preserving the property bag
184 |
185 | In order to support interchange beteween canvases when features don't overlap, canvases need to preserve nodes and relations that it doesn't support.
186 |
187 | In practice, that looks something like this:
188 |
189 | 1. Canvas A supporting Feature X creates a canvas with a Feature X node in it and exports it as an Open Canvas file.
190 | 2. Canvas B, which does not support Feature X, opens the Open Canvas file, and some edits are made to the canvas.
191 | 3. Canvas B exports the canvas to an Open Canvas file. The nodes for Feature X should still be in the Open Canvas file, unchanged.
192 |
193 | NOTE: This is perhaps the most onerous part of supporting Open Canvas; implementing canvases have to store data that they don't need or recognize.
194 |
195 | ### Extensibility through fallbacks
196 |
197 | The base node type supports an optional `fallback` property as a string. If a canvas wishes to visualize a node that it doesn't implement, the canvas can render a fallback element on the canvas at the element's x,y position using the string.
198 |
199 | ### Extensibility through schema extension
200 |
201 | Schemas can extend one another.
202 |
203 | When extending, properties are simply merged to establish the full schema. This allows extension with meaningful fallbacks: if an implementer doesn't support the extended schema, but does support the base schema, they can render the node using a simpler representation.
204 |
205 | - [ ] "merging properties" is probably a bad idea - let the implementer choose which property to use rather than using this inheritance-like behavior
206 |
207 | Schemas are defined in JSONSchema format.
208 |
209 | ```json
210 | "schemas": [
211 | "@ocwg/text": { /* JSONSchema format */ }
212 | ]
213 | ```
214 |
215 | For example, a `@tldraw/arrow` schema could extend a `@ocwg/arrow` schema, adding properties that are only applicable to TLDraw arrow.
216 |
217 | - [ ] Flesh out this example
218 |
219 | ## Core Node Schemas
220 |
221 | These are the node schemas included in the core `@ocwg` namespace.
222 |
223 | ### Text
224 |
225 | Text Nodes are used to display text on the canvas.
226 |
227 | ### Arrows
228 |
229 | ### Examples
230 |
231 | Here are a bunch of example Nodes:
232 |
233 | ```json
234 | {
235 | "nodes": [
236 | {
237 | "id": "someShapeID11321",
238 | "position": [10, 10],
239 | "properties": [{
240 | "schema": "@ocwg/text",
241 | "schema_version": "1.0",
242 | "text": "Hello, world!",
243 | "color": "#000000"
244 | }]
245 | },
246 | {
247 | "id": "2435234634t324t3245",
248 | "position": [10, 10],
249 | "size": [200, 150],
250 | "properties": [{
251 | "schema": "@ocwg/text",
252 | "schema_version": "1.0",
253 | "mime_type": "text/json",
254 | "uri": "data://9823fy9283hf2i3yf082ohu3fo2hufi2uh4fk234f8youhi",
255 | }]
256 | },
257 | {
258 | "id": "oajefioajef83r8y3ehf",
259 | "position": [10, 10],
260 | "size": [200, 150],
261 | "rotation": 90, /* +/-360 degrees */
262 | "properties": [{
263 | "schema": "@ocwg/rectangle",
264 | "schema_version": "1.0",
265 | "text": "Hello, world!",
266 | "stroke_color": "red",
267 | "stroke_weight": ,
268 | "fill_color": ,
269 | }]
270 | },
271 | {
272 | "id": "someShapeID124135",
273 | "position": [10, 10],
274 | "size": [200, 150],
275 | "properties": [{
276 | "schema": "@stately/typed-rectangle",
277 | "schema_version": "3.1",
278 | "type": "rectangle",
279 | }]
280 | },
281 | {
282 | "id": "someShapeID535",
283 | "position": [10, 10],
284 | "properties": [{
285 | "schema": "@tldraw/arrow",
286 | "schema_version": "1.0",
287 | "start": {
288 | "position": [1, 1],
289 | "terminal": "",
290 | "connected_to": "someShapeID124135",
291 | },
292 | "end": {
293 | "position": [10, 10],
294 | "terminal": "",
295 | "connected_to": "someShapeID124135",
296 | },
297 | "bend": 200,
298 | }]
299 | }
300 | ]
301 | }
302 | ```
303 |
304 | ## Core Relation Schemas
305 |
306 | These are the relation schemas included in the core `@ocwg` namespace.
307 |
308 | ### Sets
309 |
310 | Sets can be used to model groups, frames, and many other things. Their basic representation is a collection of Nodes or Relations.
311 |
312 | Here's an example:
313 |
314 | ```json
315 | {
316 | "id": "setRelationID1234",
317 | "name": "group a",
318 | "properties": [
319 | {
320 | "schema": "@ocwg/set",
321 | "schema_version": "1.0",
322 | "members": ["someID", "someOtherID"]
323 | }
324 | ]
325 | }
326 | ```
327 |
328 | - `members` can be Nodes or Relations.
329 |
330 | ### Edges
331 |
332 | Edge Relations are used to connect two Nodes or Relations together. Edge Relations are not the same as Nodes that are Arrows. Relations may not be visible on the canvas.
333 |
334 | Here's an example:
335 |
336 | ```json
337 | {
338 | "id": "edgeRelationID1234",
339 | "name": "a named edge",
340 | "properties": [
341 | {
342 | "schema": "@ocwg/edge",
343 | "schema_version": "1.0",
344 | "from": "someNodeID124135",
345 | "to": "someNodeID11321"
346 | }
347 | ]
348 | }
349 | ```
350 |
351 | - `from` and `to` can be Nodes or Relations.
352 |
353 | ### Hyper-edges
354 |
355 | Hyper-edges represent many-to-many relationships.
356 |
357 | ```json
358 | {
359 | "id": "hyperEdgeRelation1234",
360 | "name": "a named hyperedge",
361 | "properties": [
362 | {
363 | "schema": "@ocwg/hyperedge",
364 | "schema_version": "1.0",
365 | "from": ["someShapeID124135", "someShapeID11321"],
366 | "to": ["someShapeID124135", "someShapeID11321"]
367 | }
368 | ]
369 | }
370 | ```
371 |
372 | - The `from[]` and `to[]` arrays can contain Nodes or Relations.
373 |
--------------------------------------------------------------------------------
/spec/v0.5/extensions.md:
--------------------------------------------------------------------------------
1 | # OCIF Extensions
2 |
3 | Copyright © 2025 the Contributors to the Open Canvas Working Group (OCWG).
4 |
5 | Versioning Policy: Currently, all extensions in this document are at version **0.5**.
6 |
7 | ## Abstract
8 |
9 | This document describes extensions to the Open Canvas Interchange Format (OCIF) that are not part of the core specification. Extensions are optional and may be used to add additional functionality to OCIF documents.
10 | See the [OCIF Core Specification](./spec) for the core features of OCIF.
11 |
12 | ## Status of this Document
13 |
14 | This document is an editor's draft and has no official standing. It is a work in progress and may be updated, replaced, or obsoleted by other documents at any time.
15 |
16 | **Legal**:
17 | Open Canvas Interchange Format (OCIF) v0.5 © 2025 by Open Canvas Working Group is licensed under CC BY-SA 4.0. To view a copy of this licence, visit https://creativecommons.org/licenses/by-sa/4.0/
18 |
19 | ### Table of Contents
20 |
21 |
22 | * [OCIF Extensions](#ocif-extensions)
23 | * [Abstract](#abstract)
24 | * [Status of this Document](#status-of-this-document)
25 | * [Table of Contents](#table-of-contents)
26 | * [Node Extensions](#node-extensions)
27 | * [Ports Node](#ports-node)
28 | * [Node Transforms](#node-transforms)
29 | * [Anchored Node](#anchored-node)
30 | * [Text Style Node](#text-style-node)
31 | * [Relation Extensions](#relation-extensions)
32 | * [Hyperedge Relation](#hyperedge-relation)
33 | * [Parent-Child Relation](#parent-child-relation)
34 | * [Changes](#changes)
35 | * [From v0.4 to v0.5](#from-v04-to-v05)
36 | * [Up to v0.4](#up-to-v04)
37 |
38 |
39 | # Node Extensions
40 |
41 | These are [extensions](spec.md#extensions) that can be added to nodes in an OCIF document.
42 | To be placed inside the `data` `array`.
43 |
44 | ## Ports Node
45 |
46 | - Name: `@ocif/node/ports`
47 | - URI: `https://spec.canvasprotocol.org/v0.5/extensions/ports-node.json`
48 |
49 | It provides the familiar concept of _ports_ to a node. A port is a point that allows geometrically controlling where, e.g., arrows are attached to a shape.
50 |
51 | Any node can act as a port. The 'container' node uses the _Ports Extension_ to define which nodes it uses as ports.
52 | The _Ports Extension_ has the following properties:
53 |
54 | | Property | JSON Type | OCIF Type | Required | Contents | Default |
55 | |----------|-----------|------------------|--------------|-------------------------------|---------|
56 | | `ports` | `array` | [ID](spec.md#id) | **required** | IDs of nodes acting as ports. | |
57 |
58 | Each node SHOULD appear only in **one** ports array.
59 | A port cannot be used by multiple nodes as a port.
60 |
61 | **Example** \
62 | A node (n1) with two ports (p1, p2).
63 | Note that _p1_ and _p2_ are normal nodes.
64 | It is the node _n1_ which uses _p1_ and _p2_ as its ports.
65 | An arrow can now start at node p1 (which is a port of n1) and end at node n2 (which is not a port in this example).
66 |
67 | ```json
68 | {
69 | "nodes": [
70 | {
71 | "id": "n1",
72 | "position": [100, 100],
73 | "size": [100, 100],
74 | "data": [
75 | {
76 | "type": "@ocif/node/ports",
77 | "ports": ["p1", "p2"]
78 | }
79 | ]
80 | },
81 | {
82 | "id": "p1",
83 | "position": [200, 200]
84 | },
85 | {
86 | "id": "p2",
87 | "position": [100, 200]
88 | },
89 | {
90 | "id": "n2",
91 | "position": [800, 800]
92 | }
93 | ]
94 | }
95 | ```
96 |
97 | JSON schema: [ports-node.json](extensions/ports-node.json)
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 | ## Node Transforms
106 |
107 | - Name: `@ocif/node/transforms`
108 | - URI: `https://spec.canvasprotocol.org/v0.5/extensions/transforms-node.json`
109 |
110 | The node transform extension allows customizing the local coordinate system of a node relative to the parent coordinate system.
111 | This is a concept commonly found in game engines and infinitely zoomable canvases.
112 |
113 | The default parent coordinate system is the global, canvas-wide coordinate system.
114 | If the [parent-child-relation](#parent-child-relation) is used, the defined parent node MUST be used as the one,
115 | from which the reference coordinate system is used.
116 |
117 | The transforms affect the local coordinate system, which is used to display resources (see [spec](spec.md#size-and-resource)) and child nodes. The child nodes have global coordinates, and the node transform extension can provide the "recipe" how to calculate the global positions of a node when, e.g., the parent has been moved, rotated, or scaled.
118 |
119 | Transforms are chainable. For example, a node A may transform its coordinate system relative to the canvas. Node B may transform relative to the coordinate system of its parent A. Then node C transforms again relative to its parent B. The resulting scale, rotation, and offset computation requires computing first A, then B, then C.
120 |
121 | | Property | JSON Type | OCIF Type | Required | Contents | Default |
122 | |----------------|------------------------------------|-----------|--------------|---------------------|-----------|
123 | | `scale` | `number`, `number[2]`, `number[3]` | Vector | **optional** | Scale factor | `1` |
124 | | `rotation` | `number` | Angle | **optional** | Rotation in degrees | `0` |
125 | | `rotationAxis` | `number[3]` | | **optional** | Rotation axis | `[0,0,1]` |
126 | | `offset` | `number`, `number[2]`, `number[3]` | Vector | **optional** | Offset | `0` |
127 |
128 | - **scale**: A number-vector (floating-point) to override (set) the automatic scale factor of the node. This defines the scale of the local coordinate system. A larger scale SHOULD also affect font sizes. The scale factors are multiplied component-wise to the parent coordinate system.
129 |
130 | - NOTE: The scale factors cannot be computed from global positions alone.
131 | Scale factors provide additional state which influences interaction behaviour, e.g., an item drag-dropped into an item with a scale factor of less than 1 causes the item to shrink, when released.
132 |
133 | - **rotation**: A number-vector (floating-point) to override (set) the rotation of the node.
134 | - This (relative, local) rotation is added to the rotation of the parent.
135 | - A single number around the axis defined in `rotationAxis`, in degrees in counter-clockwise direction.
136 |
137 | - **rotationAxis**: The default is `(0,0,1)`. This is the [axis-angle notation](https://en.wikipedia.org/wiki/Axis%E2%80%93angle_representation). The default is to use the z-axis, which results in 'normal' 2D rotation in the x-y-plane.
138 |
139 | - **offset**: A number-vector (floating-point). This vector is added to the parent position to result in the childs position. This is the 'recipe' how the global child positions have been computed.
140 |
141 | - Semantics: When the parent is moved in an app, the app SHOULD update the children's position accordingly. The offset remains unchanged, unless the child itself is moved. In that case, the offset and the position of the child should be adapted.
142 |
143 | **Practical Advice**\
144 | On import, the global positions can be used as-is.
145 | For text rendering, the scale factors SHOULD be taken into account.
146 | For interactive apps, the transforms allow to adapt on parent changes.
147 | Furthermore, when zooming very large maps, position and size should be computed on the fly using node transforms, as global positions would become unstable due to numeric precision.
148 |
149 |
150 | **Example:** A node with a scale factor:
151 |
152 | ```json
153 | {
154 | "id": "node-with-image",
155 | "position": [100, 100],
156 | "size": [100, 200],
157 | "resource": "frog",
158 | "data": [
159 | { "type": "@ocif/node/transform",
160 | "scale": 0.5 }
161 | ]
162 | }
163 | ```
164 | JSON schema: [transform-node.json](extensions/transforms-node.json)
165 |
166 |
167 |
168 | ## Anchored Node
169 |
170 | - Name: `@ocif/node/anchored`
171 | - URI: `https://spec.canvasprotocol.org/v0.5/extensions/anchored-node.json`
172 |
173 | Relative positioning requires anchoring to a parent item.
174 | The parent position is interpreted as the root of a local coordinate system.
175 | The parent size is added to the position and yields the coordindat of the _one_.
176 | This is (1,1) in 2D and (1,1,1) in 3D.
177 | Now nodes can be positioned relative to the parent using relative positions.
178 | The coordinates in [0,1]x[0,1] (or [0,1]x[0,1]x[0,1] in 3D) cover any position within the parent item.
179 | These percentage-coordinates are now used to position the item.
180 |
181 | | Property | JSON Type | OCIF Type | Required | Contents | Default |
182 | |-----------------------|----------------------------|-----------------------|--------------|---------------------|-----------------|
183 | | `topLeftPosition` | `number[2]` or `number[3]` | Percentage Coordinate | **optional** | Top left anchor | [0,0] / [0,0,0] |
184 | | `bottomRightPosition` | `number[2]` or `number[3]` | Percentage Coordinate | **optional** | Bottom-right anchor | [1,1] / [1,1,1] |
185 | | `topLeftOffset` | `number[2]` or `number[3]` | Absolute offset | **optional** | Top left offset | [0,0] / [0,0,0] |
186 | | `bottomRightOffset` | `number[2]` or `number[3]` | Absolute offset | **optional** | Bottom-right offset | [0,0] / [0,0,0] |
187 |
188 | The offsets are interpreted in the parent's coordinate system.
189 |
190 | If only the top-left position is given, the bottom-right position defaults to [1,1] (or [1,1,1] in 3D) as specified in the default values. This means the node would be anchored at the specified top-left position, and would extend to the bottom-right of the parent.
191 |
192 | JSON schema: [anchored-node.json](extensions/anchored-node.json)
193 |
194 |
195 | ## Text Style Node
196 |
197 | - Name: `@ocif/node/textstyle`
198 | - URI: `https://spec.canvasprotocol.org/v0.5/extensions/textstyle-node.json`
199 |
200 | The text style extension allows setting common properties for rendering plain text and structured text (such as Markdown or AsciiDoc).
201 |
202 |
203 | | Property | JSON Type | OCIF Type | Required | Contents | Default |
204 | |--------------|-----------|-----------------|----------|-------------|--------------|
205 | | `fontSizePx` | `number` | | optional | Font size | `12px` |
206 | | `fontFamily` | `string` | | optional | Font family | `sans-serif` |
207 | | `color` | `string` | Color | optional | Text color | `#000000` |
208 | | `align` | `string` | enum, see below | optional | Alignment | `left` |
209 | | `bold` | `boolean` | | optional | Bold text | `false` |
210 | | `italic` | `boolean` | | optional | Italic text | `false` |
211 |
212 | * **fontSize**: The font size in `px`, as used in CSS.
213 | * **fontFamily**: The font family, as used in CSS.
214 | * **color**: The text color. See [Color](spec.md#color).
215 | * **align**: The text alignment. Possible values are `left`, `right`, `center`, `justify`.
216 | * **bold**: A boolean flag indicating if the text should be bold.
217 | * **italic**: A boolean flag indicating if the text should be italic.
218 |
219 | JSON schema: [textstyle-node.json](extensions/textstyle-node.json)
220 |
221 |
222 | # Relation Extensions
223 |
224 | ## Hyperedge Relation
225 |
226 | - Name: `@ocif/rel/hyperedge`
227 | - URI: `https://spec.canvasprotocol.org/v0.5/extensions/hyperedge-rel.json`
228 |
229 | A hyperedge is a relation that connects any number of nodes.
230 | Hyperedges can also be used to model simple bi-edges.
231 |
232 | Conceptually, a hyper-edge is an entity that has a number of _endpoints_.
233 | For each endpoint, we allow defining the directionality of the connection.
234 | The endpoints are explicitly defined as an ordered list, i.e., endpoints can be addressed by their position in the list.
235 | Such a model allows representing all kinds of hyper-edges, even rather obscure one.
236 |
237 | A hyper-edge in OCIF has the following properties:
238 |
239 | | Property | JSON Type | OCIF Type | Required | Contents | Default |
240 | |-------------|-----------|-----------|--------------|--------------------------------------|--------:|
241 | | `endpoints` | `array` | Endpoint | **required** | List of endpoints of the hyper-edge. | |
242 | | `weight` | `number` | | optional | Weight of the edge | `1.0` |
243 | | `rel` | `string` | | optional | Represented relation type | |
244 |
245 | - **endpoints**: See below.
246 | - **weight**: A floating-point number, which can be used to model the strength of the connection, as a whole. More general than endpoint-specific weights, and often sufficient.
247 |
250 | - **rel**: See [Edge Relation](spec.md#edge-relation)
251 |
252 | **Endpoint** \
253 | Each endpoint is an object with the following properties:
254 |
255 | | Property | JSON Type | OCIF Type | Required | Contents | Default |
256 | |-------------|-----------|------------------|--------------|------------------------------------------|---------|
257 | | `id` | `string` | [ID](spec.md#id) | **required** | ID of attached entity (node or relation) | |
258 | | `direction` | `string` | Direction | optional | Direction of the connection. | `undir` |
259 | | `weight` | `number` | | optional | Weight of the edge | `1.0` |
260 |
261 | - **id**: states which node (or relation) is attached to the edge
262 | - **direction**: See below
263 | - **weight**: A floating-point number, which can be used to model the strength of the connection, for this endpoint.
264 |
267 |
268 | **Direction** \
269 | An enum with three values:
270 |
271 | - `in` (edge is going **into** the hyper-edge),
272 | - `out` (edge is going **out** from thy hyper-edge),
273 | - `undir` (edge is attached **undirected**). This is the default.
274 |
275 | This allows representing cases such as:
276 |
277 | - An edge with only one endpoint
278 | - An edge with no endpoints
279 | - An edge with only incoming or only outgoing endpoints.
280 |
281 | **Example** \
282 | An hyperedge relation connecting two nodes as input (n1,n2) with one node as output (n3).
283 |
284 | ```json
285 | {
286 | "type": "@ocif/rel/hyperedge",
287 | "endpoints": [
288 | { "id": "n1", "direction": "in" },
289 | { "id": "n2", "direction": "in" },
290 | { "id": "n3", "direction": "out" }
291 | ]
292 | }
293 | ```
294 |
295 | JSON schema: [hyperedge-rel.json](extensions/hyperedge-rel.json)
296 |
297 | ## Parent-Child Relation
298 |
299 | - Name: `@ocif/rel/parent-child`
300 | - URI: `https://spec.canvasprotocol.org/v0.5/extensions/parent-child-rel.json`
301 |
302 | A parent-child relation models a strict hierarchical relationship between nodes or relations.
303 | It can be used to model inheritance, containment, or other hierarchical relationships.
304 |
305 | | Property | JSON Type | OCIF Type | Required | Contents | Default |
306 | |-----------------|-----------|------------------|--------------|-----------------------------------------|:--------|
307 | | `parent` | `string` | [ID](spec.md#id) | optional | ID of the parent. | empty |
308 | | `child` | `string` | [ID](spec.md#id) | **required** | ID of the child. | |
309 | | `inherit` | `boolean` | | optional | Inherit properties. | `false` |
310 | | `cascadeDelete` | `boolean` | | optional | Delete children when parent is deleted. | `true` |
311 |
312 | - **parent**: The ID of the parent node or relation. There MUST be only one parent per child.
313 | - If empty, the canvas itself acts as the parent node. This is relevant for [node transforms](#node-transforms).
314 |
315 | - **child**: The ID of the child node or relation. A parent can have multiple children (expressed my multiple parent-child relations).
316 |
317 | - **inherit**: A boolean flag indicating if the child should inherit properties from the parent. Default is `false`.
318 |
319 | - The Exact semantics of inheritance are defined by the application.
320 | - In general, when looking for JSON properties of a child and finding them undefined, an app should look for the same value in the parent.
321 | The chain of parents should be followed until a root is reached or a cycle is detected.
322 |
323 | - **cascadeDelete**: A boolean flag indiciating if the children should be deleted when the parent is deleted. Default it `true`.
324 |
325 | Semantics:
326 |
327 | - If a parent is deleted, all children, which inherit from the parent, SHOULD also be deleted. (see **cascadeDelete** property)
328 |
329 | JSON schema: [parent-child-rel.json](extensions/parent-child-rel.json)
330 |
331 |
332 | ## Changes
333 |
334 | ### From v0.4 to v0.5
335 | - "Relative Node" has been replaced by "Node Transforms"
336 | - "Anchored Node" has been added
337 |
338 | ### Up to v0.4
339 | - Simplified language from "Ports Extension" to "Ports Node", from "Relative Constraints Extension" to "Relative Node."
340 | - Moved "freehand-node" to issues https://github.com/ocwg/spec/issues/14
341 | - 2025-01-21: Initial version of the document.
342 |
--------------------------------------------------------------------------------