CharDesk Docs

Canvas Searching

Locate rendered text in original Cell coordinates.

HEAD a63778e

canvas_search searches the active Canvas or current Slide page's rendered Cells. It is read-only, works on source-backed projections, and does not move the camera or change interaction. Source files, read rulers, style notes, and sampled maps are not search content.

Query and position

{ "query": "Hello", "viewport": [0, 0, 100, 50] }

在 full-access local MCP 中可增加 canvasId 指定目标 Canvas;省略时使用 当前 active Canvas。list action 返回短持久化 ID;只有需要跨 Canvas 时才使用它。

query is literal and case-sensitive by default. Set ignoreCase: true for Unicode case folding; set regex: true for RE2 syntax. Lookaround and backreferences are unsupported. Each match must consume non-empty text at complete grapheme boundaries; zero-length regex matches are skipped.

Actual LF separates a two-dimensional template. Rows match at the same Cell x on consecutive Canvas rows; widths may differ. Spaces are literal, and content after each row's match is unconstrained. Every template row must contain a non-whitespace character; empty/blank rows and other control characters are rejected.

{ "query": "Hello \\w+\nWelcome", "regex": true, "ignoreCase": true }

This finds Hello Alice above Welcome, not merely two nearby strings. Queries are limited to 4096 UTF-16 units and 64 rows.

Omit viewport to search all content. A supplied rectangle uses the same Cell coordinates as reading, but search always uses original precision regardless of read's sampling sampleSize. The complete template must fit inside it. Boundary-clipped glyphs do not match.

Sparse rows and limits

Literal matching joins only gaps shorter than the longest template row. Regex matching uses each finite stored row envelope, clipped by the input viewport: missing internal Cells are spaces, and ^/$ refer to that envelope's edges, not storage spans or the template's origin. Unstored exterior space is not searchable. Envelopes over 16384 Cells are rejected, never split silently.

A shared scan/time budget returns search_limit with no partial results. Narrow viewport and retry. RE2JS avoids backtracking; the budget also bounds sparse expansion and repeated template checks.

Results and continuation

{
  "canvasId": "cv-7k3m9a1b2c",
  "matches": [{ "origin": [10, 20], "bounds": [10, 20, 5, 1], "content": "Hello" }],
  "next": null
}

Results are ordered by the match's y, then x, with non-overlapping matches. Each item contains only origin, exact bounds, and the matched content. There is no surrounding preview, padding, ruler, border, or style note. bounds is a Cell rectangle that can be passed directly to canvas_read when the agent needs context or styles.

Search is intentionally a locator, not a context reader. The agent decides when more context is worth a separate read.

Each call returns at most 20 matches. Non-null next is the last returned match's origin [x,y]; pass it as after with identical query, regex, ignoreCase, and viewport to continue strictly after that position. Confirm the canvasId still belongs to the same Canvas. Calls observe current content, not a shared snapshot; restart after an edit if consistency matters. Empty results have matches: [] and next: null.

Authorities