Skip to content

Commit cacc323

Browse files
committed
Add clipboard_rich_formats: RTF + CSV/TSV clipboard codecs
rich_clipboard added CF_HTML, but RTF (the format rich editors accept for styled paste) and the Csv format Excel reads were still missing. Add both: build_rtf/rtf_to_text build and strip RTF control words and escapes in pure Python with a unit-testable round-trip, and rows_to_csv/ csv_to_rows wrap the stdlib csv module (delimiter-parametrised for TSV). The Win32 get/set share one generic byte-transfer helper; codecs are platform-independent and headless-tested.
1 parent 6f2f1b8 commit cacc323

11 files changed

Lines changed: 574 additions & 0 deletions

File tree

WHATS_NEW.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# What's New — AutoControl
22

3+
## What's new (2026-06-24) — Rich Clipboard Formats (RTF and CSV/TSV)
4+
5+
Put styled text and tables on the clipboard for cross-app paste into Word and Excel. Full reference: [`docs/source/Eng/doc/new_features/v185_features_doc.rst`](docs/source/Eng/doc/new_features/v185_features_doc.rst).
6+
7+
- **`build_rtf` / `rtf_to_text` / `rows_to_csv` / `csv_to_rows` + `set_clipboard_rtf` / `get_clipboard_rtf` / `set_clipboard_csv` / `get_clipboard_csv`** (`AC_set_clipboard_rtf`, `AC_get_clipboard_rtf`, `AC_set_clipboard_csv`, `AC_get_clipboard_csv`): `rich_clipboard` added CF_HTML, but RTF (the format rich editors accept) and the `Csv` format Excel reads were still missing. This adds both: `build_rtf`/`rtf_to_text` build and strip RTF control words and `\uNNNN` / `\'XX` escapes in pure Python (fully unit-testable round-trip), and `rows_to_csv`/`csv_to_rows` wrap the stdlib `csv` module (delimiter-parametrised, so `\t` gives TSV). The codecs are platform-independent; the Win32 get/set share one generic byte-transfer helper, and the sets seed plain text so plain editors still paste. No `PySide6`.
8+
39
## What's new (2026-06-24) — Keyboard Focus Order (Tab sequence / WCAG audit / set-focus)
410

511
Reason about keyboard navigation: the Tab order, a WCAG focus-order audit, and set-focus. Full reference: [`docs/source/Eng/doc/new_features/v184_features_doc.rst`](docs/source/Eng/doc/new_features/v184_features_doc.rst).
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
Rich Clipboard Formats — RTF and CSV/TSV
2+
========================================
3+
4+
``rich_clipboard`` added ``CF_HTML`` for rich paste into Word / Outlook, but two
5+
other cross-application clipboard formats were still missing:
6+
7+
* **RTF** (``"Rich Text Format"``) — the format almost every rich editor accepts
8+
for styled paste. ``build_rtf`` / ``rtf_to_text`` build and strip RTF control
9+
words and ``\uNNNN`` / ``\'XX`` escapes in pure Python, with a fully
10+
unit-testable round-trip.
11+
* **CSV / TSV** (the registered ``"Csv"`` format Excel reads) — ``rows_to_csv`` /
12+
``csv_to_rows`` are a thin, delimiter-parametrised wrapper over the stdlib
13+
``csv`` module, so a table can be put on / read off the clipboard.
14+
15+
The codecs are platform-independent and headless-testable; only the actual
16+
clipboard I/O is Win32 (raising ``RuntimeError`` elsewhere, like the base
17+
``clipboard`` module), and the byte transfer is a single generic helper shared by
18+
both formats. Imports no ``PySide6``.
19+
20+
Headless API
21+
------------
22+
23+
.. code-block:: python
24+
25+
from je_auto_control import (build_rtf, rtf_to_text, rows_to_csv,
26+
csv_to_rows, set_clipboard_rtf, set_clipboard_csv)
27+
28+
rtf = build_rtf("Hello\nWorld") # minimal valid RTF document
29+
rtf_to_text(rtf) # -> "Hello\nWorld"
30+
31+
rows_to_csv([["a", "b"], ["1", "2"]]) # 'a,b\r\n1,2\r\n'
32+
csv_to_rows("a,b\r\n1,2\r\n") # [["a", "b"], ["1", "2"]]
33+
34+
set_clipboard_rtf("Paste me as styled text") # Windows
35+
set_clipboard_csv([["Name", "Qty"], ["Pen", "3"]], delimiter="\t") # TSV
36+
37+
``build_rtf`` escapes braces / backslashes, turns newlines into ``\par`` and
38+
non-ASCII characters into ``\uNNNN?`` escapes (the output is pure ASCII).
39+
``set_clipboard_rtf`` / ``set_clipboard_csv`` also seed plain text by default so
40+
plain editors still paste something; ``get_clipboard_rtf`` returns the raw RTF
41+
string (feed it to ``rtf_to_text``) and ``get_clipboard_csv`` returns rows.
42+
43+
Executor commands
44+
-----------------
45+
46+
``AC_set_clipboard_rtf`` / ``AC_get_clipboard_rtf`` / ``AC_set_clipboard_csv`` /
47+
``AC_get_clipboard_csv`` (the sets take ``text`` / ``rows`` + ``delimiter``). They
48+
are exposed as the matching ``ac_*`` MCP tools (the sets side-effect-only, the
49+
gets read-only) and as Script Builder commands under **Data**.
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
豐富剪貼簿格式——RTF 與 CSV/TSV
2+
==============================
3+
4+
``rich_clipboard`` 已加入 ``CF_HTML`` 以便把豐富內容貼進 Word / Outlook,但仍缺少另外兩種
5+
跨應用程式的剪貼簿格式:
6+
7+
* **RTF**(``"Rich Text Format"``)——幾乎每個豐富編輯器都接受、用於樣式貼上的格式。
8+
``build_rtf`` / ``rtf_to_text`` 以純 Python 建立與剝除 RTF 控制字與 ``\uNNNN`` / ``\'XX``
9+
轉義,並具備完全可單元測試的往返。
10+
* **CSV / TSV**(Excel 讀取的已註冊 ``"Csv"`` 格式)——``rows_to_csv`` / ``csv_to_rows`` 是對
11+
標準庫 ``csv`` 模組的薄包裝(可指定分隔符),讓表格能放上 / 讀下剪貼簿。
12+
13+
這些編解碼器與平台無關且可無頭測試;只有實際的剪貼簿 I/O 為 Win32(在其他平台拋出
14+
``RuntimeError``,與基礎 ``clipboard`` 模組一致),且位元組傳輸是兩種格式共用的單一泛型輔助
15+
函式。不匯入 ``PySide6``。
16+
17+
無頭 API
18+
--------
19+
20+
.. code-block:: python
21+
22+
from je_auto_control import (build_rtf, rtf_to_text, rows_to_csv,
23+
csv_to_rows, set_clipboard_rtf, set_clipboard_csv)
24+
25+
rtf = build_rtf("Hello\nWorld") # 最小的有效 RTF 文件
26+
rtf_to_text(rtf) # -> "Hello\nWorld"
27+
28+
rows_to_csv([["a", "b"], ["1", "2"]]) # 'a,b\r\n1,2\r\n'
29+
csv_to_rows("a,b\r\n1,2\r\n") # [["a", "b"], ["1", "2"]]
30+
31+
set_clipboard_rtf("以樣式文字貼上我") # Windows
32+
set_clipboard_csv([["Name", "Qty"], ["Pen", "3"]], delimiter="\t") # TSV
33+
34+
``build_rtf`` 會轉義大括號 / 反斜線,把換行轉為 ``\par``,並把非 ASCII 字元轉為 ``\uNNNN?``
35+
轉義(輸出為純 ASCII)。``set_clipboard_rtf`` / ``set_clipboard_csv`` 預設也會種入純文字,讓
36+
純文字編輯器仍能貼上內容;``get_clipboard_rtf`` 回傳原始 RTF 字串(再餵給 ``rtf_to_text``),
37+
``get_clipboard_csv`` 回傳列。
38+
39+
執行器指令
40+
----------
41+
42+
``AC_set_clipboard_rtf`` / ``AC_get_clipboard_rtf`` / ``AC_set_clipboard_csv`` /
43+
``AC_get_clipboard_csv``(set 取 ``text`` / ``rows`` 加 ``delimiter``)。皆以對應的 ``ac_*``
44+
MCP 工具(set 為僅副作用、get 為唯讀)及 Script Builder 指令(位於 **Data** 分類下)形式提供。

je_auto_control/__init__.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,11 @@
6666
from je_auto_control.utils.focus_order import (
6767
audit_focus_order, focus_control, is_interactive_role, tab_order,
6868
)
69+
# Rich clipboard formats — RTF + CSV/TSV codecs and Windows get / set
70+
from je_auto_control.utils.clipboard_rich_formats import (
71+
build_rtf, csv_to_rows, get_clipboard_csv, get_clipboard_rtf, rows_to_csv,
72+
rtf_to_text, set_clipboard_csv, set_clipboard_rtf,
73+
)
6974
# VLM element locator (headless)
7075
from je_auto_control.utils.vision import (
7176
VLMNotAvailableError, click_by_description, locate_by_description,
@@ -1634,6 +1639,9 @@ def start_autocontrol_gui(*args, **kwargs):
16341639
"control_type_name", "humanize_role", "humanize_tree",
16351640
"assign_node_paths", "find_by_path",
16361641
"is_interactive_role", "tab_order", "audit_focus_order", "focus_control",
1642+
"build_rtf", "rtf_to_text", "rows_to_csv", "csv_to_rows",
1643+
"set_clipboard_rtf", "get_clipboard_rtf",
1644+
"set_clipboard_csv", "get_clipboard_csv",
16371645
# VLM locator
16381646
"VLMNotAvailableError", "locate_by_description", "click_by_description",
16391647
"verify_description",

je_auto_control/gui/script_builder/command_schema.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1597,6 +1597,31 @@ def _add_misc_specs(specs: List[CommandSpec]) -> None:
15971597
"AC_get_clipboard_files", "Data", "Get Clipboard Files",
15981598
description="Read the clipboard's file-drop list (CF_HDROP, Windows).",
15991599
))
1600+
specs.append(CommandSpec(
1601+
"AC_set_clipboard_rtf", "Data", "Set Clipboard RTF",
1602+
fields=(FieldSpec("text", FieldType.STRING,
1603+
placeholder="Styled paste text"),),
1604+
description="Put text on the clipboard as Rich Text Format (Windows).",
1605+
))
1606+
specs.append(CommandSpec(
1607+
"AC_get_clipboard_rtf", "Data", "Get Clipboard RTF",
1608+
description="Read the clipboard's RTF document string (Windows).",
1609+
))
1610+
specs.append(CommandSpec(
1611+
"AC_set_clipboard_csv", "Data", "Set Clipboard CSV/TSV",
1612+
fields=(
1613+
FieldSpec("rows", FieldType.STRING,
1614+
placeholder='[["a", "b"], ["1", "2"]]'),
1615+
FieldSpec("delimiter", FieldType.STRING, optional=True, default=","),
1616+
),
1617+
description="Put a table on the clipboard as the Csv format (Windows).",
1618+
))
1619+
specs.append(CommandSpec(
1620+
"AC_get_clipboard_csv", "Data", "Get Clipboard CSV/TSV",
1621+
fields=(FieldSpec("delimiter", FieldType.STRING, optional=True,
1622+
default=","),),
1623+
description="Read the clipboard's Csv content as rows (Windows).",
1624+
))
16001625
specs.append(CommandSpec(
16011626
"AC_watchdog_add", "Flow", "Watchdog: Add Popup Rule",
16021627
fields=(
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
"""Rich clipboard formats — RTF and CSV/TSV codecs + Windows get / set."""
2+
from je_auto_control.utils.clipboard_rich_formats.clipboard_rich_formats import (
3+
build_rtf, csv_to_rows, get_clipboard_csv, get_clipboard_rtf, rows_to_csv,
4+
rtf_to_text, set_clipboard_csv, set_clipboard_rtf,
5+
)
6+
7+
__all__ = [
8+
"build_rtf", "rtf_to_text", "rows_to_csv", "csv_to_rows",
9+
"set_clipboard_rtf", "get_clipboard_rtf",
10+
"set_clipboard_csv", "get_clipboard_csv",
11+
]

0 commit comments

Comments
 (0)