# Quadrant Chart Draw labelled quadrant charts in Obsidian, with free-floating text placed anywhere on the plot. Charts live in `.mdx` files as plain YAML frontmatter, so they stay readable, hand-editable, and diff-friendly. ## What it does - **Any N × M grid**, not just four quadrants. Set columns and rows independently, from 1×1 to 12×12. - **Labelled axes** — name the X and Y axes, set their ranges, and the tick values are derived. Explicit ticks are supported when you want them. - **Axis notes** — a line of smaller muted text beside an axis, saying what the scale actually is: where the numbers came from, whether higher is better. Written into the `.mdx`, so it travels with the chart and can be generated by a script. - **Free text anywhere** — labels are not tied to quadrants. Drop one at any coordinate, drag it to reposition, double-click to edit, right-click to delete. Long ones wrap at a width you choose. - **Cell labels, notes and colours** — name each cell, add longer text underneath, and tint it. - **Styled labels** — give a label its own background plate, an outline, and its own text size, so it stays readable wherever it lands. - **Overlapping labels are reachable** — click the same spot again to step down through the stack, or use *Bring to front* / *Send to back*. - **Undo and redo** — Ctrl+Z and Ctrl+Shift+Z step through any change, including an accidental drag. - **Export** — save the chart as a JPG or PNG next to the `.mdx` file. - **Built-in examples** — a SWOT and a talent nine-box, creatable from the command palette. - **Your notes stay yours** — the chart is the YAML frontmatter; everything below it is free text the plugin never touches. - **Scriptable** — a chart is plain YAML, so anything that can write YAML can create one. ## Using it **Create a chart** — command palette → *Create a blank chart*. Pick a name and it opens straight into the editor. **Start from an example** — command palette → *Create from a worked example* → SWOT or talent nine-box. You get the cells, colours, labels and an explanatory body to edit. **Add text** — double-click anywhere on the plot, or use the *Add label* button. With nothing pointed at, a new label lands in the middle of the cell you last clicked. A label wider than the configured share of the plot (80% by default, see *Maximum label width* in the settings) wraps onto another line. **Move text** — drag it. **Edit text** — double-click it, or select it and use the *Label* button → *Edit text…*. Clearing the text deletes the label. **Delete text** — right-click it, or select it and use the *Label* button → *Delete label*. **Select a label or a cell** — click it. A click on a label selects the label, never the cell underneath; a click on empty space selects the cell. Both draw a dashed box. **Style a label** — select it, then *Label* → background colour, *Draw a box around it*, or a text size. *Default* clears the size override so the label follows the chart again. **Reach a label under another** — click the same spot repeatedly to step down the stack, or use *Send to back* / *Bring to front*. Order is stored in the file, so it survives a reload. **Name a cell** — click the cell, then the **Cell** button. **Change the grid** — the **Grid N×M** button. Columns and rows are independent, and the button always shows the current size. You can also drag a dashed split line on the canvas. **Name the axes / set the range** — **Axes** button, or click the X or Y caption on the canvas. **Add an axis note** — **Axes** → *Note under the X axis…* / *Note beside the Y axis…*. The X note is drawn below its caption and the Y note up the left gutter, each in smaller muted text; writing one reserves room for it, and clearing it gives the room back. A cell note says what is in a cell — an axis note says what the whole scale means, which is the one thing a quadrant chart cannot leave implicit. **Export an image** — the **Export** button, or the command palette → *Export as an image*. | Option | Notes | |---|---| | JPG | Smallest file. Always opaque — JPEG has no alpha channel. | | PNG | Lossless, sharper text. | | PNG, transparent | For slides and documents that supply their own background. | | SVG | Vector — scales to any size without blurring, and diffable in git. | | SVG, transparent | Same, with no background rectangle. | The file is written beside the `.mdx` and never overwrites an existing one. Everything you do is written straight to the file, so you can also just edit the YAML. ## Limits **A label is a label, not a paragraph.** Free text wraps, but only at the configured width — see *Maximum label width* in the settings, 80% of the plot area by default. A 91-character Chinese label at the default 14px needs 1180px, so it wraps onto two lines. Push past a few hundred characters and you get a tall stack of lines rather than a readable block. Two things follow: - Keep a label to a phrase or a short sentence. That is what the format is for. - When you need a paragraph, put it in the **cell's note**. Cell labels and notes wrap to the *cell* width, which is the right measure for a block of text, on screen and in every export format. **One halo colour per line.** The outline that keeps a label readable where it crosses a grid line is coloured to match the cell that line sits in, so it is invisible against its own background. A label that overhangs a differently tinted cell still shows the ring on the overhanging part. **No multi-select.** One label at a time — no marquee, no group operations. ## Keyboard | Shortcut | Action | |---|---| | Ctrl/Cmd+Z | Undo the last change to this chart | | Ctrl/Cmd+Shift+Z | Redo | | Ctrl+Y | Redo (Windows convention) | A new change after an undo discards the redo branch, so redo can only replay something you have not since replaced. ## The `.mdx` format A chart is a YAML frontmatter block. The body after it is yours and is never modified. ```markdown --- quadrant-chart: 1 title: Q1 Priorities x: label: Impact min: 0 max: 10 y: label: Urgency min: 0 max: 10 grid: columns: 2 rows: 2 cells: - col: 1 row: 1 label: Do now color: "#d93025" note: Anything blocking a release items: - id: a1 text: Rework the sync engine x: 8.2 y: 9 background: "#fdd663" # optional plate behind the text box: true # optional outline around it --- Notes go here. The plugin never rewrites anything below the frontmatter. ``` Coordinates are stored in **data space** — the axis `min`/`max` range — not in pixels. Resizing the pane or changing the range never rewrites your numbers, which is what lets a chart saved on a phone land in the same logical spot on a desktop. `quadrant-chart: 1` is what marks the file as a chart. Without it, a `.mdx` file is left alone. A damaged file still opens: missing fields fall back to defaults, a reversed axis range is repaired, and out-of-range cells are dropped. The point is that you can always see the file to fix it. One trap worth writing down, because it is not visible from the format: > **`row 0` is the bottom band.** Rows are counted along Y from the *minimum*, and columns along X > from the minimum. Get it backwards and a SWOT silently swaps "helpful" with "harmful" — the chart > still renders, it is just wrong. The same applies to a nine-box, where it inverts high and low > potential. ## Generating charts programmatically Because a chart is plain YAML, **you do not need the plugin to create one.** Anything that can write YAML can produce a chart; the plugin only has to render it. That includes an LLM, a cron job, a spreadsheet export, or another plugin. A reference implementation ships with this workspace: ```bash python3 /home/wang/wk/Script/mdx_chart.py --help ``` ```python import sys; sys.path.insert(0, "/home/wang/wk/Script") from mdx_chart import Chart, Axis chart = Chart( title="SWOT", x=Axis("Internal ——— External", -5, 5), y=Axis("Harmful ——— Helpful", -5, 5), columns=2, rows=2, ) chart.add_cell(0, 1, "Strengths", color="green", note="Internal · helpful") chart.add_cell(0, 0, "Weaknesses", color="red", note="Internal · harmful") chart.add_cell(1, 1, "Opportunities", color="blue", note="External · helpful") chart.add_cell(1, 0, "Threats", color="orange", note="External · harmful") chart.add_item("Plain-text format — git-diffable", x=-3.4, y=4.2, item_id="s1") chart.save("SWOT.mdx", body="# Notes\n\nThis body is never rewritten.\n") ``` The same operations from the command line: ```bash python3 Script/mdx_chart.py create --path Q1.mdx --title "Q1 Priorities" --columns 2 --rows 2 python3 Script/mdx_chart.py set-cell --path Q1.mdx --col 1 --row 1 --label "Do now" --color red python3 Script/mdx_chart.py add-item --path Q1.mdx --text "Rework sync engine" --x 8.2 --y 9 python3 Script/mdx_chart.py show --path Q1.mdx # JSON, for an agent to read ``` It edits in place: `load()` returns the chart *and* the body, and `save()` rewrites only the frontmatter, so notes written under a chart survive. It refuses to overwrite on `create` without `--force`, and refuses to touch a file that is not a chart at all. **Full reference — [`docs/API.md`](docs/API.md).** It is written to be read by an agent: the field table, the complete API surface, the CLI, worked recipes for SWOT and the talent nine-box, the colour palette, a pre-write checklist, and the `processFrontMatter` warning below. Worked examples, both generated through that API: - `/home/wang/wk/wk/SWOT-插件价值.mdx` - `/home/wang/wk/wk/人才九宫格-示例.mdx` ### If you write a plugin that edits `.mdx` Use `app.vault.process(file, fn)` for the atomic read-modify-write. > **Do not use `fileManager.processFrontMatter` on a `.mdx` file.** Measured on a live vault, it > resolves successfully and writes nothing — the file stays byte-identical. It is a *silent* no-op, > which is why it cost several rounds to track down. Its callback also takes `(frontmatter) => void`, > so a returned object is discarded. ## Note on the extension `.mdx` is also React's component-file suffix. There is no conflict inside Obsidian, which has no MDX pipeline of its own, but be aware of it if a vault ever gains a real MDX toolchain. ## Building ```bash npm install npm run build # typecheck + production bundle -> main.js npm test # 377 tests: format, geometry, hit-testing, undo, image export, the # save path, and interoperability with Script/mdx_chart.py ``` To build straight into a vault's plugin folder: ```bash OUTDIR=~/vault/.obsidian/plugins/quadrant-chart npm run build ```