Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions WHATS_NEW.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# What's New — AutoControl

## What's new (2026-06-24) — Rich Clipboard Formats (RTF and CSV/TSV)

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).

- **`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`.

## What's new (2026-06-24) — Keyboard Focus Order (Tab sequence / WCAG audit / set-focus)

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).
Expand Down
49 changes: 49 additions & 0 deletions docs/source/Eng/doc/new_features/v185_features_doc.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
Rich Clipboard Formats — RTF and CSV/TSV
========================================

``rich_clipboard`` added ``CF_HTML`` for rich paste into Word / Outlook, but two
other cross-application clipboard formats were still missing:

* **RTF** (``"Rich Text Format"``) — the format almost every rich editor accepts
for styled paste. ``build_rtf`` / ``rtf_to_text`` build and strip RTF control
words and ``\uNNNN`` / ``\'XX`` escapes in pure Python, with a fully
unit-testable round-trip.
* **CSV / TSV** (the registered ``"Csv"`` format Excel reads) — ``rows_to_csv`` /
``csv_to_rows`` are a thin, delimiter-parametrised wrapper over the stdlib
``csv`` module, so a table can be put on / read off the clipboard.

The codecs are platform-independent and headless-testable; only the actual
clipboard I/O is Win32 (raising ``RuntimeError`` elsewhere, like the base
``clipboard`` module), and the byte transfer is a single generic helper shared by
both formats. Imports no ``PySide6``.

Headless API
------------

.. code-block:: python

from je_auto_control import (build_rtf, rtf_to_text, rows_to_csv,
csv_to_rows, set_clipboard_rtf, set_clipboard_csv)

rtf = build_rtf("Hello\nWorld") # minimal valid RTF document
rtf_to_text(rtf) # -> "Hello\nWorld"

rows_to_csv([["a", "b"], ["1", "2"]]) # 'a,b\r\n1,2\r\n'
csv_to_rows("a,b\r\n1,2\r\n") # [["a", "b"], ["1", "2"]]

set_clipboard_rtf("Paste me as styled text") # Windows
set_clipboard_csv([["Name", "Qty"], ["Pen", "3"]], delimiter="\t") # TSV

``build_rtf`` escapes braces / backslashes, turns newlines into ``\par`` and
non-ASCII characters into ``\uNNNN?`` escapes (the output is pure ASCII).
``set_clipboard_rtf`` / ``set_clipboard_csv`` also seed plain text by default so
plain editors still paste something; ``get_clipboard_rtf`` returns the raw RTF
string (feed it to ``rtf_to_text``) and ``get_clipboard_csv`` returns rows.

Executor commands
-----------------

``AC_set_clipboard_rtf`` / ``AC_get_clipboard_rtf`` / ``AC_set_clipboard_csv`` /
``AC_get_clipboard_csv`` (the sets take ``text`` / ``rows`` + ``delimiter``). They
are exposed as the matching ``ac_*`` MCP tools (the sets side-effect-only, the
gets read-only) and as Script Builder commands under **Data**.
44 changes: 44 additions & 0 deletions docs/source/Zh/doc/new_features/v185_features_doc.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
豐富剪貼簿格式——RTF 與 CSV/TSV
==============================

``rich_clipboard`` 已加入 ``CF_HTML`` 以便把豐富內容貼進 Word / Outlook,但仍缺少另外兩種
跨應用程式的剪貼簿格式:

* **RTF**(``"Rich Text Format"``)——幾乎每個豐富編輯器都接受、用於樣式貼上的格式。
``build_rtf`` / ``rtf_to_text`` 以純 Python 建立與剝除 RTF 控制字與 ``\uNNNN`` / ``\'XX``
轉義,並具備完全可單元測試的往返。
* **CSV / TSV**(Excel 讀取的已註冊 ``"Csv"`` 格式)——``rows_to_csv`` / ``csv_to_rows`` 是對
標準庫 ``csv`` 模組的薄包裝(可指定分隔符),讓表格能放上 / 讀下剪貼簿。

這些編解碼器與平台無關且可無頭測試;只有實際的剪貼簿 I/O 為 Win32(在其他平台拋出
``RuntimeError``,與基礎 ``clipboard`` 模組一致),且位元組傳輸是兩種格式共用的單一泛型輔助
函式。不匯入 ``PySide6``。

無頭 API
--------

.. code-block:: python

from je_auto_control import (build_rtf, rtf_to_text, rows_to_csv,
csv_to_rows, set_clipboard_rtf, set_clipboard_csv)

rtf = build_rtf("Hello\nWorld") # 最小的有效 RTF 文件
rtf_to_text(rtf) # -> "Hello\nWorld"

rows_to_csv([["a", "b"], ["1", "2"]]) # 'a,b\r\n1,2\r\n'
csv_to_rows("a,b\r\n1,2\r\n") # [["a", "b"], ["1", "2"]]

set_clipboard_rtf("以樣式文字貼上我") # Windows
set_clipboard_csv([["Name", "Qty"], ["Pen", "3"]], delimiter="\t") # TSV

``build_rtf`` 會轉義大括號 / 反斜線,把換行轉為 ``\par``,並把非 ASCII 字元轉為 ``\uNNNN?``
轉義(輸出為純 ASCII)。``set_clipboard_rtf`` / ``set_clipboard_csv`` 預設也會種入純文字,讓
純文字編輯器仍能貼上內容;``get_clipboard_rtf`` 回傳原始 RTF 字串(再餵給 ``rtf_to_text``),
``get_clipboard_csv`` 回傳列。

執行器指令
----------

``AC_set_clipboard_rtf`` / ``AC_get_clipboard_rtf`` / ``AC_set_clipboard_csv`` /
``AC_get_clipboard_csv``(set 取 ``text`` / ``rows`` 加 ``delimiter``)。皆以對應的 ``ac_*``
MCP 工具(set 為僅副作用、get 為唯讀)及 Script Builder 指令(位於 **Data** 分類下)形式提供。
8 changes: 8 additions & 0 deletions je_auto_control/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,11 @@
from je_auto_control.utils.focus_order import (
audit_focus_order, focus_control, is_interactive_role, tab_order,
)
# Rich clipboard formats — RTF + CSV/TSV codecs and Windows get / set
from je_auto_control.utils.clipboard_rich_formats import (
build_rtf, csv_to_rows, get_clipboard_csv, get_clipboard_rtf, rows_to_csv,
rtf_to_text, set_clipboard_csv, set_clipboard_rtf,
)
# VLM element locator (headless)
from je_auto_control.utils.vision import (
VLMNotAvailableError, click_by_description, locate_by_description,
Expand Down Expand Up @@ -1634,6 +1639,9 @@ def start_autocontrol_gui(*args, **kwargs):
"control_type_name", "humanize_role", "humanize_tree",
"assign_node_paths", "find_by_path",
"is_interactive_role", "tab_order", "audit_focus_order", "focus_control",
"build_rtf", "rtf_to_text", "rows_to_csv", "csv_to_rows",
"set_clipboard_rtf", "get_clipboard_rtf",
"set_clipboard_csv", "get_clipboard_csv",
# VLM locator
"VLMNotAvailableError", "locate_by_description", "click_by_description",
"verify_description",
Expand Down
25 changes: 25 additions & 0 deletions je_auto_control/gui/script_builder/command_schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -1597,6 +1597,31 @@ def _add_misc_specs(specs: List[CommandSpec]) -> None:
"AC_get_clipboard_files", "Data", "Get Clipboard Files",
description="Read the clipboard's file-drop list (CF_HDROP, Windows).",
))
specs.append(CommandSpec(
"AC_set_clipboard_rtf", "Data", "Set Clipboard RTF",
fields=(FieldSpec("text", FieldType.STRING,
placeholder="Styled paste text"),),
description="Put text on the clipboard as Rich Text Format (Windows).",
))
specs.append(CommandSpec(
"AC_get_clipboard_rtf", "Data", "Get Clipboard RTF",
description="Read the clipboard's RTF document string (Windows).",
))
specs.append(CommandSpec(
"AC_set_clipboard_csv", "Data", "Set Clipboard CSV/TSV",
fields=(
FieldSpec("rows", FieldType.STRING,
placeholder='[["a", "b"], ["1", "2"]]'),
FieldSpec("delimiter", FieldType.STRING, optional=True, default=","),
),
description="Put a table on the clipboard as the Csv format (Windows).",
))
specs.append(CommandSpec(
"AC_get_clipboard_csv", "Data", "Get Clipboard CSV/TSV",
fields=(FieldSpec("delimiter", FieldType.STRING, optional=True,
default=","),),
description="Read the clipboard's Csv content as rows (Windows).",
))
specs.append(CommandSpec(
"AC_watchdog_add", "Flow", "Watchdog: Add Popup Rule",
fields=(
Expand Down
11 changes: 11 additions & 0 deletions je_auto_control/utils/clipboard_rich_formats/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
"""Rich clipboard formats — RTF and CSV/TSV codecs + Windows get / set."""
from je_auto_control.utils.clipboard_rich_formats.clipboard_rich_formats import (
build_rtf, csv_to_rows, get_clipboard_csv, get_clipboard_rtf, rows_to_csv,
rtf_to_text, set_clipboard_csv, set_clipboard_rtf,
)

__all__ = [
"build_rtf", "rtf_to_text", "rows_to_csv", "csv_to_rows",
"set_clipboard_rtf", "get_clipboard_rtf",
"set_clipboard_csv", "get_clipboard_csv",
]
Loading
Loading