@restless-dreams/orbit 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -2
- package/dist/bin/orbit.js +2972 -213
- package/dist/bin/orbit.js.map +25 -13
- package/dist/{chunk-mtrnm0yc.js → chunk-fkdfgtx9.js} +3156 -2854
- package/dist/chunk-fkdfgtx9.js.map +75 -0
- package/dist/{chunk-7n00wx0t.js → chunk-y748hzcr.js} +2 -2
- package/dist/{chunk-0gwh723s.js → chunk-zze4tc9n.js} +2 -2
- package/dist/core/branch-colors.d.ts +57 -0
- package/dist/core/document-source.d.ts +1 -1
- package/dist/core/editing.d.ts +8 -5
- package/dist/core/engine/scene.d.ts +5 -4
- package/dist/core/engine/views.d.ts +10 -2
- package/dist/core/index.d.ts +10 -0
- package/dist/core/input-controller.d.ts +7 -0
- package/dist/core/languages.d.ts +2 -0
- package/dist/core/node-attributes.d.ts +1 -1
- package/dist/core/other-apps/freemind.d.ts +8 -0
- package/dist/core/other-apps/icons.d.ts +16 -0
- package/dist/core/other-apps/ideas.d.ts +76 -0
- package/dist/core/other-apps/import.d.ts +28 -0
- package/dist/core/other-apps/inflate.d.ts +22 -0
- package/dist/core/other-apps/markup.d.ts +51 -0
- package/dist/core/other-apps/notes.d.ts +80 -0
- package/dist/core/other-apps/opml.d.ts +26 -0
- package/dist/core/other-apps/reading.d.ts +36 -0
- package/dist/core/other-apps/xmind.d.ts +14 -0
- package/dist/core/other-apps/zip.d.ts +37 -0
- package/dist/core/print-layout.d.ts +70 -0
- package/dist/core/renderer/content-painter.d.ts +30 -0
- package/dist/core/renderer/dom.d.ts +15 -0
- package/dist/core/renderer/families.d.ts +35 -0
- package/dist/core/renderer/label-painter.d.ts +93 -0
- package/dist/core/renderer/node-painter.d.ts +113 -0
- package/dist/core/renderer/numbers.d.ts +2 -0
- package/dist/core/renderer/options.d.ts +12 -0
- package/dist/core/renderer/rings-and-links.d.ts +23 -0
- package/dist/core/renderer/scene-helpers.d.ts +33 -0
- package/dist/core/renderer/trees.d.ts +22 -0
- package/dist/core/svg-renderer.d.ts +6 -109
- package/dist/core/types.d.ts +9 -1
- package/dist/core/web-page/page.d.ts +2 -1
- package/dist/core/web-page/runtime.d.ts +1 -1
- package/dist/index.js +2935 -26
- package/dist/index.js.map +18 -6
- package/dist/marked/index.js +2 -2
- package/dist/react/index.js +2 -2
- package/dist/web-page.js +79 -79
- package/llms-full.txt +205 -5
- package/llms.txt +2 -1
- package/package.json +1 -1
- package/skills/orbit/SKILL.md +1 -1
- package/skills/orbit/references/editing.md +2 -1
- package/skills/orbit/references/format.md +28 -0
- package/skills/orbit/references/guide.md +25 -1
- package/skills/orbit/references/import-export.md +115 -0
- package/skills/orbit/references/languages.md +1 -0
- package/skills/orbit/references/recipes.md +9 -0
- package/skills/orbit/references/theming.md +15 -0
- package/dist/chunk-mtrnm0yc.js.map +0 -63
- /package/dist/{chunk-7n00wx0t.js.map → chunk-y748hzcr.js.map} +0 -0
- /package/dist/{chunk-0gwh723s.js.map → chunk-zze4tc9n.js.map} +0 -0
package/llms-full.txt
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
- Install with `npm install @restless-dreams/orbit`. The package is ESM-only. React (`@restless-dreams/orbit/react`) and marked (`@restless-dreams/orbit/marked`) are optional peers.
|
|
6
6
|
- A map fills its container: always give the container a height.
|
|
7
7
|
- Write maps in orbit-md, Orbit's markdown (every heading and list item is a node, text under it is its note, `<!-- orbit key: value -->` comments set attributes), or in YAML (`nodes:` with `text` and `children`).
|
|
8
|
-
- Check a map with `npx @restless-dreams/orbit validate --json map.md`: every problem comes with a suggested fix, and its line where it has one. `npx @restless-dreams/orbit example` prints a map with the main features, and `orbit convert --to yaml map.md` (or `--to md`) turns one flavor into the other.
|
|
8
|
+
- Check a map with `npx @restless-dreams/orbit validate --json map.md`: every problem comes with a suggested fix, and its line where it has one. `npx @restless-dreams/orbit example` prints a map with the main features, and `orbit convert --to yaml map.md` (or `--to md`) turns one flavor into the other, and `orbit convert --to md Plan.xmind --out plan.md` brings in a map from XMind, FreeMind, Freeplane or OPML.
|
|
9
9
|
- Draw one with `createOrbitMap(element, markdown, options)`, the `<orbit-map>` element, `<OrbitMindMap source={markdown} />` from `@restless-dreams/orbit/react`, or `renderToSVGString(markdown, options)` without a browser.
|
|
10
10
|
- Share a map as one web page, which opens in any browser, offline: `npx @restless-dreams/orbit export --html map.md --out map.html`.
|
|
11
11
|
- Let people build the map on the map with `editable: true` (a + in the orbit, text typed inside nodes), and save what they make with `map.toMarkdown()` on the `edit` event.
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
- [Extending](https://github.com/restless-dreams/orbit/blob/main/docs/extending.md): node types, your own actions, status words and named themes, for one map or every map
|
|
24
24
|
- [Editing](https://github.com/restless-dreams/orbit/blob/main/docs/editing.md): context menus on a long press, adding, renaming, moving and removing nodes on the map, and saving the result as orbit-md
|
|
25
25
|
- [Map tools for AI](https://github.com/restless-dreams/orbit/blob/main/docs/map-tools.md): tools for any model to read and change a map, on screen or on a server, for each provider's API and MCP
|
|
26
|
+
- [Maps from other apps](https://github.com/restless-dreams/orbit/blob/main/docs/import-export.md): bringing in OPML, FreeMind, Freeplane and XMind maps with their notes, links and pictures, what each warning means, and writing OPML
|
|
26
27
|
- [Languages](https://github.com/restless-dreams/orbit/blob/main/docs/languages.md): maps in the page's language, English and Spanish built in, adding languages and changing single words
|
|
27
28
|
- [Pictures and videos](https://github.com/restless-dreams/orbit/blob/main/docs/media.md): the media viewer, its keys, YouTube and Vimeo, sound and autoplay, and opening, playing and following media from code
|
|
28
29
|
- [Coding agents](https://github.com/restless-dreams/orbit/blob/main/docs/agents.md): the skill, validation as JSON, and messages for setup mistakes
|
|
@@ -46,7 +47,7 @@ Mind maps written in markdown and explored like a solar system.
|
|
|
46
47
|
|
|
47
48
|

|
|
48
49
|
|
|
49
|
-
**[Website](https://orbit.hyuchia.com)** · [Playground](https://orbit.hyuchia.com/playground) · [Guide](docs/guide.md) · [Map format](docs/format.md) · [Recipes](docs/recipes.md) · [Theming](docs/theming.md) · [Extending](docs/extending.md) · [Editing](docs/editing.md) · [Map tools for AI](docs/map-tools.md) · [Pictures and videos](docs/media.md) · [Languages](docs/languages.md) · [Accessibility](docs/accessibility.md) · [Coding agents](docs/agents.md) · [API reference](docs/api/README.md)
|
|
50
|
+
**[Website](https://orbit.hyuchia.com)** · [Playground](https://orbit.hyuchia.com/playground) · [Guide](docs/guide.md) · [Map format](docs/format.md) · [Recipes](docs/recipes.md) · [Theming](docs/theming.md) · [Extending](docs/extending.md) · [Editing](docs/editing.md) · [Map tools for AI](docs/map-tools.md) · [Maps from other apps](docs/import-export.md) · [Pictures and videos](docs/media.md) · [Languages](docs/languages.md) · [Accessibility](docs/accessibility.md) · [Coding agents](docs/agents.md) · [API reference](docs/api/README.md)
|
|
50
51
|
|
|
51
52
|
- Real-markdown authoring: headers and bullet lists nest the tree.
|
|
52
53
|
- YAML alternative for app-generated maps.
|
|
@@ -127,6 +128,8 @@ hydrateOrbitBlocks(container); // turns them into live m
|
|
|
127
128
|
|
|
128
129
|
**Without a browser,** `renderToSVGString(markdown, { width: 1200, height: 800 })` draws a standalone SVG, for static sites, emails and print.
|
|
129
130
|
|
|
131
|
+
**On paper,** `renderPrintLayout(element, markdown)` lays a map out for print or PDF: the map as a picture, then every idea with its note. Maps shared as web pages print that way.
|
|
132
|
+
|
|
130
133
|
[The guide](docs/guide.md) covers every option, event and method, and [Recipes](docs/recipes.md) shows Orbit in Vue, Svelte, Astro, Next.js and static sites.
|
|
131
134
|
|
|
132
135
|
## Write a map
|
|
@@ -170,8 +173,9 @@ npx @restless-dreams/orbit validate my-map.md # every problem a
|
|
|
170
173
|
npx @restless-dreams/orbit validate --json my-map.md # the same as JSON, for scripts and coding agents
|
|
171
174
|
npx @restless-dreams/orbit validate --tree my-map.md # the map as Orbit reads it, with each node's id
|
|
172
175
|
npx @restless-dreams/orbit render my-map.md --out my-map.svg # draw a map to SVG (--view all for the whole map)
|
|
173
|
-
npx @restless-dreams/orbit export --html my-map.md --out my-map.html # one web page to explore or
|
|
176
|
+
npx @restless-dreams/orbit export --html my-map.md --out my-map.html # one web page to explore, present or print the map, offline
|
|
174
177
|
npx @restless-dreams/orbit convert --to yaml my-map.md # markdown ↔ YAML (either direction)
|
|
178
|
+
npx @restless-dreams/orbit convert --to md Plan.xmind --out plan.md # a map from XMind, FreeMind, Freeplane or OPML (--to opml writes OPML)
|
|
175
179
|
npx @restless-dreams/orbit example > my-map.md # a map with the main features (--yaml for YAML)
|
|
176
180
|
```
|
|
177
181
|
|
|
@@ -604,6 +608,29 @@ const html = exportWebPage(markdown, { script, theme: 'night', media: { 'photos/
|
|
|
604
608
|
|
|
605
609
|
Options: `title`, `theme`, `viewMode`, `focus`, `language`, `strings` and `media`, the pictures to put inside the page by the address the map gives them.
|
|
606
610
|
|
|
611
|
+
## Printing and PDF
|
|
612
|
+
|
|
613
|
+
A map's web page prints laid out for paper, so the browser's Print, or Save as PDF, gives a document to email, hand in or print: the title, the map as a picture the width of the page, then every idea in reading order with its status, tags, priority, link (its address printed, and still a link in a PDF), note, picture and video. An idea never breaks across pages, a heading stays with what follows it, and the map prints on a light theme ([Printing](theming.md#printing)).
|
|
614
|
+
|
|
615
|
+
`renderPrintLayout` lays a map out that way in any page. It fills an element, which you show only in print:
|
|
616
|
+
|
|
617
|
+
```ts
|
|
618
|
+
import { renderPrintLayout } from '@restless-dreams/orbit';
|
|
619
|
+
|
|
620
|
+
renderPrintLayout(document.querySelector('#print'), markdown, { viewMode: 'all', language: 'es' });
|
|
621
|
+
```
|
|
622
|
+
|
|
623
|
+
```css
|
|
624
|
+
#print { display: none; }
|
|
625
|
+
|
|
626
|
+
@media print {
|
|
627
|
+
#app { display: none; }
|
|
628
|
+
#print { display: block; }
|
|
629
|
+
}
|
|
630
|
+
```
|
|
631
|
+
|
|
632
|
+
Options: `title`, `picture` (`false` leaves the map's picture out), `viewMode` and `focus` (what the picture shows), `theme`, `palette`, `language`, `strings`, `baseUrl`, `nodeTypes`, `statuses`, `nodeTypeFor` (a node type's `label`, `icon` and `details` print with its nodes) and `idPrefix`. A headless browser makes the PDF without anyone printing: `chromium --headless --no-pdf-header-footer --print-to-pdf=map.pdf map.html`, or Playwright's `page.pdf()`.
|
|
633
|
+
|
|
607
634
|
## Map tools for AI
|
|
608
635
|
|
|
609
636
|
`createMapTools` gives a language model tools to read a map and change it: read it as an outline with each node's id, search it, add branches written as markdown, rename, mark, move, sort and remove nodes. `tools.for('anthropic')`, `'openai'`, `'openai-chat'` or `'gemini'` lists them in the shape that provider's API takes, `tools.definitions` as MCP lists tools, and `tools.run(name, input)` carries out each call. On a map on screen, each change animates and fires `edit`; on a document, it changes the document in place. A call a model gets wrong changes nothing, and its result says what to send instead. [Map tools for AI](map-tools.md) has the loop for each provider.
|
|
@@ -657,11 +684,12 @@ npx @restless-dreams/orbit validate --json my-map.md # the same as JSO
|
|
|
657
684
|
npx @restless-dreams/orbit validate --tree my-map.md # the map as Orbit reads it, a node a line with its #id
|
|
658
685
|
npx @restless-dreams/orbit render my-map.md --out my-map.svg # draw a map to SVG (--theme, --view, --focus, --width, --height, --language)
|
|
659
686
|
npx @restless-dreams/orbit convert --to yaml my-map.md # markdown ↔ YAML (either direction)
|
|
687
|
+
npx @restless-dreams/orbit convert --to md Plan.xmind --out plan.md # a map from XMind, FreeMind, Freeplane or OPML; --to opml writes OPML
|
|
660
688
|
npx @restless-dreams/orbit example > my-map.md # a map with the main features (--yaml for YAML)
|
|
661
689
|
npx @restless-dreams/orbit --version # the installed version
|
|
662
690
|
```
|
|
663
691
|
|
|
664
|
-
Use `-` as the file to read from stdin, and `--help` after any command for its options. `validate` exits with 1 while the map has a problem (`--loose` accepts the ones the map works around, but never a map it can't read); every command exits with 2 for a mistake in the command itself. `render` links pictures on your computer from where the SVG goes, so it can move with them. The command itself is `orbit`: where the package is installed, scripts can call `orbit validate my-map.md` directly.
|
|
692
|
+
Use `-` as the file to read from stdin, and `--help` after any command for its options. `validate` exits with 1 while the map has a problem (`--loose` accepts the ones the map works around, but never a map it can't read); every command exits with 2 for a mistake in the command itself. `render` links pictures on your computer from where the SVG goes, so it can move with them. `convert` reads other apps' maps as [Maps from other apps](import-export.md) says, and writes `--out`'s pictures next to the map. The command itself is `orbit`: where the package is installed, scripts can call `orbit validate my-map.md` directly.
|
|
665
693
|
|
|
666
694
|
## Languages
|
|
667
695
|
|
|
@@ -890,6 +918,7 @@ tags: [frontend, sprint-3]
|
|
|
890
918
|
| Key | Value | Example | Effect |
|
|
891
919
|
| --- | --- | --- | --- |
|
|
892
920
|
| `color` | any CSS color | `"#F5A3A8"`, `tomato` | The node's color, instead of its branch's. Its children take shades of it. |
|
|
921
|
+
| `paletteColor` | number | `3` | Which of the palette's colors the node takes when it has no `color`: `1` is the first after the root's, cycling. Its children take shades of it. Editing on the map writes it to keep an idea's color (see [Colors](#colors)). |
|
|
893
922
|
| `icon` | emoji or short symbol | `🚀`, `"★"` | Shown before the text. Same as an emoji at the start of the title. |
|
|
894
923
|
| `status` | text | `active`, `done`, `blocked` | Shown in the detail sheet; some words get a colored dot (see [Statuses](#statuses)). |
|
|
895
924
|
| `priority` | number | `1` | Shown in the detail sheet. |
|
|
@@ -921,6 +950,18 @@ Any other key is kept as a **custom attribute** in `node.attrs`, for your own co
|
|
|
921
950
|
|
|
922
951
|
Other words get a neutral dot. Checkboxes set `todo` (`[ ]`), `done` (`[x]`) and `active` (`[/]`).
|
|
923
952
|
|
|
953
|
+
### Colors
|
|
954
|
+
|
|
955
|
+
The root takes the palette's first color: the theme's, or the frontmatter's `palette`. The top-level nodes take the others in turn, cycling, and deeper nodes take shades of their branch's color, so siblings stay apart. A node's own `color` wins, then its type's.
|
|
956
|
+
|
|
957
|
+
`paletteColor` gives a node one of the palette's colors by number, so it still changes with the theme. The top-level nodes without one take turns with the colors no top-level node has by number: give two nodes the same number for them to share a color.
|
|
958
|
+
|
|
959
|
+
Edits keep the colors of the nodes around them, on screen and through the map tools: adding, moving or removing a top-level node writes `paletteColor` where a color would change otherwise, as few times as it can.
|
|
960
|
+
|
|
961
|
+
- A moved node keeps its color, and a new one takes a color no other top-level node has, while there is one.
|
|
962
|
+
- A node moved under another one drops its `paletteColor` and takes its new branch's shade.
|
|
963
|
+
- Where the top level has more nodes than the palette has colors, colors repeat, and an edit that would write more numbers than there are colors writes none.
|
|
964
|
+
|
|
924
965
|
## Document options
|
|
925
966
|
|
|
926
967
|
Set in the frontmatter (markdown) or as top-level keys (YAML). Options passed in code win over these.
|
|
@@ -997,6 +1038,21 @@ A document says which version of the format it's written for with `schemaVersion
|
|
|
997
1038
|
- **Newer documents are read as well as they can be,** and reported with `unsupported-schema-version`, an error: apps should open them without letting them be edited. Nothing in them is lost by saving, but what this version doesn't know of them isn't understood either.
|
|
998
1039
|
- **Node types have versions of their own,** for the fields their nodes keep in attributes: `type: task@2` says a node was written for version 2 of `task`, and none means 1. A type that changes its fields says so with `version` and brings older nodes up to date with `upgrade` (see [Extending Orbit](extending.md#versions)); a node written for a newer version of its type is left as it is.
|
|
999
1040
|
|
|
1041
|
+
### Changing the format
|
|
1042
|
+
|
|
1043
|
+
Maps outlive the version of Orbit that wrote them. Older and newer versions read them, and so do apps that bundle a copy of their own. So each change to the format is one of three kinds:
|
|
1044
|
+
|
|
1045
|
+
- **Something older versions can do without:** a new attribute (like `paletteColor`) or a new document option. An older version keeps the attribute as a custom one, and the option as an unknown frontmatter key, which it reports. It writes both back unchanged and draws the map as it did before. No new version of the format is needed.
|
|
1046
|
+
- **Something an older version would misread:**
|
|
1047
|
+
- a new kind of value for an attribute it already reads: Orbit 0.1.0 takes `color: 3` for a color and draws it wrong, without a word;
|
|
1048
|
+
- new syntax in titles or notes: it shows `==First==` as text;
|
|
1049
|
+
- an attribute renamed, removed, or given a new meaning.
|
|
1050
|
+
|
|
1051
|
+
These raise `schemaVersion`, with a step in `FORMAT_UPGRADES` (`src/core/format-upgrades.ts`) that brings documents written for the version before up to date. Older versions then report `unsupported-schema-version`, and apps open those maps read-only.
|
|
1052
|
+
- **A change to a node type's own fields:** the type gets a new `version` and an `upgrade` (see [Extending Orbit](extending.md#versions)), not a new version of the format.
|
|
1053
|
+
|
|
1054
|
+
`tests/fixtures/releases/` holds maps as each released version wrote them. Every version must read each one without errors, write it back exactly, and understand it the same way, after its upgrades.
|
|
1055
|
+
|
|
1000
1056
|
## Node ids
|
|
1001
1057
|
|
|
1002
1058
|
Each node gets an id from its text: lowercase, accents folded, other letters kept (`Investigación` → `investigacion`, `研究` → `研究`, `Q3 plan` → `q3-plan`). Repeated ids get `-2`, `-3`, … in tree order. Set `id` to choose one; ids are what `renderer.flyTo(id)`, the `focus` export option and the React `focusedId` prop take.
|
|
@@ -1266,6 +1322,15 @@ npx @restless-dreams/orbit render roadmap.md --view all --out overview.svg
|
|
|
1266
1322
|
|
|
1267
1323
|
The SVG has its colors resolved and its labels as text, so it opens the same in browsers, design tools and image converters. Give each SVG inlined in one page its own `idPrefix`.
|
|
1268
1324
|
|
|
1325
|
+
For a PDF, export the map as a web page and print it, or have a headless browser do it. The page prints laid out for paper, with the map's picture, then every idea and its note:
|
|
1326
|
+
|
|
1327
|
+
```bash
|
|
1328
|
+
npx @restless-dreams/orbit export --html roadmap.md --out roadmap.html
|
|
1329
|
+
chromium --headless --no-pdf-header-footer --print-to-pdf=roadmap.pdf roadmap.html
|
|
1330
|
+
```
|
|
1331
|
+
|
|
1332
|
+
On your own pages, `renderPrintLayout` lays a map out the same way (see [Printing and PDF](guide.md#printing-and-pdf)).
|
|
1333
|
+
|
|
1269
1334
|
---
|
|
1270
1335
|
|
|
1271
1336
|
<!-- docs/theming.md -->
|
|
@@ -1453,6 +1518,21 @@ Each top-level branch takes the next palette color, and everything under it uses
|
|
|
1453
1518
|
|
|
1454
1519
|
`renderToSVGString(markdown, { theme })` and `orbit render --theme` resolve every property to a plain color, so exported files look the same anywhere. In the browser, `renderer.toSVGString()` also picks up the custom properties your page sets. Background patterns are CSS, so exports keep the `--orbit-surface` color without them.
|
|
1455
1520
|
|
|
1521
|
+
## Printing
|
|
1522
|
+
|
|
1523
|
+
Paper is light, so a map prints in its own theme when that's light, and in its light counterpart when it's dark. Nothing prints a background. `printTheme(theme)` gives the theme a map prints in, and `PRINT_THEMES` lists the pairs:
|
|
1524
|
+
|
|
1525
|
+
| Theme | Prints as |
|
|
1526
|
+
| --- | --- |
|
|
1527
|
+
| `night`, `cosmos`, `auto` | `paper` |
|
|
1528
|
+
| `glass-night` | `glass` |
|
|
1529
|
+
| `blueprint` | `ink` |
|
|
1530
|
+
| `flat-night` | `flat` |
|
|
1531
|
+
| `material-night` | `material` |
|
|
1532
|
+
| `liquid-night` | `liquid` |
|
|
1533
|
+
|
|
1534
|
+
A theme of your own that starts from a dark one prints as that one's counterpart, keeping only its colors (`color-root` to `color-7`). The print layout takes its text, link and chip colors from the theme it prints in; set `--orbit-print-text`, `--orbit-print-muted-text`, `--orbit-print-link-text`, `--orbit-print-chip-background`, `--orbit-print-chip-text`, `--orbit-print-border`, `--orbit-print-font` or `--orbit-print-display-font` to use your own.
|
|
1535
|
+
|
|
1456
1536
|
---
|
|
1457
1537
|
|
|
1458
1538
|
<!-- docs/extending.md -->
|
|
@@ -1821,10 +1901,11 @@ To let a language model make these changes, see [Map tools for AI](map-tools.md)
|
|
|
1821
1901
|
|
|
1822
1902
|
`editNode` and `editNewNode` return false, and the others `null`, when there's nothing to do: an id that isn't in the map, removing or moving the root, or moving a node inside itself. They work on any interactive map. `editable` adds the ways people edit by themselves: the +, editing on a long press, a right click, the context menu key or F2, and dragging.
|
|
1823
1903
|
|
|
1824
|
-
Besides `text`, `addNode` and `updateNode` take the node's attributes: `icon`, `color`, `status`, `priority`, `tags`, `collapsed`, `type`, `link`, `note`, `noteFormat`, `image`, `video`, `embed`, and `attrs` for your own.
|
|
1904
|
+
Besides `text`, `addNode` and `updateNode` take the node's attributes: `icon`, `color`, `paletteColor`, `status`, `priority`, `tags`, `collapsed`, `type`, `link`, `note`, `noteFormat`, `image`, `video`, `embed`, and `attrs` for your own.
|
|
1825
1905
|
|
|
1826
1906
|
- **Ids come from the text,** as when a map is read from markdown: "Night markets" is `night-markets`, and a second "Ideas" is `ideas-2`. New text gives a node a new id, so the saved file needs no `id` attribute. A node keeps its id when a `#id` link points at it, or when the id was set by hand. Read a node's current id from what the method returns.
|
|
1827
1907
|
- **Titles are one line:** line breaks become spaces, typed or pasted.
|
|
1908
|
+
- **Colors stay with their ideas.** Adding, moving or removing a top-level node leaves every other node's color as it was: the nodes whose turn in the palette would change get a `paletteColor` that keeps theirs, and a new node takes a color no other top-level node has (see [Colors](format.md#colors)).
|
|
1828
1909
|
- **The reader keeps their place.** When the node in focus, or a node above it, is removed, the nearest node left takes the focus and `focus-change` fires. A node in focus that gets a new id stays in focus.
|
|
1829
1910
|
- **Edits change the tree in place:** `getDocument().root`, which is the tree you passed to `render()` if you passed one.
|
|
1830
1911
|
|
|
@@ -2256,6 +2337,124 @@ With reduced motion (`prefers-reduced-motion: reduce`), zooms and steps happen a
|
|
|
2256
2337
|
|
|
2257
2338
|
---
|
|
2258
2339
|
|
|
2340
|
+
<!-- docs/import-export.md -->
|
|
2341
|
+
|
|
2342
|
+
# Maps from other apps
|
|
2343
|
+
|
|
2344
|
+
Orbit reads the maps people already have in other apps: OPML, which outliners and most mind map apps write, FreeMind and Freeplane's `.mm` files, and XMind's `.xmind` workbooks. They become ordinary Orbit maps, with their notes, links, folded branches and pictures. Orbit writes OPML too, so a map moves out as easily as it came in.
|
|
2345
|
+
|
|
2346
|
+
## Bring a map in
|
|
2347
|
+
|
|
2348
|
+
`importMap` reads a file's bytes (OPML and `.mm` files work as text too) and tells the format from what the file holds, then from its name:
|
|
2349
|
+
|
|
2350
|
+
```ts
|
|
2351
|
+
import { importMap, serializeOrbitMarkdown } from '@restless-dreams/orbit';
|
|
2352
|
+
|
|
2353
|
+
const bytes = new Uint8Array(await file.arrayBuffer());
|
|
2354
|
+
const { format, maps } = importMap(bytes, { name: file.name });
|
|
2355
|
+
|
|
2356
|
+
for (const map of maps) {
|
|
2357
|
+
const markdown = serializeOrbitMarkdown(map.document);
|
|
2358
|
+
|
|
2359
|
+
for (const [path, data] of Object.entries(map.media)) {
|
|
2360
|
+
// Save each picture at this path next to the map: `media/sunrise.png`.
|
|
2361
|
+
}
|
|
2362
|
+
|
|
2363
|
+
for (const warning of map.warnings) {
|
|
2364
|
+
console.warn(`${warning.message} [${warning.code}]`);
|
|
2365
|
+
}
|
|
2366
|
+
}
|
|
2367
|
+
```
|
|
2368
|
+
|
|
2369
|
+
- `maps` holds one map, or one for each sheet of an XMind workbook.
|
|
2370
|
+
- Each map's `document` is what `parseOrbit` would give for the markdown `serializeOrbitMarkdown` writes for it, ids included: draw it, edit it or save it as you would any map.
|
|
2371
|
+
- `media` holds the pictures inside the file, by the address the map gives them. Only XMind workbooks carry pictures; the others point at files on the computer they were made on, which can't come along.
|
|
2372
|
+
- `warnings` say what the other app had that Orbit keeps another way, or couldn't keep (see [Warnings](#warnings)).
|
|
2373
|
+
- Options: `name`, the file's name, which also names a map with several top ideas and no title; `format`, to skip telling the format (`opml`, `freemind` or `xmind`); and `untitled`, the name for a map with several top ideas and nothing to name it after (default "Untitled map").
|
|
2374
|
+
|
|
2375
|
+
`detectImportFormat(bytes, name)` tells the format without reading the map: `opml`, `freemind`, `xmind`, or `null` for anything else, Orbit's own markdown, YAML and zips included. `importOPML`, `importFreeMind` and `importXMind` read one format each and take the same options.
|
|
2376
|
+
|
|
2377
|
+
A file Orbit can't read at all throws an `OrbitImportError`, whose `code` says why:
|
|
2378
|
+
|
|
2379
|
+
| Code | Why |
|
|
2380
|
+
| --- | --- |
|
|
2381
|
+
| `unknown-format` | It's none of the formats Orbit reads. |
|
|
2382
|
+
| `unreadable` | It's damaged, or not what its name says: the message says where. |
|
|
2383
|
+
| `empty` | It holds no ideas. |
|
|
2384
|
+
| `password-protected` | An XMind workbook with a password. Open it in XMind and save it without one first. |
|
|
2385
|
+
| `too-large` | More than 100,000 ideas, or more than 500 levels deep. |
|
|
2386
|
+
|
|
2387
|
+
## From the command line
|
|
2388
|
+
|
|
2389
|
+
```bash
|
|
2390
|
+
npx @restless-dreams/orbit convert --to md Garden.xmind --out garden.md # every sheet, its pictures in media/
|
|
2391
|
+
npx @restless-dreams/orbit convert --to md outline.opml > outline.md
|
|
2392
|
+
npx @restless-dreams/orbit convert --to opml my-map.md --out my-map.opml
|
|
2393
|
+
```
|
|
2394
|
+
|
|
2395
|
+
Warnings go to stderr. With `--out`, a workbook's other sheets go next to the first (`garden-2.md`, `garden-3.md`) and its pictures in `media/` beside them; without it, Orbit writes the first sheet and says what it left out.
|
|
2396
|
+
|
|
2397
|
+
## What comes over
|
|
2398
|
+
|
|
2399
|
+
Everything a reader would miss comes over, as Orbit says it. Titles keep every word on one line, and an emoji at the start of a title becomes its icon, as in Orbit's own markdown. Notes in HTML become markdown: paragraphs, headings, lists, quotes, code, bold, italics, strikethrough, links and pictures. When markdown would show a note's words any other way (a `*` or `_` in it, or a line that starts like a list), the note stays plain text instead, exactly as written, without its formatting.
|
|
2400
|
+
|
|
2401
|
+
| | OPML | FreeMind and Freeplane | XMind |
|
|
2402
|
+
| --- | --- | --- | --- |
|
|
2403
|
+
| Note | `_note` (also `note`, and a feed's `description`) | the note and Freeplane's details, in HTML or plain text, and attributes as a list | the note, in HTML or plain text; XMind 8's comments, with their authors |
|
|
2404
|
+
| Link | `type="link"`'s or an include's `url`, a feed's `htmlUrl` or `xmlUrl` | `LINK` | the topic's link |
|
|
2405
|
+
| Links between ideas | `#id` | links to other nodes, and arrow links | links to other topics, and relationships |
|
|
2406
|
+
| Status | `_status` (`checked`, `unchecked`, `indeterminate`), `_complete`, `complete` | Freeplane's progress icons and checkboxes | task markers |
|
|
2407
|
+
| Priority | | priority icons (`full-1` to `full-9`) | priority markers |
|
|
2408
|
+
| Icon | an emoji at the start of the text | built-in icons, as emoji, and Freeplane's emoji | markers and stickers, as emoji |
|
|
2409
|
+
| Tags | `category` | | labels |
|
|
2410
|
+
| Color | | the background color | the fill color a topic was given |
|
|
2411
|
+
| Folded | | `FOLDED` | folded branches |
|
|
2412
|
+
| Picture | | a picture on the web | pictures inside the workbook, and on the web |
|
|
2413
|
+
|
|
2414
|
+
An idea with a link of its own keeps it, and a second link, or a link between ideas with a label, becomes a line of its note: `→ [builds on](#chapter-2)`.
|
|
2415
|
+
|
|
2416
|
+
## Warnings
|
|
2417
|
+
|
|
2418
|
+
What has no place in a tree of ideas keeps its content where it can, and each kind gets one warning that counts the places and names a few:
|
|
2419
|
+
|
|
2420
|
+
| Code | What changed |
|
|
2421
|
+
| --- | --- |
|
|
2422
|
+
| `floating-ideas-moved` | XMind's floating topics are ideas under the center. |
|
|
2423
|
+
| `summaries-moved` | XMind's summaries are ideas beside the ideas they sum up. |
|
|
2424
|
+
| `callouts-moved` | XMind's callouts are ideas under the topic they point at. |
|
|
2425
|
+
| `boundaries-in-notes` | A titled boundary's title is in the note of the idea whose ideas it surrounds. |
|
|
2426
|
+
| `links-in-notes` | A link beyond an idea's first, or a labeled link between ideas, is a line of its note. |
|
|
2427
|
+
| `missing-link-targets` | A link to an idea that isn't in the map (another sheet's, or one deleted) was left out. |
|
|
2428
|
+
| `icons-not-kept` | An icon with no emoji like it, or one past an idea's first, was left out. |
|
|
2429
|
+
| `pictures-not-kept` | A picture that isn't in the file was left out. |
|
|
2430
|
+
| `attachments-not-kept` | A file attached to an idea was left out; the idea keeps its name. |
|
|
2431
|
+
| `local-links-not-kept` | A link to a file on the computer the map was made on was left out. |
|
|
2432
|
+
| `encrypted-branches-not-read` | A branch FreeMind or Freeplane encrypted with a password couldn't be read; its ideas were left out. |
|
|
2433
|
+
| `formatting-not-kept` | A note is plain text, without its formatting, because markdown would have shown its words differently. |
|
|
2434
|
+
|
|
2435
|
+
Styles (fonts, shapes, line colors) and positions are left out without a warning: Orbit draws every map its own way.
|
|
2436
|
+
|
|
2437
|
+
## Write OPML
|
|
2438
|
+
|
|
2439
|
+
`exportOPML` writes a map, as markdown or YAML text or a document, as OPML 2.0:
|
|
2440
|
+
|
|
2441
|
+
```ts
|
|
2442
|
+
import { exportOPML } from '@restless-dreams/orbit';
|
|
2443
|
+
|
|
2444
|
+
const opml = exportOPML(markdown, { title: 'Launch plan' });
|
|
2445
|
+
```
|
|
2446
|
+
|
|
2447
|
+
- The root is the one top outline, and each node an outline with its icon and text in `text`.
|
|
2448
|
+
- A note goes in `_note`, as its markdown or plain text.
|
|
2449
|
+
- A link is `type="link"` with its `url`, a link between ideas included (`#research`).
|
|
2450
|
+
- A status is `_status`: `checked`, `unchecked` and `indeterminate` for `done`, `todo` and `active`, which outliners show as checkboxes; any other word as it is. Done statuses add `_complete="true"`, which Workflowy reads.
|
|
2451
|
+
- Tags are `category`, separated by commas.
|
|
2452
|
+
- `title` names the outline in its head. Default: the map's `title`, or its root's text.
|
|
2453
|
+
|
|
2454
|
+
Two attributes only Orbit reads say what the text alone can't: `_noteFormat` when a note would read the other way (a markdown note with a line break, which OPML readers show as two lines, or a plain one that looks like markdown), and `_textFormat="plain"` when a title looks like HTML. With them, a map Orbit writes reads back the same: its text, icons, notes, statuses, links, tags and tree. Pictures, videos, colors, priorities and folds have no place in OPML; the map's own file, or a zip of it and its pictures, keeps them.
|
|
2455
|
+
|
|
2456
|
+
---
|
|
2457
|
+
|
|
2259
2458
|
<!-- docs/languages.md -->
|
|
2260
2459
|
|
|
2261
2460
|
# Languages
|
|
@@ -2395,6 +2594,7 @@ A map that's just a list, or has several top headings, and has no title gets a r
|
|
|
2395
2594
|
| `readNote` | Read note | Leer nota |
|
|
2396
2595
|
| `openLink` | Open link | Abrir enlace |
|
|
2397
2596
|
| `goTo` | Go to {name} | Ir a {name} |
|
|
2597
|
+
| `seeIdea` | See {name} | Ver {name} |
|
|
2398
2598
|
| `details` | Details | Detalles |
|
|
2399
2599
|
| `close` | Close | Cerrar |
|
|
2400
2600
|
| `previous` | Previous | Anterior |
|
package/llms.txt
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
- Install with `npm install @restless-dreams/orbit`. The package is ESM-only. React (`@restless-dreams/orbit/react`) and marked (`@restless-dreams/orbit/marked`) are optional peers.
|
|
6
6
|
- A map fills its container: always give the container a height.
|
|
7
7
|
- Write maps in orbit-md, Orbit's markdown (every heading and list item is a node, text under it is its note, `<!-- orbit key: value -->` comments set attributes), or in YAML (`nodes:` with `text` and `children`).
|
|
8
|
-
- Check a map with `npx @restless-dreams/orbit validate --json map.md`: every problem comes with a suggested fix, and its line where it has one. `npx @restless-dreams/orbit example` prints a map with the main features, and `orbit convert --to yaml map.md` (or `--to md`) turns one flavor into the other.
|
|
8
|
+
- Check a map with `npx @restless-dreams/orbit validate --json map.md`: every problem comes with a suggested fix, and its line where it has one. `npx @restless-dreams/orbit example` prints a map with the main features, and `orbit convert --to yaml map.md` (or `--to md`) turns one flavor into the other, and `orbit convert --to md Plan.xmind --out plan.md` brings in a map from XMind, FreeMind, Freeplane or OPML.
|
|
9
9
|
- Draw one with `createOrbitMap(element, markdown, options)`, the `<orbit-map>` element, `<OrbitMindMap source={markdown} />` from `@restless-dreams/orbit/react`, or `renderToSVGString(markdown, options)` without a browser.
|
|
10
10
|
- Share a map as one web page, which opens in any browser, offline: `npx @restless-dreams/orbit export --html map.md --out map.html`.
|
|
11
11
|
- Let people build the map on the map with `editable: true` (a + in the orbit, text typed inside nodes), and save what they make with `map.toMarkdown()` on the `edit` event.
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
- [Extending](https://github.com/restless-dreams/orbit/blob/main/docs/extending.md): node types, your own actions, status words and named themes, for one map or every map
|
|
24
24
|
- [Editing](https://github.com/restless-dreams/orbit/blob/main/docs/editing.md): context menus on a long press, adding, renaming, moving and removing nodes on the map, and saving the result as orbit-md
|
|
25
25
|
- [Map tools for AI](https://github.com/restless-dreams/orbit/blob/main/docs/map-tools.md): tools for any model to read and change a map, on screen or on a server, for each provider's API and MCP
|
|
26
|
+
- [Maps from other apps](https://github.com/restless-dreams/orbit/blob/main/docs/import-export.md): bringing in OPML, FreeMind, Freeplane and XMind maps with their notes, links and pictures, what each warning means, and writing OPML
|
|
26
27
|
- [Languages](https://github.com/restless-dreams/orbit/blob/main/docs/languages.md): maps in the page's language, English and Spanish built in, adding languages and changing single words
|
|
27
28
|
- [Pictures and videos](https://github.com/restless-dreams/orbit/blob/main/docs/media.md): the media viewer, its keys, YouTube and Vimeo, sound and autoplay, and opening, playing and following media from code
|
|
28
29
|
- [Coding agents](https://github.com/restless-dreams/orbit/blob/main/docs/agents.md): the skill, validation as JSON, and messages for setup mistakes
|
package/package.json
CHANGED
package/skills/orbit/SKILL.md
CHANGED
|
@@ -96,7 +96,7 @@ const svg = renderToSVGString(markdown, { width: 1200, height: 800, theme: 'pape
|
|
|
96
96
|
|
|
97
97
|
- **Afterwards:** `map.render(source)` redraws and keeps the reader's place, `map.setOptions({ … })` changes options, `map.flyTo(id)` and `map.overview()` move, and `map.destroy()` cleans up. Events come through `map.on(name, listener)`, or as `orbit-<name>` DOM events that bubble out of `<orbit-map>` (details in `event.detail`).
|
|
98
98
|
- **Your own controls over the map:** `insets: { bottom: 72 }` fits each view into what a toolbar floating over the map leaves.
|
|
99
|
-
- **From the command line:** `npx @restless-dreams/orbit render map.md --view all --out map.svg --theme night` draws the whole map; without `--view all` it draws it as it opens, where only the root and its children have labels. `orbit convert --to yaml map.md` (or `--to md`) turns one flavor into the other.
|
|
99
|
+
- **From the command line:** `npx @restless-dreams/orbit render map.md --view all --out map.svg --theme night` draws the whole map; without `--view all` it draws it as it opens, where only the root and its children have labels. `orbit convert --to yaml map.md` (or `--to md`) turns one flavor into the other, and `orbit convert --to md Plan.xmind --out plan.md` brings in a map from XMind, FreeMind, Freeplane or OPML (`importMap` in code; [references/import-export.md](references/import-export.md)).
|
|
100
100
|
- **To share a map for review** (with someone, or to the user's own phone): `npx @restless-dreams/orbit export --html map.md --out map.html` makes one web page with Orbit, the map and its pictures inside, which opens in any browser, offline, to explore or present.
|
|
101
101
|
- **marked:** `new Marked(orbitMarked())` (from `@restless-dreams/orbit/marked`) turns ```` ```orbit ```` fences into placeholders, and `hydrateOrbitBlocks(container)` makes them maps. Fences take display options: ```` ```orbit height=480 view=all ````.
|
|
102
102
|
|
|
@@ -114,10 +114,11 @@ To let a language model make these changes, see [Map tools for AI](map-tools.md)
|
|
|
114
114
|
|
|
115
115
|
`editNode` and `editNewNode` return false, and the others `null`, when there's nothing to do: an id that isn't in the map, removing or moving the root, or moving a node inside itself. They work on any interactive map. `editable` adds the ways people edit by themselves: the +, editing on a long press, a right click, the context menu key or F2, and dragging.
|
|
116
116
|
|
|
117
|
-
Besides `text`, `addNode` and `updateNode` take the node's attributes: `icon`, `color`, `status`, `priority`, `tags`, `collapsed`, `type`, `link`, `note`, `noteFormat`, `image`, `video`, `embed`, and `attrs` for your own.
|
|
117
|
+
Besides `text`, `addNode` and `updateNode` take the node's attributes: `icon`, `color`, `paletteColor`, `status`, `priority`, `tags`, `collapsed`, `type`, `link`, `note`, `noteFormat`, `image`, `video`, `embed`, and `attrs` for your own.
|
|
118
118
|
|
|
119
119
|
- **Ids come from the text,** as when a map is read from markdown: "Night markets" is `night-markets`, and a second "Ideas" is `ideas-2`. New text gives a node a new id, so the saved file needs no `id` attribute. A node keeps its id when a `#id` link points at it, or when the id was set by hand. Read a node's current id from what the method returns.
|
|
120
120
|
- **Titles are one line:** line breaks become spaces, typed or pasted.
|
|
121
|
+
- **Colors stay with their ideas.** Adding, moving or removing a top-level node leaves every other node's color as it was: the nodes whose turn in the palette would change get a `paletteColor` that keeps theirs, and a new node takes a color no other top-level node has (see [Colors](format.md#colors)).
|
|
121
122
|
- **The reader keeps their place.** When the node in focus, or a node above it, is removed, the nearest node left takes the focus and `focus-change` fires. A node in focus that gets a new id stays in focus.
|
|
122
123
|
- **Edits change the tree in place:** `getDocument().root`, which is the tree you passed to `render()` if you passed one.
|
|
123
124
|
|
|
@@ -156,6 +156,7 @@ tags: [frontend, sprint-3]
|
|
|
156
156
|
| Key | Value | Example | Effect |
|
|
157
157
|
| --- | --- | --- | --- |
|
|
158
158
|
| `color` | any CSS color | `"#F5A3A8"`, `tomato` | The node's color, instead of its branch's. Its children take shades of it. |
|
|
159
|
+
| `paletteColor` | number | `3` | Which of the palette's colors the node takes when it has no `color`: `1` is the first after the root's, cycling. Its children take shades of it. Editing on the map writes it to keep an idea's color (see [Colors](#colors)). |
|
|
159
160
|
| `icon` | emoji or short symbol | `🚀`, `"★"` | Shown before the text. Same as an emoji at the start of the title. |
|
|
160
161
|
| `status` | text | `active`, `done`, `blocked` | Shown in the detail sheet; some words get a colored dot (see [Statuses](#statuses)). |
|
|
161
162
|
| `priority` | number | `1` | Shown in the detail sheet. |
|
|
@@ -187,6 +188,18 @@ Any other key is kept as a **custom attribute** in `node.attrs`, for your own co
|
|
|
187
188
|
|
|
188
189
|
Other words get a neutral dot. Checkboxes set `todo` (`[ ]`), `done` (`[x]`) and `active` (`[/]`).
|
|
189
190
|
|
|
191
|
+
### Colors
|
|
192
|
+
|
|
193
|
+
The root takes the palette's first color: the theme's, or the frontmatter's `palette`. The top-level nodes take the others in turn, cycling, and deeper nodes take shades of their branch's color, so siblings stay apart. A node's own `color` wins, then its type's.
|
|
194
|
+
|
|
195
|
+
`paletteColor` gives a node one of the palette's colors by number, so it still changes with the theme. The top-level nodes without one take turns with the colors no top-level node has by number: give two nodes the same number for them to share a color.
|
|
196
|
+
|
|
197
|
+
Edits keep the colors of the nodes around them, on screen and through the map tools: adding, moving or removing a top-level node writes `paletteColor` where a color would change otherwise, as few times as it can.
|
|
198
|
+
|
|
199
|
+
- A moved node keeps its color, and a new one takes a color no other top-level node has, while there is one.
|
|
200
|
+
- A node moved under another one drops its `paletteColor` and takes its new branch's shade.
|
|
201
|
+
- Where the top level has more nodes than the palette has colors, colors repeat, and an edit that would write more numbers than there are colors writes none.
|
|
202
|
+
|
|
190
203
|
## Document options
|
|
191
204
|
|
|
192
205
|
Set in the frontmatter (markdown) or as top-level keys (YAML). Options passed in code win over these.
|
|
@@ -263,6 +276,21 @@ A document says which version of the format it's written for with `schemaVersion
|
|
|
263
276
|
- **Newer documents are read as well as they can be,** and reported with `unsupported-schema-version`, an error: apps should open them without letting them be edited. Nothing in them is lost by saving, but what this version doesn't know of them isn't understood either.
|
|
264
277
|
- **Node types have versions of their own,** for the fields their nodes keep in attributes: `type: task@2` says a node was written for version 2 of `task`, and none means 1. A type that changes its fields says so with `version` and brings older nodes up to date with `upgrade` (see [Extending Orbit](extending.md#versions)); a node written for a newer version of its type is left as it is.
|
|
265
278
|
|
|
279
|
+
### Changing the format
|
|
280
|
+
|
|
281
|
+
Maps outlive the version of Orbit that wrote them. Older and newer versions read them, and so do apps that bundle a copy of their own. So each change to the format is one of three kinds:
|
|
282
|
+
|
|
283
|
+
- **Something older versions can do without:** a new attribute (like `paletteColor`) or a new document option. An older version keeps the attribute as a custom one, and the option as an unknown frontmatter key, which it reports. It writes both back unchanged and draws the map as it did before. No new version of the format is needed.
|
|
284
|
+
- **Something an older version would misread:**
|
|
285
|
+
- a new kind of value for an attribute it already reads: Orbit 0.1.0 takes `color: 3` for a color and draws it wrong, without a word;
|
|
286
|
+
- new syntax in titles or notes: it shows `==First==` as text;
|
|
287
|
+
- an attribute renamed, removed, or given a new meaning.
|
|
288
|
+
|
|
289
|
+
These raise `schemaVersion`, with a step in `FORMAT_UPGRADES` (`src/core/format-upgrades.ts`) that brings documents written for the version before up to date. Older versions then report `unsupported-schema-version`, and apps open those maps read-only.
|
|
290
|
+
- **A change to a node type's own fields:** the type gets a new `version` and an `upgrade` (see [Extending Orbit](extending.md#versions)), not a new version of the format.
|
|
291
|
+
|
|
292
|
+
`tests/fixtures/releases/` holds maps as each released version wrote them. Every version must read each one without errors, write it back exactly, and understand it the same way, after its upgrades.
|
|
293
|
+
|
|
266
294
|
## Node ids
|
|
267
295
|
|
|
268
296
|
Each node gets an id from its text: lowercase, accents folded, other letters kept (`Investigación` → `investigacion`, `研究` → `研究`, `Q3 plan` → `q3-plan`). Repeated ids get `-2`, `-3`, … in tree order. Set `id` to choose one; ids are what `renderer.flyTo(id)`, the `focus` export option and the React `focusedId` prop take.
|
|
@@ -395,6 +395,29 @@ const html = exportWebPage(markdown, { script, theme: 'night', media: { 'photos/
|
|
|
395
395
|
|
|
396
396
|
Options: `title`, `theme`, `viewMode`, `focus`, `language`, `strings` and `media`, the pictures to put inside the page by the address the map gives them.
|
|
397
397
|
|
|
398
|
+
## Printing and PDF
|
|
399
|
+
|
|
400
|
+
A map's web page prints laid out for paper, so the browser's Print, or Save as PDF, gives a document to email, hand in or print: the title, the map as a picture the width of the page, then every idea in reading order with its status, tags, priority, link (its address printed, and still a link in a PDF), note, picture and video. An idea never breaks across pages, a heading stays with what follows it, and the map prints on a light theme ([Printing](theming.md#printing)).
|
|
401
|
+
|
|
402
|
+
`renderPrintLayout` lays a map out that way in any page. It fills an element, which you show only in print:
|
|
403
|
+
|
|
404
|
+
```ts
|
|
405
|
+
import { renderPrintLayout } from '@restless-dreams/orbit';
|
|
406
|
+
|
|
407
|
+
renderPrintLayout(document.querySelector('#print'), markdown, { viewMode: 'all', language: 'es' });
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
```css
|
|
411
|
+
#print { display: none; }
|
|
412
|
+
|
|
413
|
+
@media print {
|
|
414
|
+
#app { display: none; }
|
|
415
|
+
#print { display: block; }
|
|
416
|
+
}
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
Options: `title`, `picture` (`false` leaves the map's picture out), `viewMode` and `focus` (what the picture shows), `theme`, `palette`, `language`, `strings`, `baseUrl`, `nodeTypes`, `statuses`, `nodeTypeFor` (a node type's `label`, `icon` and `details` print with its nodes) and `idPrefix`. A headless browser makes the PDF without anyone printing: `chromium --headless --no-pdf-header-footer --print-to-pdf=map.pdf map.html`, or Playwright's `page.pdf()`.
|
|
420
|
+
|
|
398
421
|
## Map tools for AI
|
|
399
422
|
|
|
400
423
|
`createMapTools` gives a language model tools to read a map and change it: read it as an outline with each node's id, search it, add branches written as markdown, rename, mark, move, sort and remove nodes. `tools.for('anthropic')`, `'openai'`, `'openai-chat'` or `'gemini'` lists them in the shape that provider's API takes, `tools.definitions` as MCP lists tools, and `tools.run(name, input)` carries out each call. On a map on screen, each change animates and fires `edit`; on a document, it changes the document in place. A call a model gets wrong changes nothing, and its result says what to send instead. [Map tools for AI](map-tools.md) has the loop for each provider.
|
|
@@ -448,11 +471,12 @@ npx @restless-dreams/orbit validate --json my-map.md # the same as JSO
|
|
|
448
471
|
npx @restless-dreams/orbit validate --tree my-map.md # the map as Orbit reads it, a node a line with its #id
|
|
449
472
|
npx @restless-dreams/orbit render my-map.md --out my-map.svg # draw a map to SVG (--theme, --view, --focus, --width, --height, --language)
|
|
450
473
|
npx @restless-dreams/orbit convert --to yaml my-map.md # markdown ↔ YAML (either direction)
|
|
474
|
+
npx @restless-dreams/orbit convert --to md Plan.xmind --out plan.md # a map from XMind, FreeMind, Freeplane or OPML; --to opml writes OPML
|
|
451
475
|
npx @restless-dreams/orbit example > my-map.md # a map with the main features (--yaml for YAML)
|
|
452
476
|
npx @restless-dreams/orbit --version # the installed version
|
|
453
477
|
```
|
|
454
478
|
|
|
455
|
-
Use `-` as the file to read from stdin, and `--help` after any command for its options. `validate` exits with 1 while the map has a problem (`--loose` accepts the ones the map works around, but never a map it can't read); every command exits with 2 for a mistake in the command itself. `render` links pictures on your computer from where the SVG goes, so it can move with them. The command itself is `orbit`: where the package is installed, scripts can call `orbit validate my-map.md` directly.
|
|
479
|
+
Use `-` as the file to read from stdin, and `--help` after any command for its options. `validate` exits with 1 while the map has a problem (`--loose` accepts the ones the map works around, but never a map it can't read); every command exits with 2 for a mistake in the command itself. `render` links pictures on your computer from where the SVG goes, so it can move with them. `convert` reads other apps' maps as [Maps from other apps](import-export.md) says, and writes `--out`'s pictures next to the map. The command itself is `orbit`: where the package is installed, scripts can call `orbit validate my-map.md` directly.
|
|
456
480
|
|
|
457
481
|
## Languages
|
|
458
482
|
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
<!-- A copy of docs/import-export.md, made by scripts/build-llms.ts, with links to other files pointing at the repository. Edit the original. -->
|
|
2
|
+
|
|
3
|
+
# Maps from other apps
|
|
4
|
+
|
|
5
|
+
Orbit reads the maps people already have in other apps: OPML, which outliners and most mind map apps write, FreeMind and Freeplane's `.mm` files, and XMind's `.xmind` workbooks. They become ordinary Orbit maps, with their notes, links, folded branches and pictures. Orbit writes OPML too, so a map moves out as easily as it came in.
|
|
6
|
+
|
|
7
|
+
## Bring a map in
|
|
8
|
+
|
|
9
|
+
`importMap` reads a file's bytes (OPML and `.mm` files work as text too) and tells the format from what the file holds, then from its name:
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
import { importMap, serializeOrbitMarkdown } from '@restless-dreams/orbit';
|
|
13
|
+
|
|
14
|
+
const bytes = new Uint8Array(await file.arrayBuffer());
|
|
15
|
+
const { format, maps } = importMap(bytes, { name: file.name });
|
|
16
|
+
|
|
17
|
+
for (const map of maps) {
|
|
18
|
+
const markdown = serializeOrbitMarkdown(map.document);
|
|
19
|
+
|
|
20
|
+
for (const [path, data] of Object.entries(map.media)) {
|
|
21
|
+
// Save each picture at this path next to the map: `media/sunrise.png`.
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
for (const warning of map.warnings) {
|
|
25
|
+
console.warn(`${warning.message} [${warning.code}]`);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
- `maps` holds one map, or one for each sheet of an XMind workbook.
|
|
31
|
+
- Each map's `document` is what `parseOrbit` would give for the markdown `serializeOrbitMarkdown` writes for it, ids included: draw it, edit it or save it as you would any map.
|
|
32
|
+
- `media` holds the pictures inside the file, by the address the map gives them. Only XMind workbooks carry pictures; the others point at files on the computer they were made on, which can't come along.
|
|
33
|
+
- `warnings` say what the other app had that Orbit keeps another way, or couldn't keep (see [Warnings](#warnings)).
|
|
34
|
+
- Options: `name`, the file's name, which also names a map with several top ideas and no title; `format`, to skip telling the format (`opml`, `freemind` or `xmind`); and `untitled`, the name for a map with several top ideas and nothing to name it after (default "Untitled map").
|
|
35
|
+
|
|
36
|
+
`detectImportFormat(bytes, name)` tells the format without reading the map: `opml`, `freemind`, `xmind`, or `null` for anything else, Orbit's own markdown, YAML and zips included. `importOPML`, `importFreeMind` and `importXMind` read one format each and take the same options.
|
|
37
|
+
|
|
38
|
+
A file Orbit can't read at all throws an `OrbitImportError`, whose `code` says why:
|
|
39
|
+
|
|
40
|
+
| Code | Why |
|
|
41
|
+
| --- | --- |
|
|
42
|
+
| `unknown-format` | It's none of the formats Orbit reads. |
|
|
43
|
+
| `unreadable` | It's damaged, or not what its name says: the message says where. |
|
|
44
|
+
| `empty` | It holds no ideas. |
|
|
45
|
+
| `password-protected` | An XMind workbook with a password. Open it in XMind and save it without one first. |
|
|
46
|
+
| `too-large` | More than 100,000 ideas, or more than 500 levels deep. |
|
|
47
|
+
|
|
48
|
+
## From the command line
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
npx @restless-dreams/orbit convert --to md Garden.xmind --out garden.md # every sheet, its pictures in media/
|
|
52
|
+
npx @restless-dreams/orbit convert --to md outline.opml > outline.md
|
|
53
|
+
npx @restless-dreams/orbit convert --to opml my-map.md --out my-map.opml
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Warnings go to stderr. With `--out`, a workbook's other sheets go next to the first (`garden-2.md`, `garden-3.md`) and its pictures in `media/` beside them; without it, Orbit writes the first sheet and says what it left out.
|
|
57
|
+
|
|
58
|
+
## What comes over
|
|
59
|
+
|
|
60
|
+
Everything a reader would miss comes over, as Orbit says it. Titles keep every word on one line, and an emoji at the start of a title becomes its icon, as in Orbit's own markdown. Notes in HTML become markdown: paragraphs, headings, lists, quotes, code, bold, italics, strikethrough, links and pictures. When markdown would show a note's words any other way (a `*` or `_` in it, or a line that starts like a list), the note stays plain text instead, exactly as written, without its formatting.
|
|
61
|
+
|
|
62
|
+
| | OPML | FreeMind and Freeplane | XMind |
|
|
63
|
+
| --- | --- | --- | --- |
|
|
64
|
+
| Note | `_note` (also `note`, and a feed's `description`) | the note and Freeplane's details, in HTML or plain text, and attributes as a list | the note, in HTML or plain text; XMind 8's comments, with their authors |
|
|
65
|
+
| Link | `type="link"`'s or an include's `url`, a feed's `htmlUrl` or `xmlUrl` | `LINK` | the topic's link |
|
|
66
|
+
| Links between ideas | `#id` | links to other nodes, and arrow links | links to other topics, and relationships |
|
|
67
|
+
| Status | `_status` (`checked`, `unchecked`, `indeterminate`), `_complete`, `complete` | Freeplane's progress icons and checkboxes | task markers |
|
|
68
|
+
| Priority | | priority icons (`full-1` to `full-9`) | priority markers |
|
|
69
|
+
| Icon | an emoji at the start of the text | built-in icons, as emoji, and Freeplane's emoji | markers and stickers, as emoji |
|
|
70
|
+
| Tags | `category` | | labels |
|
|
71
|
+
| Color | | the background color | the fill color a topic was given |
|
|
72
|
+
| Folded | | `FOLDED` | folded branches |
|
|
73
|
+
| Picture | | a picture on the web | pictures inside the workbook, and on the web |
|
|
74
|
+
|
|
75
|
+
An idea with a link of its own keeps it, and a second link, or a link between ideas with a label, becomes a line of its note: `→ [builds on](#chapter-2)`.
|
|
76
|
+
|
|
77
|
+
## Warnings
|
|
78
|
+
|
|
79
|
+
What has no place in a tree of ideas keeps its content where it can, and each kind gets one warning that counts the places and names a few:
|
|
80
|
+
|
|
81
|
+
| Code | What changed |
|
|
82
|
+
| --- | --- |
|
|
83
|
+
| `floating-ideas-moved` | XMind's floating topics are ideas under the center. |
|
|
84
|
+
| `summaries-moved` | XMind's summaries are ideas beside the ideas they sum up. |
|
|
85
|
+
| `callouts-moved` | XMind's callouts are ideas under the topic they point at. |
|
|
86
|
+
| `boundaries-in-notes` | A titled boundary's title is in the note of the idea whose ideas it surrounds. |
|
|
87
|
+
| `links-in-notes` | A link beyond an idea's first, or a labeled link between ideas, is a line of its note. |
|
|
88
|
+
| `missing-link-targets` | A link to an idea that isn't in the map (another sheet's, or one deleted) was left out. |
|
|
89
|
+
| `icons-not-kept` | An icon with no emoji like it, or one past an idea's first, was left out. |
|
|
90
|
+
| `pictures-not-kept` | A picture that isn't in the file was left out. |
|
|
91
|
+
| `attachments-not-kept` | A file attached to an idea was left out; the idea keeps its name. |
|
|
92
|
+
| `local-links-not-kept` | A link to a file on the computer the map was made on was left out. |
|
|
93
|
+
| `encrypted-branches-not-read` | A branch FreeMind or Freeplane encrypted with a password couldn't be read; its ideas were left out. |
|
|
94
|
+
| `formatting-not-kept` | A note is plain text, without its formatting, because markdown would have shown its words differently. |
|
|
95
|
+
|
|
96
|
+
Styles (fonts, shapes, line colors) and positions are left out without a warning: Orbit draws every map its own way.
|
|
97
|
+
|
|
98
|
+
## Write OPML
|
|
99
|
+
|
|
100
|
+
`exportOPML` writes a map, as markdown or YAML text or a document, as OPML 2.0:
|
|
101
|
+
|
|
102
|
+
```ts
|
|
103
|
+
import { exportOPML } from '@restless-dreams/orbit';
|
|
104
|
+
|
|
105
|
+
const opml = exportOPML(markdown, { title: 'Launch plan' });
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
- The root is the one top outline, and each node an outline with its icon and text in `text`.
|
|
109
|
+
- A note goes in `_note`, as its markdown or plain text.
|
|
110
|
+
- A link is `type="link"` with its `url`, a link between ideas included (`#research`).
|
|
111
|
+
- A status is `_status`: `checked`, `unchecked` and `indeterminate` for `done`, `todo` and `active`, which outliners show as checkboxes; any other word as it is. Done statuses add `_complete="true"`, which Workflowy reads.
|
|
112
|
+
- Tags are `category`, separated by commas.
|
|
113
|
+
- `title` names the outline in its head. Default: the map's `title`, or its root's text.
|
|
114
|
+
|
|
115
|
+
Two attributes only Orbit reads say what the text alone can't: `_noteFormat` when a note would read the other way (a markdown note with a line break, which OPML readers show as two lines, or a plain one that looks like markdown), and `_textFormat="plain"` when a title looks like HTML. With them, a map Orbit writes reads back the same: its text, icons, notes, statuses, links, tags and tree. Pictures, videos, colors, priorities and folds have no place in OPML; the map's own file, or a zip of it and its pictures, keeps them.
|
|
@@ -137,6 +137,7 @@ A map that's just a list, or has several top headings, and has no title gets a r
|
|
|
137
137
|
| `readNote` | Read note | Leer nota |
|
|
138
138
|
| `openLink` | Open link | Abrir enlace |
|
|
139
139
|
| `goTo` | Go to {name} | Ir a {name} |
|
|
140
|
+
| `seeIdea` | See {name} | Ver {name} |
|
|
140
141
|
| `details` | Details | Detalles |
|
|
141
142
|
| `close` | Close | Cerrar |
|
|
142
143
|
| `previous` | Previous | Anterior |
|
|
@@ -225,3 +225,12 @@ npx @restless-dreams/orbit render roadmap.md --view all --out overview.svg
|
|
|
225
225
|
```
|
|
226
226
|
|
|
227
227
|
The SVG has its colors resolved and its labels as text, so it opens the same in browsers, design tools and image converters. Give each SVG inlined in one page its own `idPrefix`.
|
|
228
|
+
|
|
229
|
+
For a PDF, export the map as a web page and print it, or have a headless browser do it. The page prints laid out for paper, with the map's picture, then every idea and its note:
|
|
230
|
+
|
|
231
|
+
```bash
|
|
232
|
+
npx @restless-dreams/orbit export --html roadmap.md --out roadmap.html
|
|
233
|
+
chromium --headless --no-pdf-header-footer --print-to-pdf=roadmap.pdf roadmap.html
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
On your own pages, `renderPrintLayout` lays a map out the same way (see [Printing and PDF](guide.md#printing-and-pdf)).
|