Auto-build: opencode (powershell) from da6ac2b

This commit is contained in:
github-actions[bot]
2026-05-25 10:08:58 +00:00
commit 874aaf7b68
210 changed files with 108303 additions and 0 deletions
+77
View File
@@ -0,0 +1,77 @@
---
name: img-grid
description: Наложить пронумерованную сетку на изображение. Используй при анализе скриншота макета или печатной формы — измерить пропорции колонок перед генерацией табличного документа
argument-hint: <ImagePath> [-c COLS]
allowed-tools:
- Bash
- Read
---
# /img-grid — Сетка для анализа макета
Накладывает пронумерованную сетку на изображение печатной формы. Позволяет точно определить границы колонок, их пропорции и span-ы для генерации макета табличного документа.
## Использование
```
/img-grid <ImagePath> [-c COLS] [-o OUTPUT]
```
## Параметры
| Параметр | Обязательный | По умолчанию | Описание |
|-----------|:------------:|--------------|-----------------------------------------------|
| ImagePath | да | — | Путь к изображению (PNG, JPG) |
| -c COLS | нет | 50 | Количество вертикальных делений |
| -r ROWS | нет | авто | Количество горизонтальных делений (авто = квадратные ячейки) |
| -o OUTPUT | нет | `<name>-grid.<ext>` | Путь для результата |
## Команда
```bash
python ".opencode/skills/img-grid/scripts/overlay-grid.py" "<ImagePath>" [-c 50] [-o "<OutputPath>"]
```
Требуется Python 3 с библиотекой Pillow (`pip install Pillow`).
## Что делает
1. Рисует полупрозрачные вертикальные (красные) и горизонтальные (синие) линии
2. Нумерует линии в отдельных полях сверху и слева (не перекрывает содержимое)
3. Каждая 5-я и 10-я линия выделены ярче для удобства счёта
## Как использовать результат
### 1. Определить границы колонок
Посмотреть на изображение с сеткой и записать координаты вертикальных границ каждой колонки таблицы (в номерах grid-линий).
### 2. Найти базовую решётку
Если на форме несколько таблиц с разной раскладкой (например, шапка документа и основная таблица), объединить все граничные точки. Каждый сегмент между соседними границами — одна колонка MXL.
Пример для М-11:
- Шапка: границы 0, 2, 4, 9, 14, 21, 28, 34, 40, 48
- Таблица: границы 0, 2, 4, 11, 16, 19, 23, 28, 32, 36, 42, 48
- Объединение: 0, 2, 4, 9, 11, 14, 16, 19, 21, 23, 28, 32, 34, 36, 40, 42, 48
- Результат: **16 базовых колонок** с пропорциями 2, 2, 5, 2, 3, 2, 3, 2, 2, 5, 4, 2, 2, 4, 2, 6
### 3. Записать в JSON DSL
```json
{
"columns": 16,
"page": "A4-landscape",
"columnWidths": {
"1": "2x", "2": "2x", "3": "5x", "4": "2x", "5": "3x",
"6": "2x", "7": "3x", "8": "2x", "9": "2x", "10": "5x",
"11": "4x", "12": "2x", "13": "2x", "14": "4x", "15": "2x", "16": "6x"
}
}
```
Поле `"page"` позволяет компилятору автоматически вычислить абсолютные ширины из пропорций.
### 4. Скомпилировать
`/mxl-compile``/mxl-validate``/mxl-info`
@@ -0,0 +1,115 @@
"""Overlay a numbered grid on an image to help determine column/row proportions.
Usage: python overlay-grid.py <image> [-c COLS] [-r ROWS] [-o OUTPUT]
The grid helps an LLM count "squares" to determine exact column widths
and positions when analyzing printed forms for MXL template generation.
Numbers are rendered in a dedicated margin band outside the image content,
so they never overlap with the form and remain readable at any grid density.
"""
import argparse
import os
import sys
from PIL import Image, ImageDraw, ImageFont
MARGIN_TOP = 20
MARGIN_LEFT = 24
def main():
sys.stdout.reconfigure(encoding="utf-8")
sys.stderr.reconfigure(encoding="utf-8")
parser = argparse.ArgumentParser(description="Overlay numbered grid on image")
parser.add_argument("image", help="Input image path")
parser.add_argument("-c", "--cols", type=int, default=50,
help="Number of vertical divisions (default: 50)")
parser.add_argument("-r", "--rows", type=int, default=0,
help="Number of horizontal divisions (0 = auto, match cell aspect ratio)")
parser.add_argument("-o", "--output", help="Output path (default: <name>-grid.<ext>)")
args = parser.parse_args()
src = Image.open(args.image).convert("RGBA")
sw, sh = src.size
cols = args.cols
step_x = sw / cols
rows = args.rows
if rows == 0:
rows = round(sh / step_x)
step_y = sh / rows
# Canvas with margins for labels
cw = MARGIN_LEFT + sw
ch = MARGIN_TOP + sh
canvas = Image.new("RGBA", (cw, ch), (255, 255, 255, 255))
canvas.paste(src, (MARGIN_LEFT, MARGIN_TOP))
overlay = Image.new("RGBA", (cw, ch), (0, 0, 0, 0))
draw = ImageDraw.Draw(overlay)
# Font for labels in margin
label_font_size = 12
try:
label_font = ImageFont.truetype("arial.ttf", label_font_size)
except Exception:
label_font = ImageFont.load_default()
# --- Vertical lines + numbers in top margin ---
for i in range(cols + 1):
x = MARGIN_LEFT + round(i * step_x)
major = i % 10 == 0
mid = i % 5 == 0
alpha = 160 if major else (110 if mid else 40)
lw = 2 if major else 1
draw.line([(x, MARGIN_TOP), (x, ch)], fill=(255, 0, 0, alpha), width=lw)
# Labels: always show multiples of 5; show all if spacing allows
show_label = major or mid or step_x >= 20
if show_label:
label = str(i)
bbox = label_font.getbbox(label)
tw = bbox[2] - bbox[0]
tx = x - tw // 2
ty = 2
color = (200, 0, 0, 255) if (major or mid) else (200, 0, 0, 180)
draw.text((tx, ty), label, fill=color, font=label_font)
# --- Horizontal lines + numbers in left margin ---
for j in range(rows + 1):
y = MARGIN_TOP + round(j * step_y)
major = j % 10 == 0
mid = j % 5 == 0
alpha = 160 if major else (110 if mid else 20)
lw = 2 if major else 1
draw.line([(MARGIN_LEFT, y), (cw, y)], fill=(0, 0, 200, alpha), width=lw)
show_label = major or mid or step_y >= 20
if show_label:
label = str(j)
bbox = label_font.getbbox(label)
tw = bbox[2] - bbox[0]
tx = MARGIN_LEFT - tw - 3
ty = y - label_font_size // 2
color = (0, 0, 200, 255) if (major or mid) else (0, 0, 200, 180)
draw.text((tx, ty), label, fill=color, font=label_font)
result = Image.alpha_composite(canvas, overlay).convert("RGB")
if args.output:
out = args.output
else:
name, ext = os.path.splitext(args.image)
out = f"{name}-grid{ext}"
result.save(out)
print(f"Grid: {cols} x {rows} cells")
print(f"Cell size: {step_x:.1f} x {step_y:.1f} px")
print(f"Image: {sw} x {sh} px")
print(f"Saved: {out}")
if __name__ == "__main__":
main()