@amaster.ai/pi-lark 0.1.9 → 0.1.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -3
- package/package.json +2 -2
- package/skills/lark-approval/SKILL.md +2 -2
- package/skills/lark-approval/references/lark-approval-instances-initiated.md +5 -0
- package/skills/lark-approval/references/lark-approval-tasks-add-sign.md +68 -20
- package/skills/lark-approval/references/lark-approval-tasks-query.md +5 -0
- package/skills/lark-apps/SKILL.md +1 -1
- package/skills/lark-apps/references/lark-apps-cache.md +38 -5
- package/skills/lark-base/SKILL.md +131 -11
- package/skills/lark-base/references/lark-base-app.md +18 -0
- package/skills/lark-base/references/lark-base-dashboard-block-config.md +28 -1
- package/skills/lark-base/references/lark-base-dashboard.md +29 -11
- package/skills/lark-base/references/lark-base-data-query.md +2 -6
- package/skills/lark-base/references/lark-base-field-extension.md +170 -0
- package/skills/lark-base/references/lark-base-field-lookup.md +1 -1
- package/skills/lark-base/references/lark-base-field-schema.md +10 -1
- package/skills/lark-base/references/lark-base-filter-condition.md +32 -5
- package/skills/lark-base/references/lark-base-form-detail.md +1 -1
- package/skills/lark-base/references/lark-base-form-questions-create.md +36 -5
- package/skills/lark-base/references/lark-base-form-submit.md +2 -2
- package/skills/lark-base/references/lark-base-record-history-list.md +19 -2
- package/skills/lark-base/references/lark-base-record-query-and-analysis-sop.md +95 -205
- package/skills/lark-base/references/lark-base-template-center.md +199 -0
- package/skills/lark-calendar/SKILL.md +14 -5
- package/skills/lark-calendar/references/lark-calendar-join-event.md +43 -0
- package/skills/lark-calendar/references/lark-calendar-transfer.md +89 -0
- package/skills/lark-doc/references/lark-doc-fetch.md +1 -1
- package/skills/lark-drive/references/lark-drive-add-comment.md +2 -2
- package/skills/lark-drive/references/lark-drive-member-remove.md +2 -1
- package/skills/lark-im/SKILL.md +23 -3
- package/skills/lark-im/references/lark-im-message-read-status.md +96 -0
- package/skills/lark-im/references/lark-im-messages-edit.md +89 -0
- package/skills/lark-im/references/lark-im-messages-mget.md +8 -0
- package/skills/lark-im/references/lark-im-messages-reply.md +2 -0
- package/skills/lark-im/references/lark-im-messages-send.md +6 -2
- package/skills/lark-mail/references/lark-mail-draft-create.md +12 -12
- package/skills/lark-mail/references/lark-mail-forward.md +17 -17
- package/skills/lark-mail/references/lark-mail-reply-all.md +8 -8
- package/skills/lark-mail/references/lark-mail-reply.md +6 -6
- package/skills/lark-mail/references/lark-mail-send.md +20 -20
- package/skills/lark-mail/references/lark-mail-template-create.md +7 -6
- package/skills/lark-mail/references/lark-mail-template-update.md +7 -6
- package/skills/lark-markdown/SKILL.md +1 -1
- package/skills/lark-meeting/SKILL.md +150 -0
- package/skills/{lark-minutes → lark-meeting}/references/lark-minutes-apply-permission.md +2 -5
- package/skills/lark-meeting/references/lark-minutes-detail.md +52 -0
- package/skills/{lark-minutes → lark-meeting}/references/lark-minutes-download.md +4 -6
- package/skills/{lark-minutes → lark-meeting}/references/lark-minutes-search.md +4 -34
- package/skills/{lark-minutes → lark-meeting}/references/lark-minutes-speaker-replace.md +3 -4
- package/skills/{lark-minutes → lark-meeting}/references/lark-minutes-summary.md +3 -5
- package/skills/{lark-minutes → lark-meeting}/references/lark-minutes-todo.md +44 -16
- package/skills/{lark-minutes → lark-meeting}/references/lark-minutes-update.md +2 -3
- package/skills/lark-meeting/references/lark-minutes-upload.md +71 -0
- package/skills/lark-meeting/references/lark-note-detail.md +15 -0
- package/skills/lark-meeting/references/lark-note-transcript.md +19 -0
- package/skills/lark-meeting/references/lark-vc-agent-meeting-end.md +26 -0
- package/skills/lark-meeting/references/lark-vc-agent-meeting-invite.md +32 -0
- package/skills/{lark-vc-agent → lark-meeting}/references/lark-vc-agent-meeting-join.md +11 -56
- package/skills/{lark-vc-agent → lark-meeting}/references/lark-vc-agent-meeting-leave.md +2 -41
- package/skills/lark-meeting/references/lark-vc-detail.md +31 -0
- package/skills/lark-meeting/references/lark-vc-meeting-countdown.md +103 -0
- package/skills/{lark-vc → lark-meeting}/references/lark-vc-meeting-events.md +9 -98
- package/skills/{lark-vc → lark-meeting}/references/lark-vc-meeting-list-active.md +4 -29
- package/skills/{lark-vc → lark-meeting}/references/lark-vc-meeting-message-send.md +3 -5
- package/skills/lark-meeting/references/lark-vc-meeting-screenshot.md +34 -0
- package/skills/{lark-vc → lark-meeting}/references/lark-vc-recording.md +4 -65
- package/skills/{lark-vc → lark-meeting}/references/lark-vc-search.md +9 -28
- package/skills/lark-meeting/scenes/create-and-edit-minutes.md +147 -0
- package/skills/lark-meeting/scenes/live-meeting-attend.md +164 -0
- package/skills/lark-meeting/scenes/live-meeting-interact.md +101 -0
- package/skills/lark-meeting/scenes/query-meeting-and-artifacts.md +90 -0
- package/skills/lark-meeting/scenes/query-minutes-and-artifacts.md +70 -0
- package/skills/lark-meeting/scenes/query-note-and-artifacts.md +127 -0
- package/skills/lark-minutes/SKILL.md +5 -203
- package/skills/lark-note/SKILL.md +5 -88
- package/skills/lark-shared/SKILL.md +25 -224
- package/skills/lark-shared/references/lark-shared-config-init.md +12 -0
- package/skills/lark-shared/references/lark-shared-high-risk-approval.md +38 -0
- package/skills/lark-shared/references/lark-shared-identity-and-permissions.md +105 -0
- package/skills/lark-shared/references/lark-shared-output-contract.md +17 -0
- package/skills/lark-shared/references/lark-shared-update-notice.md +23 -0
- package/skills/lark-sheets/SKILL.md +76 -60
- package/skills/lark-sheets/references/lark-sheets-batch-update.md +83 -14
- package/skills/lark-sheets/references/lark-sheets-chart.md +296 -159
- package/skills/lark-sheets/references/lark-sheets-conditional-format.md +46 -9
- package/skills/lark-sheets/references/lark-sheets-filter.md +1 -1
- package/skills/lark-sheets/references/lark-sheets-formula-translation.md +78 -65
- package/skills/lark-sheets/references/lark-sheets-formula-verify.md +21 -17
- package/skills/lark-sheets/references/lark-sheets-pivot-table.md +2 -1
- package/skills/lark-sheets/references/lark-sheets-range-operations.md +1 -1
- package/skills/lark-sheets/references/lark-sheets-read-data.md +8 -5
- package/skills/lark-sheets/references/lark-sheets-search-replace.md +4 -4
- package/skills/lark-sheets/references/lark-sheets-sheet-structure.md +2 -2
- package/skills/lark-sheets/references/lark-sheets-sparkline.md +1 -0
- package/skills/lark-sheets/references/lark-sheets-styles-put.md +3 -3
- package/skills/lark-sheets/references/lark-sheets-visual-standards.md +6 -4
- package/skills/lark-sheets/references/lark-sheets-workbook.md +3 -1
- package/skills/lark-sheets/references/lark-sheets-write-cells.md +50 -48
- package/skills/lark-sheets/scripts/lark_chart_layout_check.py +472 -0
- package/skills/lark-slides/SKILL.md +2 -0
- package/skills/lark-slides/references/cli/lark-slides-add-slide.md +6 -6
- package/skills/lark-slides/references/cli/lark-slides-delete-slide.md +3 -3
- package/skills/lark-slides/references/cli/lark-slides-replace-slide.md +11 -11
- package/skills/lark-slides/references/cli/lark-slides-xml-presentation-slide-get.md +1 -1
- package/skills/lark-slides/references/cli/lark-slides-xml-presentation-slide-replace.md +2 -2
- package/skills/lark-slides/references/workflow/slides-editing.md +11 -11
- package/skills/lark-slides/references/xml/slides_xml_schema_definition.xml +103 -14
- package/skills/lark-task/SKILL.md +1 -1
- package/skills/lark-vc/SKILL.md +5 -205
- package/skills/lark-vc-agent/SKILL.md +5 -206
- package/skills/lark-workflow-meeting-summary/SKILL.md +20 -13
- package/skills/lark-base/references/lark-base-cell-value.md +0 -165
- package/skills/lark-base/references/lark-base-data-analysis-pandas.md +0 -93
- package/skills/lark-base/references/lark-base-data-analysis-python-stdlib.md +0 -120
- package/skills/lark-base/references/lark-base-record-batch-create.md +0 -63
- package/skills/lark-base/references/lark-base-record-batch-update.md +0 -57
- package/skills/lark-base/references/lark-base-record-query-and-analysis-cloud-sop.md +0 -145
- package/skills/lark-minutes/references/lark-minutes-detail.md +0 -63
- package/skills/lark-minutes/references/lark-minutes-upload.md +0 -104
- package/skills/lark-note/references/lark-note-detail.md +0 -29
- package/skills/lark-note/references/lark-note-transcript.md +0 -25
- package/skills/lark-vc/references/lark-vc-detail.md +0 -49
- package/skills/lark-vc/references/vc-domain-boundaries.md +0 -203
|
@@ -0,0 +1,472 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# Copyright (c) 2026 Lark Technologies Pte. Ltd.
|
|
3
|
+
# SPDX-License-Identifier: MIT
|
|
4
|
+
"""Check whether Lark Sheet charts have obvious placement problems.
|
|
5
|
+
|
|
6
|
+
The single required argument is a spreadsheet URL or spreadsheet token. By
|
|
7
|
+
default every worksheet is checked; pass --worksheet-id to restrict the check
|
|
8
|
+
to one worksheet reference_id.
|
|
9
|
+
|
|
10
|
+
Exit codes:
|
|
11
|
+
0: check completed and no layout issue was found
|
|
12
|
+
1: the check could not be completed (CLI/read/response error)
|
|
13
|
+
2: check completed and at least one layout issue was found
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
import argparse
|
|
19
|
+
import json
|
|
20
|
+
from typing import Any
|
|
21
|
+
|
|
22
|
+
from lark_sheet_read_cli import (
|
|
23
|
+
LarkCliError,
|
|
24
|
+
emit_error,
|
|
25
|
+
envelope_data,
|
|
26
|
+
resolve_target_sheets,
|
|
27
|
+
run_sheets,
|
|
28
|
+
sheet_identifier,
|
|
29
|
+
sheet_title,
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
ACTION = "chart_layout_check"
|
|
33
|
+
DEFAULT_COLUMN_WIDTH = 105.0
|
|
34
|
+
DEFAULT_ROW_HEIGHT = 27.0
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def column_to_index(column: str) -> int:
|
|
38
|
+
value = 0
|
|
39
|
+
text = str(column).strip().upper()
|
|
40
|
+
if not text or not text.isalpha():
|
|
41
|
+
raise ValueError(f"Invalid column: {column!r}")
|
|
42
|
+
for char in text:
|
|
43
|
+
value = value * 26 + ord(char) - ord("A") + 1
|
|
44
|
+
return value - 1
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def index_to_column(index: int) -> str:
|
|
48
|
+
if index < 0:
|
|
49
|
+
raise ValueError(f"Invalid column index: {index}")
|
|
50
|
+
chars: list[str] = []
|
|
51
|
+
value = index + 1
|
|
52
|
+
while value:
|
|
53
|
+
value, remainder = divmod(value - 1, 26)
|
|
54
|
+
chars.append(chr(ord("A") + remainder))
|
|
55
|
+
return "".join(reversed(chars))
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _span_bounds(span: str, *, columns: bool) -> tuple[int, int]:
|
|
59
|
+
start, separator, end = str(span).partition(":")
|
|
60
|
+
end = end if separator else start
|
|
61
|
+
if columns:
|
|
62
|
+
return column_to_index(start), column_to_index(end)
|
|
63
|
+
return int(start) - 1, int(end) - 1
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def _size_edges(
|
|
67
|
+
groups: Any,
|
|
68
|
+
*,
|
|
69
|
+
count: int,
|
|
70
|
+
span_key: str,
|
|
71
|
+
size_key: str,
|
|
72
|
+
columns: bool,
|
|
73
|
+
default_size: float,
|
|
74
|
+
) -> tuple[list[float], bool]:
|
|
75
|
+
sizes: list[float | None] = [None] * count
|
|
76
|
+
if isinstance(groups, list):
|
|
77
|
+
for group in groups:
|
|
78
|
+
if not isinstance(group, dict) or group.get(span_key) is None:
|
|
79
|
+
continue
|
|
80
|
+
start, end = _span_bounds(str(group[span_key]), columns=columns)
|
|
81
|
+
size = float(group.get(size_key, default_size))
|
|
82
|
+
for index in range(max(0, start), min(count - 1, end) + 1):
|
|
83
|
+
sizes[index] = max(0.0, size)
|
|
84
|
+
|
|
85
|
+
used_default = any(size is None for size in sizes)
|
|
86
|
+
resolved = [default_size if size is None else size for size in sizes]
|
|
87
|
+
edges = [0.0]
|
|
88
|
+
for size in resolved:
|
|
89
|
+
edges.append(edges[-1] + size)
|
|
90
|
+
return edges, used_default
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def build_layout(
|
|
94
|
+
structure: dict[str, Any], row_count: int, column_count: int
|
|
95
|
+
) -> tuple[list[float], list[float], list[str]]:
|
|
96
|
+
row_groups = structure.get("row_heights")
|
|
97
|
+
column_groups = structure.get("col_widths", structure.get("column_widths"))
|
|
98
|
+
row_edges, row_defaulted = _size_edges(
|
|
99
|
+
row_groups,
|
|
100
|
+
count=row_count,
|
|
101
|
+
span_key="rows",
|
|
102
|
+
size_key="height",
|
|
103
|
+
columns=False,
|
|
104
|
+
default_size=DEFAULT_ROW_HEIGHT,
|
|
105
|
+
)
|
|
106
|
+
column_edges, column_defaulted = _size_edges(
|
|
107
|
+
column_groups,
|
|
108
|
+
count=column_count,
|
|
109
|
+
span_key="cols",
|
|
110
|
+
size_key="width",
|
|
111
|
+
columns=True,
|
|
112
|
+
default_size=DEFAULT_COLUMN_WIDTH,
|
|
113
|
+
)
|
|
114
|
+
warnings: list[str] = []
|
|
115
|
+
if row_defaulted:
|
|
116
|
+
warnings.append("部分行缺少高度信息,按 27 px 估算")
|
|
117
|
+
if column_defaulted:
|
|
118
|
+
warnings.append("部分列缺少宽度信息,按 105 px 估算")
|
|
119
|
+
return row_edges, column_edges, warnings
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def _first_dict(value: Any) -> dict[str, Any] | None:
|
|
123
|
+
if isinstance(value, dict):
|
|
124
|
+
return value
|
|
125
|
+
if isinstance(value, list):
|
|
126
|
+
return next((item for item in value if isinstance(item, dict)), None)
|
|
127
|
+
return None
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def extract_sheet_structure(data: dict[str, Any]) -> dict[str, Any]:
|
|
131
|
+
sheet = _first_dict(data.get("sheets")) or _first_dict(data.get("sheet"))
|
|
132
|
+
return sheet or data
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def extract_charts(data: dict[str, Any], sheet_id: str, title: str) -> list[dict[str, Any]]:
|
|
136
|
+
sheets = data.get("sheets")
|
|
137
|
+
if isinstance(sheets, list):
|
|
138
|
+
for sheet in sheets:
|
|
139
|
+
if not isinstance(sheet, dict):
|
|
140
|
+
continue
|
|
141
|
+
if sheet_identifier(sheet) == sheet_id or sheet_title(sheet) == title:
|
|
142
|
+
charts = sheet.get("charts")
|
|
143
|
+
return [chart for chart in charts if isinstance(chart, dict)] if isinstance(charts, list) else []
|
|
144
|
+
charts = data.get("charts")
|
|
145
|
+
return [chart for chart in charts if isinstance(chart, dict)] if isinstance(charts, list) else []
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def chart_rectangle(
|
|
149
|
+
chart: dict[str, Any], row_edges: list[float], column_edges: list[float]
|
|
150
|
+
) -> dict[str, Any]:
|
|
151
|
+
details = chart.get("details") if isinstance(chart.get("details"), dict) else chart
|
|
152
|
+
position = details.get("position") if isinstance(details.get("position"), dict) else {}
|
|
153
|
+
offset = details.get("offset") if isinstance(details.get("offset"), dict) else {}
|
|
154
|
+
size = details.get("size") if isinstance(details.get("size"), dict) else {}
|
|
155
|
+
|
|
156
|
+
row = int(position["row"])
|
|
157
|
+
column = column_to_index(str(position["col"]))
|
|
158
|
+
if row < 0 or column < 0 or row >= len(row_edges) - 1 or column >= len(column_edges) - 1:
|
|
159
|
+
raise ValueError(f"anchor outside sheet: {position!r}")
|
|
160
|
+
|
|
161
|
+
width = float(size["width"])
|
|
162
|
+
height = float(size["height"])
|
|
163
|
+
if width <= 0 or height <= 0:
|
|
164
|
+
raise ValueError(f"invalid chart size: {size!r}")
|
|
165
|
+
|
|
166
|
+
left = column_edges[column] + float(offset.get("col_offset", 0) or 0)
|
|
167
|
+
top = row_edges[row] + float(offset.get("row_offset", 0) or 0)
|
|
168
|
+
return {
|
|
169
|
+
"chart_id": str(chart.get("chart_id") or chart.get("id") or ""),
|
|
170
|
+
"anchor_cell": f"{index_to_column(column)}{row + 1}",
|
|
171
|
+
"left": left,
|
|
172
|
+
"top": top,
|
|
173
|
+
"right": left + width,
|
|
174
|
+
"bottom": top + height,
|
|
175
|
+
"width": width,
|
|
176
|
+
"height": height,
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
def intersection(first: dict[str, Any], second: dict[str, Any]) -> dict[str, float] | None:
|
|
181
|
+
left = max(float(first["left"]), float(second["left"]))
|
|
182
|
+
top = max(float(first["top"]), float(second["top"]))
|
|
183
|
+
right = min(float(first["right"]), float(second["right"]))
|
|
184
|
+
bottom = min(float(first["bottom"]), float(second["bottom"]))
|
|
185
|
+
if right <= left or bottom <= top:
|
|
186
|
+
return None
|
|
187
|
+
return {
|
|
188
|
+
"width": round(right - left, 2),
|
|
189
|
+
"height": round(bottom - top, 2),
|
|
190
|
+
"area": round((right - left) * (bottom - top), 2),
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def chart_context(rectangle: dict[str, Any]) -> dict[str, Any]:
|
|
195
|
+
return {
|
|
196
|
+
"chart_id": rectangle["chart_id"],
|
|
197
|
+
"anchor_cell": rectangle["anchor_cell"],
|
|
198
|
+
"rectangle_px": {
|
|
199
|
+
"left": round(rectangle["left"], 2),
|
|
200
|
+
"top": round(rectangle["top"], 2),
|
|
201
|
+
"right": round(rectangle["right"], 2),
|
|
202
|
+
"bottom": round(rectangle["bottom"], 2),
|
|
203
|
+
"width": round(rectangle["width"], 2),
|
|
204
|
+
"height": round(rectangle["height"], 2),
|
|
205
|
+
},
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
def _covered_indexes(edges: list[float], start: float, end: float) -> list[int]:
|
|
210
|
+
return [
|
|
211
|
+
index
|
|
212
|
+
for index in range(len(edges) - 1)
|
|
213
|
+
if edges[index + 1] > start and edges[index] < end
|
|
214
|
+
]
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
def rectangle_cell_range(
|
|
218
|
+
rectangle: dict[str, Any], row_edges: list[float], column_edges: list[float]
|
|
219
|
+
) -> str | None:
|
|
220
|
+
rows = _covered_indexes(row_edges, max(0.0, rectangle["top"]), rectangle["bottom"])
|
|
221
|
+
columns = _covered_indexes(column_edges, max(0.0, rectangle["left"]), rectangle["right"])
|
|
222
|
+
if not rows or not columns:
|
|
223
|
+
return None
|
|
224
|
+
return f"{index_to_column(columns[0])}{rows[0] + 1}:{index_to_column(columns[-1])}{rows[-1] + 1}"
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
def _has_content(cell: Any) -> bool:
|
|
228
|
+
if not isinstance(cell, dict):
|
|
229
|
+
return False
|
|
230
|
+
for key in ("value", "formula", "note"):
|
|
231
|
+
value = cell.get(key)
|
|
232
|
+
if value not in (None, ""):
|
|
233
|
+
return True
|
|
234
|
+
return bool(cell.get("rich_text") or cell.get("multiple_values"))
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
def non_empty_cells(data: dict[str, Any], sample_limit: int) -> tuple[int, list[str], bool]:
|
|
238
|
+
count = 0
|
|
239
|
+
samples: list[str] = []
|
|
240
|
+
truncated = bool(data.get("has_more"))
|
|
241
|
+
ranges = data.get("ranges")
|
|
242
|
+
if not isinstance(ranges, list):
|
|
243
|
+
return 0, [], truncated
|
|
244
|
+
for result_range in ranges:
|
|
245
|
+
if not isinstance(result_range, dict):
|
|
246
|
+
continue
|
|
247
|
+
truncated = truncated or bool(result_range.get("truncated"))
|
|
248
|
+
cells = result_range.get("cells")
|
|
249
|
+
rows = result_range.get("row_indices")
|
|
250
|
+
columns = result_range.get("col_indices")
|
|
251
|
+
if not isinstance(cells, list):
|
|
252
|
+
continue
|
|
253
|
+
for row_offset, row in enumerate(cells):
|
|
254
|
+
if not isinstance(row, list):
|
|
255
|
+
continue
|
|
256
|
+
row_number = rows[row_offset] if isinstance(rows, list) and row_offset < len(rows) else row_offset + 1
|
|
257
|
+
for column_offset, cell in enumerate(row):
|
|
258
|
+
if not _has_content(cell):
|
|
259
|
+
continue
|
|
260
|
+
count += 1
|
|
261
|
+
if len(samples) < sample_limit:
|
|
262
|
+
column = columns[column_offset] if isinstance(columns, list) and column_offset < len(columns) else index_to_column(column_offset)
|
|
263
|
+
samples.append(f"{column}{row_number}")
|
|
264
|
+
return count, samples, truncated
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
def _locator(target: str) -> dict[str, str]:
|
|
268
|
+
return {"url": target} if target.startswith(("http://", "https://")) else {"spreadsheet_token": target}
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
def _sheet_counts(sheet: dict[str, Any]) -> tuple[int, int]:
|
|
272
|
+
row_count = int(sheet.get("row_count") or sheet.get("rowCount") or 0)
|
|
273
|
+
column_count = int(sheet.get("column_count") or sheet.get("columnCount") or 0)
|
|
274
|
+
if row_count <= 0 or column_count <= 0:
|
|
275
|
+
raise LarkCliError(f"Missing row_count/column_count for sheet {sheet_title(sheet)!r}")
|
|
276
|
+
return row_count, column_count
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
def check_sheet(
|
|
280
|
+
locator: dict[str, str], sheet: dict[str, Any], *, timeout: int, sample_limit: int
|
|
281
|
+
) -> dict[str, Any]:
|
|
282
|
+
sheet_id = sheet_identifier(sheet)
|
|
283
|
+
title = sheet_title(sheet)
|
|
284
|
+
row_count, column_count = _sheet_counts(sheet)
|
|
285
|
+
if not sheet_id:
|
|
286
|
+
raise LarkCliError(f"Missing sheet_id for sheet {title!r}")
|
|
287
|
+
|
|
288
|
+
structure_data = envelope_data(
|
|
289
|
+
run_sheets(
|
|
290
|
+
"+sheet-info",
|
|
291
|
+
**locator,
|
|
292
|
+
sheet_id=sheet_id,
|
|
293
|
+
flags={"include": "row_heights,col_widths"},
|
|
294
|
+
timeout=timeout,
|
|
295
|
+
)
|
|
296
|
+
)
|
|
297
|
+
row_edges, column_edges, warnings = build_layout(
|
|
298
|
+
extract_sheet_structure(structure_data), row_count, column_count
|
|
299
|
+
)
|
|
300
|
+
chart_data = envelope_data(
|
|
301
|
+
run_sheets("+chart-list", **locator, sheet_id=sheet_id, timeout=timeout)
|
|
302
|
+
)
|
|
303
|
+
charts = extract_charts(chart_data, sheet_id, title)
|
|
304
|
+
|
|
305
|
+
rectangles: list[dict[str, Any]] = []
|
|
306
|
+
unverifiable: list[dict[str, str]] = []
|
|
307
|
+
expected_chart_count = sheet.get("chart_count")
|
|
308
|
+
if expected_chart_count is not None and int(expected_chart_count) != len(charts):
|
|
309
|
+
unverifiable.append(
|
|
310
|
+
{
|
|
311
|
+
"chart_id": "",
|
|
312
|
+
"reason": (
|
|
313
|
+
f"chart-list returned {len(charts)} charts, "
|
|
314
|
+
f"but workbook-info reported {int(expected_chart_count)}"
|
|
315
|
+
),
|
|
316
|
+
}
|
|
317
|
+
)
|
|
318
|
+
for chart in charts:
|
|
319
|
+
chart_id = str(chart.get("chart_id") or chart.get("id") or "")
|
|
320
|
+
if not chart_id:
|
|
321
|
+
unverifiable.append({"chart_id": "", "reason": "chart is missing chart_id"})
|
|
322
|
+
continue
|
|
323
|
+
try:
|
|
324
|
+
rectangles.append(chart_rectangle(chart, row_edges, column_edges))
|
|
325
|
+
except (KeyError, TypeError, ValueError) as exc:
|
|
326
|
+
unverifiable.append({"chart_id": chart_id, "reason": str(exc)})
|
|
327
|
+
|
|
328
|
+
overlaps: list[dict[str, Any]] = []
|
|
329
|
+
for index, first in enumerate(rectangles):
|
|
330
|
+
for second in rectangles[index + 1 :]:
|
|
331
|
+
overlap = intersection(first, second)
|
|
332
|
+
if overlap:
|
|
333
|
+
overlaps.append(
|
|
334
|
+
{
|
|
335
|
+
"chart_ids": [first["chart_id"], second["chart_id"]],
|
|
336
|
+
"charts": [chart_context(first), chart_context(second)],
|
|
337
|
+
"intersection": overlap,
|
|
338
|
+
}
|
|
339
|
+
)
|
|
340
|
+
|
|
341
|
+
sheet_width = column_edges[-1]
|
|
342
|
+
sheet_height = row_edges[-1]
|
|
343
|
+
out_of_bounds: list[dict[str, Any]] = []
|
|
344
|
+
content_overlaps: list[dict[str, Any]] = []
|
|
345
|
+
for rectangle in rectangles:
|
|
346
|
+
overflow = {
|
|
347
|
+
"left": round(max(0.0, -rectangle["left"]), 2),
|
|
348
|
+
"top": round(max(0.0, -rectangle["top"]), 2),
|
|
349
|
+
"right": round(max(0.0, rectangle["right"] - sheet_width), 2),
|
|
350
|
+
"bottom": round(max(0.0, rectangle["bottom"] - sheet_height), 2),
|
|
351
|
+
}
|
|
352
|
+
if any(overflow.values()):
|
|
353
|
+
out_of_bounds.append({**chart_context(rectangle), "overflow_px": overflow})
|
|
354
|
+
|
|
355
|
+
covered_range = rectangle_cell_range(rectangle, row_edges, column_edges)
|
|
356
|
+
if not covered_range:
|
|
357
|
+
continue
|
|
358
|
+
cells_data = envelope_data(
|
|
359
|
+
run_sheets(
|
|
360
|
+
"+cells-get",
|
|
361
|
+
**locator,
|
|
362
|
+
sheet_id=sheet_id,
|
|
363
|
+
flags={"range": covered_range, "include": "value,formula,comment"},
|
|
364
|
+
timeout=timeout,
|
|
365
|
+
)
|
|
366
|
+
)
|
|
367
|
+
count, samples, truncated = non_empty_cells(cells_data, sample_limit)
|
|
368
|
+
if truncated:
|
|
369
|
+
unverifiable.append(
|
|
370
|
+
{"chart_id": rectangle["chart_id"], "reason": f"cells-get truncated for {covered_range}"}
|
|
371
|
+
)
|
|
372
|
+
if count:
|
|
373
|
+
content_overlaps.append(
|
|
374
|
+
{
|
|
375
|
+
**chart_context(rectangle),
|
|
376
|
+
"covered_range": covered_range,
|
|
377
|
+
"non_empty_cell_count": count,
|
|
378
|
+
"sample_cells": samples,
|
|
379
|
+
}
|
|
380
|
+
)
|
|
381
|
+
|
|
382
|
+
issue_count = len(overlaps) + len(out_of_bounds) + len(content_overlaps)
|
|
383
|
+
return {
|
|
384
|
+
"sheet_id": sheet_id,
|
|
385
|
+
"sheet_name": title,
|
|
386
|
+
"chart_count": len(charts),
|
|
387
|
+
"sheet_size_px": {"width": round(sheet_width, 2), "height": round(sheet_height, 2)},
|
|
388
|
+
"chart_overlaps": overlaps,
|
|
389
|
+
"cell_content_overlaps": content_overlaps,
|
|
390
|
+
"out_of_visible_range": out_of_bounds,
|
|
391
|
+
"unverifiable_charts": unverifiable,
|
|
392
|
+
"issue_count": issue_count,
|
|
393
|
+
"unverifiable_count": len(unverifiable),
|
|
394
|
+
"warnings": warnings,
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
|
|
398
|
+
def parse_args() -> argparse.Namespace:
|
|
399
|
+
parser = argparse.ArgumentParser(
|
|
400
|
+
description="Check chart overlap, covered cell content, and worksheet boundary overflow."
|
|
401
|
+
)
|
|
402
|
+
parser.add_argument("sheet_id", help="Spreadsheet URL or spreadsheet token")
|
|
403
|
+
parser.add_argument("--worksheet-id", help="Only check this worksheet reference_id")
|
|
404
|
+
parser.add_argument("--timeout", type=int, default=60)
|
|
405
|
+
parser.add_argument("--sample-limit", type=int, default=10)
|
|
406
|
+
return parser.parse_args()
|
|
407
|
+
|
|
408
|
+
|
|
409
|
+
def success_envelope(results: list[dict[str, Any]]) -> dict[str, Any]:
|
|
410
|
+
issue_count = sum(result["issue_count"] for result in results)
|
|
411
|
+
unverifiable_count = sum(result["unverifiable_count"] for result in results)
|
|
412
|
+
warnings = [
|
|
413
|
+
f"{result['sheet_name'] or result['sheet_id']}: {warning}"
|
|
414
|
+
for result in results
|
|
415
|
+
for warning in result["warnings"]
|
|
416
|
+
]
|
|
417
|
+
return {
|
|
418
|
+
"ok": True,
|
|
419
|
+
"engine": "lark",
|
|
420
|
+
"action": ACTION,
|
|
421
|
+
"data": {
|
|
422
|
+
"passed": issue_count == 0 and unverifiable_count == 0,
|
|
423
|
+
"scope_note": "out_of_visible_range checks worksheet drawable bounds, not a device-specific browser viewport",
|
|
424
|
+
"summary": {
|
|
425
|
+
"worksheet_count": len(results),
|
|
426
|
+
"chart_count": sum(result["chart_count"] for result in results),
|
|
427
|
+
"issue_count": issue_count,
|
|
428
|
+
"unverifiable_count": unverifiable_count,
|
|
429
|
+
},
|
|
430
|
+
"sheets": results,
|
|
431
|
+
},
|
|
432
|
+
"warnings": warnings,
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
|
|
436
|
+
def report_exit_code(report: dict[str, Any]) -> int:
|
|
437
|
+
if report["data"]["passed"]:
|
|
438
|
+
return 0
|
|
439
|
+
if report["data"]["summary"]["issue_count"] > 0:
|
|
440
|
+
return 2
|
|
441
|
+
return 1
|
|
442
|
+
|
|
443
|
+
|
|
444
|
+
def main() -> None:
|
|
445
|
+
args = parse_args()
|
|
446
|
+
locator = _locator(args.sheet_id)
|
|
447
|
+
try:
|
|
448
|
+
workbook_data = envelope_data(
|
|
449
|
+
run_sheets("+workbook-info", **locator, timeout=args.timeout)
|
|
450
|
+
)
|
|
451
|
+
sheets = resolve_target_sheets(workbook_data, sheet_id=args.worksheet_id)
|
|
452
|
+
if not args.worksheet_id:
|
|
453
|
+
sheets = [sheet for sheet in sheets if not bool(sheet.get("is_hidden"))]
|
|
454
|
+
if not sheets:
|
|
455
|
+
raise LarkCliError("No visible worksheet matched")
|
|
456
|
+
results = [
|
|
457
|
+
check_sheet(locator, sheet, timeout=args.timeout, sample_limit=args.sample_limit)
|
|
458
|
+
for sheet in sheets
|
|
459
|
+
]
|
|
460
|
+
except (LarkCliError, KeyError, TypeError, ValueError) as exc:
|
|
461
|
+
emit_error(ACTION, str(exc))
|
|
462
|
+
raise SystemExit(1) from exc
|
|
463
|
+
|
|
464
|
+
report = success_envelope(results)
|
|
465
|
+
print(json.dumps(report, ensure_ascii=False, indent=2))
|
|
466
|
+
exit_code = report_exit_code(report)
|
|
467
|
+
if exit_code:
|
|
468
|
+
raise SystemExit(exit_code)
|
|
469
|
+
|
|
470
|
+
|
|
471
|
+
if __name__ == "__main__":
|
|
472
|
+
main()
|
|
@@ -88,6 +88,7 @@ metadata:
|
|
|
88
88
|
| 读取或分析已有 PPT | 解析 slides/wiki token,用 shortcut 回读全文 XML 或读取单页 XML,保存 `xml_presentation_id`、`slide_id`、`revision_id` | `slides +xml-get`、`xml_presentation.slide.get`、`cli/lark-slides-xml-presentations-get.md` |
|
|
89
89
|
| 查看或回滚历史版本 | 先用 `+history-list` 找 `history_version_id`,再 `+history-revert`,必要时 `+history-revert-status` 轮询 | [`cli/lark-slides-history.md`](references/cli/lark-slides-history.md) |
|
|
90
90
|
| 获取幻灯片页面截图 | 按页码用 `--slide-number`,按 ID 用 `--slide-id`;单张用 `--output`,批量或全量用 `--output-dir`,每批最多 10 页串行执行;截图目录复用同一任务的 deck/task 标识,后续读取返回的实际路径 | `slides +screenshot`、`cli/lark-slides-screenshot.md` |
|
|
91
|
+
| 下载图片 | `--output` 选填;传入时指定单个文件路径,未传时自动保存到默认目录 `.lark-slides/media`,并按响应文件名/类型生成路径;调用后读取返回的 `path`,不要猜测文件名;直连被拒时自动回退到源文件预览 | `slides +media-download --file-token <file_token>` |
|
|
91
92
|
| 上传或使用图片 | 先上传为 `file_token`,禁止直接写 http(s) 外链 | `slides +media-upload`、`cli/lark-slides-media-upload.md`,或 `+create --slides` 的 XML 里写 `<img src="@./path">` 占位符 |
|
|
92
93
|
| 绘制图表 | 原生图表(柱状、条形、折线、面积、饼(环)、雷达、组合图)用 `<chart>`,其他(漏斗图、金字塔图、象限图、矩阵图等)用 `<shape>` + `<line>` 模拟 | `xml/xml-schema-quick-ref.md`、`xml/slides_chart_demo.xml` |
|
|
93
94
|
| 绘制表格 | 优先用 `rect` 和 `text` 模拟,其他用 `<table>` | `xml/xml-schema-quick-ref.md` |
|
|
@@ -289,6 +290,7 @@ Shortcut 是对常用操作的高级封装(`lark-cli slides +<verb> [flags]`
|
|
|
289
290
|
| [`+xml-get`](references/cli/lark-slides-xml-presentations-get.md) | 读取全文 XML,用 `--presentation` 指定演示文稿的 `xml_presentation_id`,用 `--output` 把 XML 存到本地文件(必须是 CWD 内的相对路径,如 `.lark-slides/plan/<deck>/readback.xml`) |
|
|
290
291
|
| [`+screenshot`](references/cli/lark-slides-screenshot.md) | 把幻灯片页面截图保存为本地图片;用 `--slide-number` 指定页码(从 1 开始,多页重复传入)或用 `--slide-id` 指定页面;单张用 `--output .lark-slides/screenshots/<deck-or-task-id>/page-01`,批量用 `--output-dir .lark-slides/screenshots/<deck-or-task-id>`(一次最多 10 页);后续必须读取返回的 `output` / `screenshots[].path` |
|
|
291
292
|
| [`+media-upload`](references/cli/lark-slides-media-upload.md) | 上传本地图片到指定演示文稿,返回 `file_token`(用作 `<img src="...">`),最大 20 MB |
|
|
293
|
+
| `+media-download` | 根据 Slides 图片 `file_token` 下载本地图片;`--output` 选填,未传时使用 `--output-dir` 默认值 `.lark-slides/media` 并自动生成文件名;调用后使用返回的 `path`,不要猜测实际路径;直连下载无权限时自动回退到源文件预览 |
|
|
292
294
|
| [`+replace-slide`](references/cli/lark-slides-replace-slide.md) | 对已有幻灯片页面进行块级替换/插入(`block_replace` / `block_insert`),自动注入 id 和 `<content/>`,不改变页序 |
|
|
293
295
|
| [`+update-slide`](references/cli/lark-slides-update-slide.md) | 把一整页 XML 交给已有页面,页面变成 `--content` 描述的样子;能一次改样式/插入/删除/备注/背景,`slide_id` 和页序不变。**没写进 `--content` 的元素会被删除** |
|
|
294
296
|
|
|
@@ -11,20 +11,20 @@
|
|
|
11
11
|
```bash
|
|
12
12
|
# 追加到末尾(XML 直接作为参数)
|
|
13
13
|
lark-cli slides +add-slide --as user \
|
|
14
|
-
--presentation "$
|
|
14
|
+
--presentation "$PRES_ID" \
|
|
15
15
|
--slide '<slide xmlns="https://www.larkoffice.com/sml/2.0"><data></data></slide>'
|
|
16
16
|
|
|
17
17
|
# XML 从文件读(推荐:避免 shell 转义和长参数截断)
|
|
18
18
|
lark-cli slides +add-slide --as user \
|
|
19
|
-
--presentation "$
|
|
19
|
+
--presentation "$PRES_ID" \
|
|
20
20
|
--slide @page3.xml
|
|
21
21
|
|
|
22
22
|
# XML 从 stdin 读
|
|
23
|
-
cat page3.xml | lark-cli slides +add-slide --as user --presentation "$
|
|
23
|
+
cat page3.xml | lark-cli slides +add-slide --as user --presentation "$PRES_ID" --slide -
|
|
24
24
|
|
|
25
25
|
# 插到某页之前
|
|
26
26
|
lark-cli slides +add-slide --as user \
|
|
27
|
-
--presentation "$
|
|
27
|
+
--presentation "$PRES_ID" \
|
|
28
28
|
--slide @cover.xml \
|
|
29
29
|
--before-slide-id "$SID"
|
|
30
30
|
|
|
@@ -34,7 +34,7 @@ lark-cli slides +add-slide --as user \
|
|
|
34
34
|
--slide @page3.xml
|
|
35
35
|
|
|
36
36
|
# 预览请求,不实际写入
|
|
37
|
-
lark-cli slides +add-slide --presentation "$
|
|
37
|
+
lark-cli slides +add-slide --presentation "$PRES_ID" --slide @page3.xml --dry-run
|
|
38
38
|
```
|
|
39
39
|
|
|
40
40
|
## 参数
|
|
@@ -57,7 +57,7 @@ XML 里写 `<img src="@./chart.png" .../>`,CLI 会:先把每个不重复的
|
|
|
57
57
|
|
|
58
58
|
```bash
|
|
59
59
|
lark-cli slides +add-slide --as user \
|
|
60
|
-
--presentation "$
|
|
60
|
+
--presentation "$PRES_ID" \
|
|
61
61
|
--slide '<slide xmlns="https://www.larkoffice.com/sml/2.0"><data><img src="@./chart.png" topLeftX="100" topLeftY="100" width="320" height="180"/></data></slide>'
|
|
62
62
|
```
|
|
63
63
|
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
```bash
|
|
12
12
|
# 直接传 xml_presentation_id
|
|
13
13
|
lark-cli slides +delete-slide --as user \
|
|
14
|
-
--presentation "$
|
|
14
|
+
--presentation "$PRES_ID" \
|
|
15
15
|
--slide-id "$SID"
|
|
16
16
|
|
|
17
17
|
# slides URL / wiki URL 都可以(wiki 会自动解析并校验 obj_type=slides)
|
|
@@ -20,7 +20,7 @@ lark-cli slides +delete-slide --as user \
|
|
|
20
20
|
--slide-id "$SID"
|
|
21
21
|
|
|
22
22
|
# 删之前先确认打到哪份 PPT、哪一页
|
|
23
|
-
lark-cli slides +delete-slide --presentation "$
|
|
23
|
+
lark-cli slides +delete-slide --presentation "$PRES_ID" --slide-id "$SID" --dry-run
|
|
24
24
|
```
|
|
25
25
|
|
|
26
26
|
## 参数
|
|
@@ -48,7 +48,7 @@ lark-cli slides +delete-slide --presentation "$PID" --slide-id "$SID" --dry-run
|
|
|
48
48
|
`slide_id` 是服务端短 ID,**不能从 XML 里推导**。两个来源:
|
|
49
49
|
|
|
50
50
|
1. `+create` / `+add-slide` 的返回值里存下来;
|
|
51
|
-
2. 事后回读:`slides +xml-get --presentation "$
|
|
51
|
+
2. 事后回读:`slides +xml-get --presentation "$PRES_ID" --output .lark-slides/plan/<deck>/readback.xml`。
|
|
52
52
|
|
|
53
53
|
删错页的代价高于多跑一次回读 —— 不确定就先回读 + `+screenshot` 看一眼再删。
|
|
54
54
|
|
|
@@ -28,9 +28,9 @@ lark-cli slides +replace-slide --as user \
|
|
|
28
28
|
|
|
29
29
|
# 大 --parts 走文件或 stdin(auto-gen 命令不支持 @file,但 shortcut 支持)
|
|
30
30
|
lark-cli slides +replace-slide --as user \
|
|
31
|
-
--presentation $
|
|
31
|
+
--presentation $PRES_ID --slide-id $SID --parts @parts.json
|
|
32
32
|
cat parts.json | lark-cli slides +replace-slide --as user \
|
|
33
|
-
--presentation $
|
|
33
|
+
--presentation $PRES_ID --slide-id $SID --parts -
|
|
34
34
|
|
|
35
35
|
# wiki URL 直接传(CLI 自动 get_node → 拿真实 xml_presentation_id)
|
|
36
36
|
lark-cli slides +replace-slide --as user \
|
|
@@ -39,7 +39,7 @@ lark-cli slides +replace-slide --as user \
|
|
|
39
39
|
|
|
40
40
|
# 预览(不实际调用)
|
|
41
41
|
lark-cli slides +replace-slide --as user \
|
|
42
|
-
--presentation $
|
|
42
|
+
--presentation $PRES_ID --slide-id $SID --parts "$PARTS" --dry-run
|
|
43
43
|
```
|
|
44
44
|
|
|
45
45
|
## 参数
|
|
@@ -180,16 +180,16 @@ lark-cli slides +replace-slide --as user \
|
|
|
180
180
|
### 给已有页加图(典型场景)
|
|
181
181
|
|
|
182
182
|
```bash
|
|
183
|
-
|
|
183
|
+
PRES_ID=xxx
|
|
184
184
|
SID=yyy
|
|
185
185
|
|
|
186
186
|
# 1) 上传图片
|
|
187
187
|
TOKEN=$(lark-cli slides +media-upload --as user \
|
|
188
|
-
--file ./pic.png --presentation "$
|
|
188
|
+
--file ./pic.png --presentation "$PRES_ID" --jq '.data.file_token')
|
|
189
189
|
|
|
190
190
|
# 2) block_insert 到页末
|
|
191
191
|
lark-cli slides +replace-slide --as user \
|
|
192
|
-
--presentation "$
|
|
192
|
+
--presentation "$PRES_ID" --slide-id "$SID" \
|
|
193
193
|
--parts "$(jq -n --arg token "$TOKEN" \
|
|
194
194
|
'[{action:"block_insert",insertion:("<img src=\""+$token+"\" topLeftX=\"500\" topLeftY=\"100\" width=\"200\" height=\"150\"/>")}]')"
|
|
195
195
|
```
|
|
@@ -199,11 +199,11 @@ lark-cli slides +replace-slide --as user \
|
|
|
199
199
|
```bash
|
|
200
200
|
# 先拿原页 XML,从里面找到标题块的 3 位 short id(如 bUn)
|
|
201
201
|
lark-cli slides xml_presentation.slide get --as user \
|
|
202
|
-
--params "{\"xml_presentation_id\":\"$
|
|
202
|
+
--params "{\"xml_presentation_id\":\"$PRES_ID\",\"slide_id\":\"$SID\"}"
|
|
203
203
|
|
|
204
204
|
# block_replace 换掉整个标题块(id 自动注入)
|
|
205
205
|
lark-cli slides +replace-slide --as user \
|
|
206
|
-
--presentation "$
|
|
206
|
+
--presentation "$PRES_ID" --slide-id "$SID" \
|
|
207
207
|
--parts '[{"action":"block_replace","block_id":"bUn","replacement":"<shape type=\"text\" topLeftX=\"80\" topLeftY=\"80\" width=\"800\" height=\"120\"><content textType=\"title\"><p>新标题</p></content></shape>"}]'
|
|
208
208
|
```
|
|
209
209
|
|
|
@@ -213,7 +213,7 @@ lark-cli slides +replace-slide --as user \
|
|
|
213
213
|
|
|
214
214
|
```bash
|
|
215
215
|
lark-cli slides +replace-slide --as user \
|
|
216
|
-
--presentation "$
|
|
216
|
+
--presentation "$PRES_ID" --slide-id "$SID" \
|
|
217
217
|
--parts '[
|
|
218
218
|
{"action":"block_replace","block_id":"bab","replacement":"<shape type=\"text\" topLeftX=\"80\" topLeftY=\"80\" width=\"800\" height=\"120\"><content textType=\"title\"><p>新标题</p></content></shape>"},
|
|
219
219
|
{"action":"block_insert","insertion":"<img src=\"<file_token>\" topLeftX=\"700\" topLeftY=\"400\" width=\"180\" height=\"100\"/>"}
|
|
@@ -225,12 +225,12 @@ lark-cli slides +replace-slide --as user \
|
|
|
225
225
|
```bash
|
|
226
226
|
# 读时记录 revision_id
|
|
227
227
|
REV=$(lark-cli slides xml_presentation.slide get --as user \
|
|
228
|
-
--params "{\"xml_presentation_id\":\"$
|
|
228
|
+
--params "{\"xml_presentation_id\":\"$PRES_ID\",\"slide_id\":\"$SID\"}" \
|
|
229
229
|
--jq '.data.revision_id')
|
|
230
230
|
|
|
231
231
|
# 写时传 --revision-id;传不存在的版本号(超过当前 revision)返回 3350002
|
|
232
232
|
lark-cli slides +replace-slide --as user \
|
|
233
|
-
--presentation "$
|
|
233
|
+
--presentation "$PRES_ID" --slide-id "$SID" --revision-id "$REV" \
|
|
234
234
|
--parts "$PARTS"
|
|
235
235
|
```
|
|
236
236
|
|
|
@@ -98,7 +98,7 @@ lark-cli slides xml_presentation.slide get --as user --params '{
|
|
|
98
98
|
|
|
99
99
|
```bash
|
|
100
100
|
lark-cli slides xml_presentation.slide get --as user \
|
|
101
|
-
--params "{\"xml_presentation_id\":\"$
|
|
101
|
+
--params "{\"xml_presentation_id\":\"$PRES_ID\",\"slide_id\":\"$SID\"}" \
|
|
102
102
|
--jq '.data.slide.content' | grep -oE 'id="[^"]+"' | sed 's/id="//;s/"//'
|
|
103
103
|
```
|
|
104
104
|
|
|
@@ -95,10 +95,10 @@ lark-cli slides xml_presentation.slide replace --as user --params '{
|
|
|
95
95
|
|
|
96
96
|
```bash
|
|
97
97
|
# 先拿 file_token
|
|
98
|
-
TOKEN=$(lark-cli slides +media-upload --file ./pic.png --presentation "$
|
|
98
|
+
TOKEN=$(lark-cli slides +media-upload --file ./pic.png --presentation "$PRES_ID" --as user --jq '.data.file_token')
|
|
99
99
|
|
|
100
100
|
lark-cli slides xml_presentation.slide replace --as user --params "{
|
|
101
|
-
\"xml_presentation_id\": \"$
|
|
101
|
+
\"xml_presentation_id\": \"$PRES_ID\",
|
|
102
102
|
\"slide_id\": \"$SID\"
|
|
103
103
|
}" --data "$(jq -n --arg token "$TOKEN" '{
|
|
104
104
|
parts: [
|