|
| 1 | +# Word Aligner — AI Agent Skill |
| 2 | + |
| 3 | +You have access to the **Word Aligner** tool. Use it whenever the user asks to visualize word-by-word alignment between two or more texts (translations, glosses, morpheme breakdowns, parallel corpora, etc.). |
| 4 | + |
| 5 | +## What it does |
| 6 | + |
| 7 | +Word Aligner generates a shareable interactive diagram that shows which words in one text correspond to which words in another text. Words are connected by colored arcs; multiple words that form a group share the same color. |
| 8 | + |
| 9 | +## When to use it |
| 10 | + |
| 11 | +- User asks to translate a phrase and show which words correspond to which |
| 12 | +- User wants to align a translation with its source (any languages, including RTL) |
| 13 | +- User wants to show a linguistic gloss (morpheme-by-morpheme breakdown) |
| 14 | +- User asks to visualize parallel text or bitext alignment |
| 15 | + |
| 16 | +## API |
| 17 | + |
| 18 | +**Endpoint:** `POST https://aligner.tinygods.dev/api/align` |
| 19 | +**Content-Type:** `application/json` |
| 20 | +**Returns:** `{ "url": "https://aligner.tinygods.dev/?data=..." }` |
| 21 | + |
| 22 | +Show the returned `url` to the user — it opens an interactive, shareable alignment diagram. |
| 23 | + |
| 24 | +## Request format |
| 25 | + |
| 26 | +```json |
| 27 | +{ |
| 28 | + "lines": ["line 0 text", "line 1 text"], |
| 29 | + "alignments": [[lineA, wordA, lineB, wordB], ...], |
| 30 | + "settings": { ... }, |
| 31 | + "pairs": [{ "upper": 0, "lower": 1, "gapPx": 120, "showConnectors": true }, ...] |
| 32 | +} |
| 33 | +``` |
| 34 | + |
| 35 | +### `lines` (required) |
| 36 | + |
| 37 | +Array of 1–8 lines, top to bottom. Each entry is either: |
| 38 | +- A **plain string**: `"Hello world"` |
| 39 | +- A **LineInput object** with per-line options: |
| 40 | + |
| 41 | +| Field | Type | Default | Description | |
| 42 | +|----------|---------------|---------|--------------------------------------------------------------| |
| 43 | +| `text` | string | — | Line text (required) | |
| 44 | +| `font` | string | Inter | Google Fonts family, e.g. `"Noto Serif"`, `"Noto Sans Arabic"` | |
| 45 | +| `sizePx` | integer 12–64 | 36 | Text size in px | |
| 46 | +| `gapPx` | integer 0–56 | 14 | Horizontal gap between word tokens in px | |
| 47 | +| `rtl` | boolean | false | Right-to-left layout (Hebrew, Arabic, Farsi, Urdu, etc.) | |
| 48 | + |
| 49 | +### `alignments` (optional) |
| 50 | + |
| 51 | +Array of `[lineA, wordA, lineB, wordB]` tuples: |
| 52 | +- Lines A and B must be **adjacent** (differ by exactly 1) |
| 53 | +- Indices are **0-based** |
| 54 | +- Multiple tuples sharing the same word get the **same color automatically** |
| 55 | + |
| 56 | +### `settings` (optional) |
| 57 | + |
| 58 | +| Field | Values | Default | Description | |
| 59 | +|-------------------|-------------------------------|----------|-----------------------------------------| |
| 60 | +| `palette` | `pastel` `vivid` `academic` | pastel | Color palette for connection lines | |
| 61 | +| `lineStyle` | `curved` `straight` | curved | Shape of connection lines | |
| 62 | +| `lineThickness` | number 1–8 | 3 | Stroke width | |
| 63 | +| `lineOpacity` | number 0.2–1 | 1 | Opacity of lines | |
| 64 | +| `background` | `light` `dark` | light | Preview background | |
| 65 | +| `theme` | `light` `dark` | light | UI theme (token chip color) | |
| 66 | +| `showNumbers` | boolean | false | Show line numbers | |
| 67 | +| `colorTokensByLink` | boolean | true | Tint word tokens in their connection color | |
| 68 | + |
| 69 | +### `pairs` (optional) |
| 70 | + |
| 71 | +Array of `{ upper, lower, gapPx?, showConnectors? }` — controls for specific adjacent line pairs: |
| 72 | +- `upper` / `lower`: 0-based line indices; `lower` must equal `upper + 1` |
| 73 | +- `gapPx` (12–156, default 120): vertical gap between the two lines |
| 74 | +- `showConnectors` (default true): set to `false` to hide connector lines for a pair (useful for gloss rows) |
| 75 | + |
| 76 | +## Word index counting |
| 77 | + |
| 78 | +Count words left to right starting from 0, splitting on whitespace. Characters `.`, `-`, `|` also split words. |
| 79 | + |
| 80 | +For RTL lines, word 0 is the **logically first** word (rightmost on screen for Arabic/Hebrew). |
| 81 | + |
| 82 | +**Example — "I have been going":** |
| 83 | +| Index | Word | |
| 84 | +|-------|------| |
| 85 | +| 0 | I | |
| 86 | +| 1 | have | |
| 87 | +| 2 | been | |
| 88 | +| 3 | going | |
| 89 | + |
| 90 | +## Examples |
| 91 | + |
| 92 | +### Simple two-language alignment |
| 93 | + |
| 94 | +```json |
| 95 | +{ |
| 96 | + "lines": ["Hello world", "Bonjour le monde"], |
| 97 | + "alignments": [ |
| 98 | + [0, 0, 1, 0], |
| 99 | + [0, 1, 1, 2] |
| 100 | + ] |
| 101 | +} |
| 102 | +``` |
| 103 | + |
| 104 | +"Hello" → "Bonjour", "world" → "monde" ("le" is word 1, "monde" is word 2). |
| 105 | + |
| 106 | +### One-to-many (one source word maps to several target words) |
| 107 | + |
| 108 | +```json |
| 109 | +{ |
| 110 | + "lines": ["Я ходил", "I have been going"], |
| 111 | + "alignments": [ |
| 112 | + [0, 0, 1, 0], |
| 113 | + [0, 1, 1, 1], |
| 114 | + [0, 1, 1, 2], |
| 115 | + [0, 1, 1, 3] |
| 116 | + ] |
| 117 | +} |
| 118 | +``` |
| 119 | + |
| 120 | +"ходил" (Russian past imperfective) maps to three English words: "have been going". All three connections are the same color. |
| 121 | + |
| 122 | +### RTL language (Hebrew) |
| 123 | + |
| 124 | +```json |
| 125 | +{ |
| 126 | + "lines": [ |
| 127 | + { "text": "שלום עולם", "rtl": true, "sizePx": 48, "font": "Noto Sans Hebrew" }, |
| 128 | + { "text": "Hello world", "sizePx": 40 } |
| 129 | + ], |
| 130 | + "alignments": [ |
| 131 | + [0, 0, 1, 0], |
| 132 | + [0, 1, 1, 1] |
| 133 | + ] |
| 134 | +} |
| 135 | +``` |
| 136 | + |
| 137 | +Word 0 of the Hebrew line is "שלום" (logically first), word 1 is "עולם". |
| 138 | + |
| 139 | +### Three lines with a gloss row |
| 140 | + |
| 141 | +```json |
| 142 | +{ |
| 143 | + "lines": [ |
| 144 | + "Я ходил", |
| 145 | + "I have been going", |
| 146 | + "1SG.NOM PST.IPFV" |
| 147 | + ], |
| 148 | + "alignments": [ |
| 149 | + [0, 0, 1, 0], |
| 150 | + [0, 1, 1, 1], |
| 151 | + [0, 1, 1, 2], |
| 152 | + [0, 1, 1, 3] |
| 153 | + ], |
| 154 | + "pairs": [ |
| 155 | + { "upper": 1, "lower": 2, "gapPx": 60, "showConnectors": false } |
| 156 | + ] |
| 157 | +} |
| 158 | +``` |
| 159 | + |
| 160 | +Lines 0–1 are connected. Line 2 is a gloss with smaller vertical gap and no connector arcs. |
| 161 | + |
| 162 | +### Custom visual style |
| 163 | + |
| 164 | +```json |
| 165 | +{ |
| 166 | + "lines": ["Hello world", "Bonjour le monde"], |
| 167 | + "alignments": [[0, 0, 1, 0], [0, 1, 1, 2]], |
| 168 | + "settings": { |
| 169 | + "palette": "vivid", |
| 170 | + "lineStyle": "straight", |
| 171 | + "background": "dark", |
| 172 | + "theme": "dark" |
| 173 | + } |
| 174 | +} |
| 175 | +``` |
| 176 | + |
| 177 | +## Workflow tip |
| 178 | + |
| 179 | +When asked to "translate and align": |
| 180 | +1. Translate the phrase into the target language yourself. |
| 181 | +2. Determine which source words correspond to which target words. |
| 182 | +3. Call the API with the source + translation as `lines` and the correspondences as `alignments`. |
| 183 | +4. Return the `url` to the user with a brief explanation of the alignment. |
| 184 | + |
| 185 | +If you are uncertain about word boundaries in a language, call the GET endpoint first to preview the tokenization: |
| 186 | +``` |
| 187 | +GET https://aligner.tinygods.dev/api/align?lines=your+text+here |
| 188 | +``` |
| 189 | +Open the URL and count the word boxes. |
0 commit comments