Skip to main content
Glama

ass_swap_an_pos

Swap vertical Aegisub alignment tags and shift \pos or MarginV to keep subtitle text visually fixed; optionally mirror horizontally.

Instructions

Mirror \an vertically and move \pos so the text does not budge.

The classic Aegisub helper: \an1 <-> \an7, \an2 <-> \an8, \an3 <-> \an9 (the middle row 4/5/6 has no vertical mirror and is left alone). Because the anchor flips between the bottom and the top of the line, \pos has to move by exactly the line height for the rendered text to stay where it was.

The shift is computed from the real rendered size of the line, measured with :func:asscore.measure.measure_line_render in script pixels (see :func:_measure_geometry). height is the vertical anchor separation (\an7 ink top minus \an1 ink top, both anchored at the same y), i.e. the line height, and width is the advance width (left side bearing + ink width + right side bearing, from the \an7/\an9 probes), i.e. the horizontal anchor separation. One numpad row or column step moves the anchor by half of the corresponding separation, and the anchor must move the other way for the ink to stay put::

row_step = new_row - row      # numpad grid, +-2 for a top/bottom swap
col_step = new_col - col      # +-2 for a left/right swap
dy = -(height / 2) * row_step # row 2 is the top of the screen
dx = +(width  / 2) * col_step # col 2 is the right of the screen

For \an7 -> \an1 that is row_step = -2 and dy = +height: the anchor walks down by the line height so the text stays on its screen row. A purely vertical mirror never changes the column, so dx stays 0 there; horizontal=True additionally mirrors the column (\an7 <-> \an9, \an1 <-> \an3, \an4 <-> \an6) and then dx = +width for \an7 -> \an3 (numpad columns grow rightwards, so the new right-edge anchor has to sit one width further right), which is what makes the width term reachable.

The measured text is the line with its own \pos/\move/\an removed, rendered with the line's style and the document's PlayRes, so the height matches what libass does for the real line.

Indices: \an/\pos are located inside the override blocks, so no plain-character index and no raw offset is taken as input. selection refers to event indices (doc.events() order, 0-based), and the plain text of every line survives byte for byte, so both index maps are unchanged.

Args: selection: selection spelling; the lines to rewrite. doc_id: document to edit; the current one when omitted. margin_mode: how to treat lines that have no \pos. By default they are skipped (there is nothing to shift). With margin_mode=True the vertical margin is rewritten instead: MarginV = PlayResY - MarginV - height, which keeps an alignment/margin-positioned line in place across the mirror. dry_run: compute and report the plan without writing (no snapshot). horizontal: also mirror the alignment column (180 degree mirror) and shift x by the advance width. Off by default, matching the Aegisub helper.

Returns {"doc_id", "dry_run", "margin_mode", "horizontal", "count", "changed", "written", "geometry_source", "lines": [...]}; each line entry has index, before, after, old_an, new_an, old_pos, new_pos, dx, dy, margin_v, measured ({"width", "height", "ink_width", "ink_height", "left_bearing"}), changed, skipped and reason. Retiming/render checks aside, the check to apply is that the ink bounding box before and after is identical — see the test suite, which asserts exactly that with :func:asscore.measure.measure_render.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
doc_idNo
dry_runNo
selectionYes
horizontalNo
margin_modeNo

Output Schema

TableJSON Schema
NameRequiredDescriptionDefault

No arguments

Schema Changelog

Changes observed during successful MCP inspections.

  1. First observedv0.1.0

TDQS

A4.2/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full burden and does so well: it discloses that dry_run writes nothing and takes no snapshot, the exact MarginV rewrite formula, that selection indexes are event indices and both index maps are unchanged, that plain text survives byte for byte, and the invariant that the ink bounding box is identical before and after.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness2/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The purpose is front-loaded, but the body is an essay of implementation-derivation math (row_step/dx formulas, measurement probes) that an agent selecting or invoking the tool does not need, and the actionable Args block is buried at the very end. Much of the length does not earn its place for tool selection.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given a 5-parameter mutation tool with an output schema, the description is complete: it covers every input's effect, the write/dry-run behavior, index stability, and even the returned keys (redundant against the output schema but harmless). Nothing an agent needs to call it correctly is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must compensate, and the Args block explains four of five parameters substantively (doc_id default, margin_mode semantics with formula, dry_run, horizontal). The remaining gap is that ``selection`` is only described as a "selection spelling" without defining the format, so it does not fully close the coverage hole.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The opening sentence states a specific verb and resource: mirror ``\an`` vertically and move ``\pos`` to keep text stationary. It names the exact transformation and the classic Aegisub helper it mirrors, so an agent can distinguish it from the many other tag-mutation siblings without opening a schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It gives real context: lines without ``\pos`` are skipped by default, ``margin_mode=True`` rewrites MarginV instead, and ``horizontal`` extends to a 180 degree mirror. However, there is no explicit when-to-use/when-not-to-use framing and no alternative sibling is named (e.g. set_tag/remove_tag), leaving routing to inference.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Deploy Server

Other Tools