mirror of
https://github.com/larksuite/cli.git
synced 2026-07-09 18:34:03 +08:00
Compare commits
3 Commits
main
...
feat/mail-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cc56cc5665 | ||
|
|
c02fa92667 | ||
|
|
49f60d9239 |
@@ -94,7 +94,7 @@ func TestMailTriageTableHintRoutesSingleAndMultipleReads(t *testing.T) {
|
||||
registerTriageReadHintStubs(reg)
|
||||
|
||||
err := runMountedMailShortcut(t, MailTriage, []string{
|
||||
"+triage", "--max", "1",
|
||||
"+triage", "--format", "table", "--max", "1",
|
||||
}, f, stdout)
|
||||
if err != nil {
|
||||
t.Fatalf("triage returned error: %v", err)
|
||||
|
||||
@@ -55,7 +55,7 @@ var MailTriage = common.Shortcut{
|
||||
Scopes: []string{"mail:user_mailbox.message:readonly", "mail:user_mailbox.message.address:read", "mail:user_mailbox.message.subject:read", "mail:user_mailbox.message.body:read"},
|
||||
AuthTypes: []string{"user", "bot"},
|
||||
Flags: []common.Flag{
|
||||
{Name: "format", Default: "table", Enum: []string{"table", "json", "data"}, Desc: "output format: table | json | data (json/data output object with pagination fields)"},
|
||||
{Name: "format", Default: "json", Enum: []string{"table", "json", "data"}, Desc: "output format: table | json | data (json/data output object with pagination fields)"},
|
||||
{Name: "max", Type: "int", Default: "20", Desc: "maximum number of messages to fetch (1-400; auto-paginates internally)"},
|
||||
{Name: "page-size", Type: "int", Desc: "alias for --max"},
|
||||
{Name: "page-token", Desc: "pagination token from a previous response to fetch the next page"},
|
||||
|
||||
@@ -1655,6 +1655,39 @@ func TestMailTriageMissingMessageMetadataStillGetsMailboxID(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestMailTriageDefaultFormatIsJSON verifies that with no --format flag the
|
||||
// command defaults to json and prints the paginated object to stdout, not the
|
||||
// human table.
|
||||
func TestMailTriageDefaultFormatIsJSON(t *testing.T) {
|
||||
f, stdout, _, reg := mailShortcutTestFactory(t)
|
||||
defer reg.Verify(t)
|
||||
|
||||
registerMailTriageListStub(reg, "me", []string{"msg_ok"}, false, "")
|
||||
registerMailTriageBatchStub(reg, "me", []map[string]interface{}{
|
||||
mailTriageBatchMessage("msg_ok", "Present"),
|
||||
})
|
||||
|
||||
err := runMountedMailShortcut(t, MailTriage, []string{
|
||||
"+triage",
|
||||
"--filter", `{"folder_id":"INBOX"}`,
|
||||
}, f, stdout)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
data := decodeMailTriageJSONOutput(t, stdout) // fails to unmarshal if default were table
|
||||
messages := mailTriageMessagesFromOutput(t, data)
|
||||
if len(messages) != 1 {
|
||||
t.Fatalf("expected 1 message, got %d", len(messages))
|
||||
}
|
||||
if _, ok := data["count"]; !ok {
|
||||
t.Fatalf("default json output missing count field: %#v", data)
|
||||
}
|
||||
if _, ok := data["has_more"]; !ok {
|
||||
t.Fatalf("default json output missing has_more field: %#v", data)
|
||||
}
|
||||
}
|
||||
|
||||
// TestMailTriageTableOutputPreservesMailboxContext verifies public mailbox table hints.
|
||||
func TestMailTriageTableOutputPreservesMailboxContext(t *testing.T) {
|
||||
tests := []struct {
|
||||
@@ -1678,7 +1711,7 @@ func TestMailTriageTableOutputPreservesMailboxContext(t *testing.T) {
|
||||
mailTriageBatchMessage("msg_001", "Table message"),
|
||||
})
|
||||
|
||||
args := []string{"+triage", "--max", "1", "--filter", `{"folder_id":"INBOX"}`}
|
||||
args := []string{"+triage", "--format", "table", "--max", "1", "--filter", `{"folder_id":"INBOX"}`}
|
||||
if tt.mailbox != "me" {
|
||||
args = append(args, "--mailbox", tt.mailbox)
|
||||
}
|
||||
@@ -1719,6 +1752,7 @@ func TestMailTriageDefaultTableOutputPrintsSearchNoticeToStderr(t *testing.T) {
|
||||
|
||||
if err := runMountedMailShortcut(t, MailTriage, []string{
|
||||
"+triage",
|
||||
"--format", "table",
|
||||
"--query", strings.Repeat("q", 81),
|
||||
}, f, stdout); err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
|
||||
@@ -99,7 +99,7 @@ var MailWatch = common.Shortcut{
|
||||
Scopes: []string{"mail:event", "mail:user_mailbox.event.mail_address:read", "mail:user_mailbox:readonly", "mail:user_mailbox.message:readonly", "mail:user_mailbox.message.address:read", "mail:user_mailbox.message.subject:read", "mail:user_mailbox.message.body:read"},
|
||||
AuthTypes: []string{"user"},
|
||||
Flags: []common.Flag{
|
||||
{Name: "format", Default: "data", Enum: []string{"json", "data"}, Desc: "json: NDJSON stream with ok/data envelope; data: bare NDJSON stream"},
|
||||
{Name: "format", Default: "json", Enum: []string{"json", "data"}, Desc: "json: NDJSON stream with ok/data envelope; data: bare NDJSON stream"},
|
||||
{Name: "msg-format", Default: "metadata", Desc: "message payload mode: metadata(headers + meta, for triage/notification) | minimal(IDs and state only, no headers, for tracking read/folder changes) | plain_text_full(all metadata fields + full plain-text body) | event(raw WebSocket event, no API call, for debug) | full(full message including HTML body and attachments)"},
|
||||
{Name: "output-dir", Desc: "Write each message as a JSON file (always full payload, regardless of --msg-format)"},
|
||||
{Name: "mailbox", Default: "me", Desc: "email address (default: me)"},
|
||||
@@ -385,12 +385,7 @@ var MailWatch = common.Shortcut{
|
||||
}
|
||||
}
|
||||
|
||||
switch outFormat {
|
||||
case "json", "":
|
||||
output.PrintNdjson(out, output.Envelope{OK: true, Identity: string(runtime.As()), Data: outputData})
|
||||
case "data":
|
||||
output.PrintNdjson(out, outputData)
|
||||
}
|
||||
output.PrintNdjson(out, watchOutputValue(outFormat, string(runtime.As()), outputData))
|
||||
}
|
||||
|
||||
rawHandler := func(ctx context.Context, event *larkevent.EventReq) error {
|
||||
@@ -687,6 +682,17 @@ func minimalWatchMessage(message map[string]interface{}) map[string]interface{}
|
||||
return out
|
||||
}
|
||||
|
||||
// watchOutputValue selects the per-event value that +watch prints as NDJSON:
|
||||
// "data" emits the bare payload; every other format (json — the default) wraps
|
||||
// it in an ok/identity/data envelope. Extracted from Execute so the default
|
||||
// envelope behavior is unit-testable without a live WebSocket.
|
||||
func watchOutputValue(outFormat, identity string, outputData interface{}) interface{} {
|
||||
if outFormat == "data" {
|
||||
return outputData
|
||||
}
|
||||
return output.Envelope{OK: true, Identity: identity, Data: outputData}
|
||||
}
|
||||
|
||||
func watchFetchFailureValue(messageID, fetchFormat string, err error, eventBody map[string]interface{}) map[string]interface{} {
|
||||
payload := map[string]interface{}{
|
||||
"ok": false,
|
||||
|
||||
@@ -885,3 +885,59 @@ func dryRunAPIsForMailWatchTest(t *testing.T, dry *common.DryRunAPI) []struct {
|
||||
}
|
||||
return payload.API
|
||||
}
|
||||
|
||||
// TestWatchOutputValueDefaultIsJSONEnvelope verifies +watch's default format
|
||||
// (json) wraps each event in an ok/identity/data envelope, while --format data
|
||||
// emits the bare payload. Covers the default-format behavior without a live
|
||||
// WebSocket (addresses the coderabbitai review ask).
|
||||
func TestWatchOutputValueDefaultIsJSONEnvelope(t *testing.T) {
|
||||
payload := map[string]interface{}{"message": map[string]interface{}{"message_id": "m1"}}
|
||||
|
||||
// default (json) → ok/identity/data envelope
|
||||
b, err := json.Marshal(watchOutputValue("json", "user", payload))
|
||||
if err != nil {
|
||||
t.Fatalf("marshal json value: %v", err)
|
||||
}
|
||||
var env map[string]interface{}
|
||||
if err := json.Unmarshal(b, &env); err != nil {
|
||||
t.Fatalf("unmarshal json value: %v", err)
|
||||
}
|
||||
if env["ok"] != true {
|
||||
t.Fatalf("default json must have ok:true, got %s", b)
|
||||
}
|
||||
if env["identity"] != "user" {
|
||||
t.Fatalf("default json must carry identity, got %s", b)
|
||||
}
|
||||
if _, ok := env["data"]; !ok {
|
||||
t.Fatalf("default json must have data field, got %s", b)
|
||||
}
|
||||
|
||||
// empty format (safety) also defaults to the json envelope
|
||||
b3, err := json.Marshal(watchOutputValue("", "bot", payload))
|
||||
if err != nil {
|
||||
t.Fatalf("marshal empty-format value: %v", err)
|
||||
}
|
||||
var env3 map[string]interface{}
|
||||
if err := json.Unmarshal(b3, &env3); err != nil {
|
||||
t.Fatalf("unmarshal empty-format value: %v", err)
|
||||
}
|
||||
if env3["ok"] != true {
|
||||
t.Fatalf("empty format should default to json envelope, got %s", b3)
|
||||
}
|
||||
|
||||
// --format data → bare payload, no envelope
|
||||
b2, err := json.Marshal(watchOutputValue("data", "user", payload))
|
||||
if err != nil {
|
||||
t.Fatalf("marshal data value: %v", err)
|
||||
}
|
||||
var bare map[string]interface{}
|
||||
if err := json.Unmarshal(b2, &bare); err != nil {
|
||||
t.Fatalf("unmarshal data value: %v", err)
|
||||
}
|
||||
if _, hasOK := bare["ok"]; hasOK {
|
||||
t.Fatalf("--format data must be bare (no envelope), got %s", b2)
|
||||
}
|
||||
if _, hasMsg := bare["message"]; !hasMsg {
|
||||
t.Fatalf("--format data payload should carry message, got %s", b2)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ metadata:
|
||||
- 用户要**识别飞书 / doubao 云空间 URL 的类型和 token**时,可以先按 URL 路径形态做轻量判断;当路径已明确指向 docx / sheet / bitable / slides / file / folder 等资源时,可直接提取对应 token/type。传入 wiki URL、需要识别标题或 canonical URL、URL/token 有歧义,或后续操作依赖底层真实资源时,再使用 `lark-cli drive +inspect --url '<url>'` 进行识别;具体用法、失败处理和边界见 [`references/lark-drive-inspect.md`](references/lark-drive-inspect.md)。
|
||||
- 高风险写操作(删除、公开权限修改、owner 转移、版本删除/回滚、批量移动/覆盖/同步)必须同时满足三个条件才执行:目标已解析为该操作可直接使用的执行对象,执行细节已明确到可直接调用命令(例如删除的 file-token/type、公开权限修改的共享范围、owner 转移的目标 owner、版本删除/回滚的 version id、移动/覆盖/同步的目标位置和冲突策略),且用户在本轮明确确认执行这些具体目标和执行细节。用户只说“删除没用的文件”“开放/共享给大家”“改成开放”“覆盖/移动这些”只表示目标状态;先只读发现并列出候选、权限档位或执行方案,停止等待用户确认。
|
||||
- 用户要**检查 / 治理文档权限、公开范围、链接分享、外部访问、复制下载权限、密级标签、owner 转移**,或要“权限风险报告、收紧权限、申请查看 / 编辑权限、转移 / 批量转移 owner”,必须先阅读 [`references/lark-drive-workflow.md`](references/lark-drive-workflow.md),再按其中 `Workflow Registry` 进入 [`permission_governance`](references/lark-drive-workflow-permission-governance.md) workflow。
|
||||
- 用户要**整理云盘 / 文件夹 / 文档库 / 知识库 / 个人文档库**,或要“盘点目录结构、找出未归档/临时/重复/空目录、生成整理方案”,必须先阅读 [`references/lark-drive-workflow.md`](references/lark-drive-workflow.md),再按其中 `Workflow Registry` 进入 [`knowledge_organize`](references/lark-drive-workflow-knowledge-organize.md) workflow。默认只生成方案;创建目录、移动资源、申请权限都必须单独确认。
|
||||
- 用户要**整理云盘 / 文件夹 / 文档库 / 知识库 / 个人文档库**,或要“盘点目录结构、找出未归档/临时/重复/空目录、生成整理方案”,必须先阅读 [`references/lark-drive-workflow-knowledge-organize.md`](references/lark-drive-workflow-knowledge-organize.md)。默认只生成方案;创建目录、移动资源、申请权限都必须单独确认。
|
||||
- 用户要**搜文档 / Wiki / 电子表格 / 多维表格 / 云空间(云盘/云存储)对象**,优先使用 `lark-cli drive +search`。自然语言里"最近我编辑过的"、"我创建的"(→ `--created-by-me`,原始创建者语义)、"我负责/owner 的"(→ `--mine`,owner 语义)、"最近一周我打开过的 xxx"、"某人 owner 的 docx" 等直接映射到扁平 flag,避免手写嵌套 JSON。
|
||||
- 用户要**根据文档评论定位正文位置**,例如 根据评论 review 文档、根据评论内容回看文档、区分多处相同引用文本时,对于 docx 类型(`file_type=docx`)的文档支持通过 `need_relation=true` 返回评论位置,其他类型暂不支持,具体用法需要先阅读 [`references/lark-drive-comment-location.md`](references/lark-drive-comment-location.md) 了解。
|
||||
- 用户给出 doubao.com 的云空间资源 URL/token,或明确提到豆包里的 file/folder/docx/sheet/bitable/wiki 资源时,仍按资源类型、URL 路径和 token 路由到本 skill;不要因为域名不是飞书而回退到 WebFetch。
|
||||
|
||||
@@ -1,12 +1,6 @@
|
||||
# 知识整理工作流
|
||||
|
||||
Workflow id: `knowledge_organize`
|
||||
|
||||
Risk / Structure: `R2-R3` / `S3`
|
||||
|
||||
This file implements the registered knowledge organization workflow. Before execution, the agent MUST read [`lark-drive-workflow.md`](lark-drive-workflow.md) and [`../../lark-shared/SKILL.md`](../../lark-shared/SKILL.md), and follow the shared execution protocol, Artifact Contract, Workflow Loading rules, authentication rules, and write confirmation rules.
|
||||
|
||||
It defines the workflow-specific state machine and progressive loading map. Stage-specific rules live in phase files and MUST be loaded only when the workflow reaches the corresponding state.
|
||||
This file is the single entry point for the knowledge organization workflow. It defines the global contract, state machine, and progressive loading map. Stage-specific rules live in phase files and MUST be loaded only when the workflow reaches the corresponding state.
|
||||
|
||||
Phase files are references for this workflow, not independent skills. Do not route user requests directly to a phase file.
|
||||
|
||||
@@ -86,7 +80,7 @@ When this workflow is triggered, the agent MUST:
|
||||
|
||||
## Runtime State
|
||||
|
||||
This workflow extends the shared Artifact Contract. Agent MUST maintain these internal fields during one workflow run:
|
||||
Agent MUST maintain these internal fields during one workflow run:
|
||||
|
||||
| Field | Meaning |
|
||||
|-------|---------|
|
||||
@@ -120,24 +114,24 @@ This workflow extends the shared Artifact Contract. Agent MUST maintain these in
|
||||
|
||||
## Execution State Machine
|
||||
|
||||
| State | Protocol Step | Entry Condition | Agent MUST Do | User-Facing Output | wait_for_user | Next State |
|
||||
|-------|---------------|-----------------|---------------|--------------------|---------------|------------|
|
||||
| `PARSE_SCOPE` | `route` / `scope` | Workflow triggered | Load discovery phase; parse target, environment, identity, and target type | Scope confirmation or clarification question | `true` | `INVENTORY` |
|
||||
| `INVENTORY` | `read` | Scope confirmed | Load discovery phase; recursively list resources and build `resource_items` | Inventory progress / summary; continue automatically unless blocked | `false` unless blocked | `CONTENT_READ` |
|
||||
| `CONTENT_READ` | `read` | Inventory complete | Load analysis phase; identify low-confidence items and perform mandatory partial read when needed | Low-confidence read summary | `false` unless auth / permission blocks | `ISSUE_ANALYSIS` |
|
||||
| `ISSUE_ANALYSIS` | `assess` / `plan` | Resource list and partial reads ready | Load analysis phase; detect structure problems, evidence, and organization approach | Inventory result, problems, organization approach, and decision options | `true` | `RULE_GENERATION` |
|
||||
| `RULE_GENERATION` | `assess` / `plan` | User confirms organization approach | Load analysis phase; generate classification rules and `target_tree` | No separate stop; target tree is shown with plan generation | `false` | `PLAN_GENERATION` |
|
||||
| `PLAN_GENERATION` | `assess` / `plan` | Target tree ready | Load planning phase; generate complete internal `plan_items`; show target tree plus plan overview or page | Target tree and plan overview / paginated plan page | `true` | `EXEC_CONFIRM` |
|
||||
| `EXEC_CONFIRM` | `confirm` | User wants execution | Load planning phase; ask user to choose execution scope | Execution options and write-operation summary | `true` | `EXECUTE` or `DONE` |
|
||||
| `EXECUTE` | `execute` | User explicitly confirmed execution scope | Load execution phase; execute only whitelisted write operations for confirmed scope while maintaining internal recovery state | Progress reports for large or long-running execution; if blocked after successful moves, ask whether to try restoring to `整理前的位置` | `false` unless blocked / recovery offered | `VERIFY`, `ROLLBACK_CONFIRM`, or `DONE` |
|
||||
| `VERIFY` | `verify` | Execution finished | Load execution phase; rescan target scope and compare actual path/token against plan | Verification table and final summary; if serious mismatches exist, ask whether to try restoring to `整理前的位置` | `false` unless recovery offered | `DONE` or `ROLLBACK_CONFIRM` |
|
||||
| `ROLLBACK_CONFIRM` | `recovery confirm` | User asks to restore after execution failure / verification mismatch / explicit rollback request | Load rollback phase; generate internal `rollback_plan`; ask whether to execute recovery | Recoverable scope and restore confirmation | `true` | `ROLLBACK` or `DONE` |
|
||||
| `ROLLBACK` | `recovery execute` | User explicitly confirms restore execution | Load rollback phase; execute confirmed reverse moves only | Recovery progress / result | `false` | `ROLLBACK_VERIFY` |
|
||||
| `ROLLBACK_VERIFY` | `recovery verify` | Recovery execution finished | Load rollback phase; verify restored locations and decide whether cleanup candidates exist | Recovery verification result | `false` | `ROLLBACK_CLEANUP_CONFIRM` or `DONE` |
|
||||
| `ROLLBACK_CLEANUP_CONFIRM` | `cleanup confirm` | Cleanup candidates exist after recovery, or user asks to clean workflow-created empty folders / nodes | Load rollback phase; generate cleanup plan and ask for delete confirmation | Cleanup candidates and delete confirmation | `true` | `ROLLBACK_CLEANUP` or `DONE` |
|
||||
| `ROLLBACK_CLEANUP` | `cleanup execute` | User explicitly confirms cleanup deletion | Load rollback phase; delete only confirmed workflow-created safe-empty folders / nodes | Cleanup progress / result | `false` | `ROLLBACK_CLEANUP_VERIFY` |
|
||||
| `ROLLBACK_CLEANUP_VERIFY` | `cleanup verify` | Cleanup deletion finished | Load rollback phase; verify deleted cleanup targets | Cleanup verification result | `false` | `DONE` |
|
||||
| `DONE` | `done` | No more action | Stop | Final answer | `false` | End |
|
||||
| State | Entry Condition | Agent MUST Do | User-Facing Output | wait_for_user | Next State |
|
||||
|-------|-----------------|---------------|--------------------|---------------|------------|
|
||||
| `PARSE_SCOPE` | Workflow triggered | Load discovery phase; parse target, environment, identity, and target type | Scope confirmation or clarification question | `true` | `INVENTORY` |
|
||||
| `INVENTORY` | Scope confirmed | Load discovery phase; recursively list resources and build `resource_items` | Inventory progress / summary; continue automatically unless blocked | `false` unless blocked | `CONTENT_READ` |
|
||||
| `CONTENT_READ` | Inventory complete | Load analysis phase; identify low-confidence items and perform mandatory partial read when needed | Low-confidence read summary | `false` unless auth / permission blocks | `ISSUE_ANALYSIS` |
|
||||
| `ISSUE_ANALYSIS` | Resource list and partial reads ready | Load analysis phase; detect structure problems, evidence, and organization approach | Inventory result, problems, organization approach, and decision options | `true` | `RULE_GENERATION` |
|
||||
| `RULE_GENERATION` | User confirms organization approach | Load analysis phase; generate classification rules and `target_tree` | No separate stop; target tree is shown with plan generation | `false` | `PLAN_GENERATION` |
|
||||
| `PLAN_GENERATION` | Target tree ready | Load planning phase; generate complete internal `plan_items`; show target tree plus plan overview or page | Target tree and plan overview / paginated plan page | `true` | `EXEC_CONFIRM` |
|
||||
| `EXEC_CONFIRM` | User wants execution | Load planning phase; ask user to choose execution scope | Execution options and write-operation summary | `true` | `EXECUTE` or `DONE` |
|
||||
| `EXECUTE` | User explicitly confirmed execution scope | Load execution phase; execute only whitelisted write operations for confirmed scope while maintaining internal recovery state | Progress reports for large or long-running execution; if blocked after successful moves, ask whether to try restoring to `整理前的位置` | `false` unless blocked / recovery offered | `VERIFY`, `ROLLBACK_CONFIRM`, or `DONE` |
|
||||
| `VERIFY` | Execution finished | Load execution phase; rescan target scope and compare actual path/token against plan | Verification table and final summary; if serious mismatches exist, ask whether to try restoring to `整理前的位置` | `false` unless recovery offered | `DONE` or `ROLLBACK_CONFIRM` |
|
||||
| `ROLLBACK_CONFIRM` | User asks to restore after execution failure / verification mismatch / explicit rollback request | Load rollback phase; generate internal `rollback_plan`; ask whether to execute recovery | Recoverable scope and restore confirmation | `true` | `ROLLBACK` or `DONE` |
|
||||
| `ROLLBACK` | User explicitly confirms restore execution | Load rollback phase; execute confirmed reverse moves only | Recovery progress / result | `false` | `ROLLBACK_VERIFY` |
|
||||
| `ROLLBACK_VERIFY` | Recovery execution finished | Load rollback phase; verify restored locations and decide whether cleanup candidates exist | Recovery verification result | `false` | `ROLLBACK_CLEANUP_CONFIRM` or `DONE` |
|
||||
| `ROLLBACK_CLEANUP_CONFIRM` | Cleanup candidates exist after recovery, or user asks to clean workflow-created empty folders / nodes | Load rollback phase; generate cleanup plan and ask for delete confirmation | Cleanup candidates and delete confirmation | `true` | `ROLLBACK_CLEANUP` or `DONE` |
|
||||
| `ROLLBACK_CLEANUP` | User explicitly confirms cleanup deletion | Load rollback phase; delete only confirmed workflow-created safe-empty folders / nodes | Cleanup progress / result | `false` | `ROLLBACK_CLEANUP_VERIFY` |
|
||||
| `ROLLBACK_CLEANUP_VERIFY` | Cleanup deletion finished | Load rollback phase; verify deleted cleanup targets | Cleanup verification result | `false` | `DONE` |
|
||||
| `DONE` | No more action | Stop | Final answer | `false` | End |
|
||||
|
||||
## Progressive Load Map
|
||||
|
||||
|
||||
@@ -97,7 +97,7 @@ Structure Level:
|
||||
2. Entry file 超过约 300 行时,优先拆 `commands`、`outputs` 或 `artifacts` reference。
|
||||
3. 只有执行、验证、恢复或 rollback 状态链复杂到影响可读性时,才升级到 `S3` phase files。
|
||||
4. 垂直业务包优先作为已有 workflow 的 recipe / policy / template,不默认新增独立 workflow。
|
||||
5. 已有样板:`permission_governance` 是 `R2/S2`;`knowledge_organize` 是 `R2-R3/S3`。
|
||||
5. 已有样板:`permission_governance` 是 `R2/S2`;已发布的独立 `knowledge_organize` 是 `R2-R3/S3`,当前不作为本总框架 registry entry。
|
||||
|
||||
## 加载与拆分边界
|
||||
|
||||
@@ -111,7 +111,6 @@ Structure Level:
|
||||
| Workflow | Status | Risk | Structure | Entry File | Trigger |
|
||||
|----------|--------|------|-----------|------------|---------|
|
||||
| `permission_governance` | Registered | `R2` | `S2` | [`lark-drive-workflow-permission-governance.md`](lark-drive-workflow-permission-governance.md) | 权限审计、公开链接/外部访问、复制/下载/评论/分享设置、权限申请、owner 转移 / 批量 owner 转移、密级标签调整 |
|
||||
| `knowledge_organize` | Registered | `R2-R3` | `S3` | [`lark-drive-workflow-knowledge-organize.md`](lark-drive-workflow-knowledge-organize.md) | 整理云盘 / 文件夹 / 文档库 / 知识库、盘点目录结构、归类资源、生成整理方案,并在用户确认后创建目录或移动资源 |
|
||||
|
||||
## Workflow Loading
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ lark-cli mail +triage --page-size 10
|
||||
|------|------|------|
|
||||
| `--filter <json>` | — | 筛选条件(见下方字段说明) |
|
||||
| `--query <text>` | — | 全文搜索关键词 |
|
||||
| `--format <mode>` | `table` | `table` / `json` / `data`(`json` 和 `data` 均输出含分页信息的对象) |
|
||||
| `--format <mode>` | `json` | `json`(默认,输出含分页信息的对象)/ `data`(同 `json`)/ `table`(人类可读表格) |
|
||||
| `--max <n>` | `20` | 最大返回条数(1-400),内部自动分页拉取 |
|
||||
| `--page-size <n>` | — | `--max` 的别名,两者含义相同;同时指定时 `--page-size` 优先 |
|
||||
| `--page-token <token>` | — | 上一次响应返回的分页令牌,传入后从该位置继续拉取。令牌带 `search:` 或 `list:` 前缀,标识来源路径,不可混用 |
|
||||
|
||||
@@ -47,7 +47,7 @@ lark-cli mail +watch --print-output-schema
|
||||
|------|------|------|
|
||||
| `--mailbox <id>` | `me` | 订阅目标邮箱 |
|
||||
| `--msg-format <mode>` | `metadata` | 输出模式:`metadata` / `minimal` / `plain_text_full` / `full` / `event` |
|
||||
| `--format <mode>` | `data` | 输出样式:`json`(带 ok/data 信封的 NDJSON 流)/ `data`(裸 NDJSON 流) |
|
||||
| `--format <mode>` | `json` | 输出样式:`json`(默认,带 ok/data 信封的 NDJSON 流)/ `data`(裸 NDJSON 流) |
|
||||
| `--folder-ids <json-array>` | — | 文件夹 ID 过滤,如 `["INBOX","SENT"]` |
|
||||
| `--folders <json-array>` | — | 文件夹名称过滤(与 `--folder-ids` 取并集) |
|
||||
| `--label-ids <json-array>` | — | 标签 ID 过滤,如 `["FLAGGED","IMPORTANT"]` |
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
## Core Rules
|
||||
|
||||
- `asset_need` is metadata only. It can guide page design, but it must not require web search, local download, media upload, or external tools.
|
||||
- Every planned asset must include a fallback visual plan. The fallback can use native charts, tables, whiteboard diagrams, placeholder regions, or XML shapes, text, and arrows as appropriate.
|
||||
- Every planned asset must include a fallback visual plan so the slide can be generated with XML shapes, text, arrows, tables, simple charts, whiteboard diagrams, or placeholder regions.
|
||||
- Asset needs must serve the page's `key_message` and `visual_focus`. Do not add decorative assets that do not clarify the page.
|
||||
- Prefer a few high-value asset plans over one asset on every page. For a 6-page technical or business deck, plan assets on at least 3 pages when the content allows.
|
||||
- If a real local asset already exists or the user provides one, it can be used through the normal media-upload workflow. Still keep `fallback_if_missing` in the plan.
|
||||
@@ -43,7 +43,7 @@ For a page without a meaningful asset need, use:
|
||||
- `architecture_diagram`: system components, data flow, dependency map, or model structure.
|
||||
- `icon`: small semantic symbol for a concept, step, role, or status.
|
||||
- `logo`: brand, product, team, or customer mark.
|
||||
- `chart`: column, bar, line, area, radar, pie, doughnut/ring, or combo data visual. Note: `<chart>` does not support funnel or scatter — map those to `<whiteboard>` SVG at generation time.
|
||||
- `chart`: line, bar, pie, radar, area, or combo data visual. Note: `<chart>` does not support funnel or scatter — map those to `<whiteboard>` SVG at generation time.
|
||||
- `infographic`: composed visual explanation, usually combining labels, numbers, and simple shapes.
|
||||
- `screenshot`: product UI, terminal output, workflow state, or page capture.
|
||||
- `flow_diagram`: process, sequence, decision tree, or mechanism diagram.
|
||||
@@ -64,23 +64,11 @@ Match asset type to slide role:
|
||||
|
||||
`suggested_query` is only a future lookup hint. Write it as a short phrase a human or later workflow could search, but do not execute the search unless the user separately requests real assets.
|
||||
|
||||
For `asset_type: "chart"`:
|
||||
|
||||
- If the visual is a supported standard data chart — column, bar, line, area, radar, pie, doughnut/ring, or combo — `fallback_if_missing` must still render as a native `<chart>`.
|
||||
- Do not imitate supported standard data visuals with manual drawing primitives or `<whiteboard>`.
|
||||
- Choose the data source explicitly:
|
||||
- `user_provided`: when the user provides concrete values, tables, CSV, or metric lists, use those values and do not replace them with mock data.
|
||||
- `mock_placeholder`: when the user asks for a placeholder, template, example, or chart position to replace later, use mock data in a native `<chart>`.
|
||||
- `mock_required_by_intent`: when the user does not provide concrete values but asks for data expression, charts, trends, comparisons, or distributions, use mock data in a native `<chart>`.
|
||||
- Mock data must be labeled as `模拟数据,仅占位,待替换真实数据` or equivalent. Do not present mock values as facts.
|
||||
- Manual drawing fallbacks are allowed only for unsupported chart types such as scatter, funnel, waterfall-like custom visuals, or decorative non-data visuals.
|
||||
|
||||
`fallback_if_missing` must be concrete enough to turn into XML, for example:
|
||||
|
||||
- "Draw a simplified attention matrix with 5 token labels, semi-transparent cells, and arrows to output token."
|
||||
- "Use three grouped boxes with arrows from client to gateway to service; add small protocol labels."
|
||||
- "Render a native `<chart>` using the user-provided series."
|
||||
- "Render a native `<chart>` with mock placeholder values and label it as `模拟数据,仅占位,待替换真实数据`."
|
||||
- "Render a mini bar chart with 4 bars using shapes and value labels."
|
||||
- "Use a bordered placeholder panel with product area labels, not an empty image."
|
||||
|
||||
Weak fallbacks to avoid:
|
||||
@@ -130,7 +118,7 @@ Business comparison page:
|
||||
When generating XML:
|
||||
|
||||
1. If an asset exists and the workflow supports it, place it in the planned visual region.
|
||||
2. If no asset exists, immediately render `fallback_if_missing` with the planned XML-native element type. Supported standard data visuals still use native `<chart>`; other fallbacks may use shapes, text, lines, arrows, tables, whiteboard diagrams, or placeholder panels.
|
||||
2. If no asset exists, immediately render `fallback_if_missing` with XML-native shapes, text, lines, arrows, tables, whiteboard diagrams, or chart-like elements.
|
||||
3. Size the fallback to satisfy `visual_focus`; it should be a real page element, not a tiny decoration.
|
||||
4. Keep text-density limits. Do not compensate for missing assets by adding long bullet text.
|
||||
5. After creation, fetch the presentation and verify asset pages are not blank and that each planned fallback is visible when no real asset was used.
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
|
||||
`<whiteboard>` 放在 `<data>` 内,内部可放 **SVG** 或 **Mermaid**,用于绘制流程图、时序图、架构图、散点图、漏斗图、自定义图标、装饰图案等 `<chart>` 和 `<shape>` 难以覆盖的视觉内容。
|
||||
|
||||
普通柱状图、条形图、折线图、面积图、雷达图、饼图 / 环图和组合图应优先使用原生 `<chart>`。除非用户明确要求像素级自定义,或图表类型确实不受 `<chart>` 支持,否则不要用 `<whiteboard>` + SVG / Mermaid 重画这些标准图表。
|
||||
|
||||
> 前置条件:使用本文档前先阅读 [lark-slides SKILL.md](../SKILL.md)。
|
||||
|
||||
---
|
||||
@@ -14,13 +12,13 @@
|
||||
|
||||
| 场景 | 推荐元素 |
|
||||
|------|---------|
|
||||
| 有结构化数据序列的柱/条/折线/面积/雷达/饼/环/组合图 | `<chart>` — 原生渲染,支持 legend / tooltip / 系列配色 |
|
||||
| 散点图、漏斗图(`<chart>` 不支持)或其他非原生数据视觉 | `<whiteboard>` SVG |
|
||||
| 有结构化数据序列的柱/条/折线/面积/雷达/饼/组合图 | `<chart>` — 原生渲染,支持 legend / tooltip / 系列配色 |
|
||||
| 散点图、漏斗图(`<chart>` 不支持) | `<whiteboard>` SVG |
|
||||
| 流程图、时序图、架构图、类图、ER 图等拓扑图 | `<whiteboard>` Mermaid 或 SVG |
|
||||
| 自定义图标、徽标、示意性图形(需要 path/polygon 精确控制) | `<whiteboard>` SVG |
|
||||
| 进度条、波浪背景、装饰图案、像素级自定义可视化 | `<whiteboard>` SVG |
|
||||
|
||||
> 适合 `<chart>` 的内容就用 `<chart>`,不要用 SVG / Mermaid 手绘——原生渲染更省力、结构更稳定,也更容易被回读和后续编辑。
|
||||
> 适合 `<chart>` 的内容就用 `<chart>`,不要用 SVG 手绘——原生渲染更省力且质量更高。
|
||||
|
||||
---
|
||||
|
||||
@@ -41,13 +39,9 @@ SVG 内的坐标相对于 whiteboard 自身左上角(0,0),与 slide 坐标
|
||||
|
||||
## SVG 还是 Mermaid?
|
||||
|
||||
选择分三步:**先排除原生 `<chart>`,再判断 whiteboard 类型,最后看当前模型身份**。
|
||||
选择分两步:**先看图表类型,再看当前模型身份**。
|
||||
|
||||
### 第一步:先确认是否应该使用 `<chart>`
|
||||
|
||||
如果内容是柱状图、条形图、折线图、面积图、雷达图、饼图 / 环图或组合图,返回使用原生 `<chart>`,不要继续套用本文档的 SVG / Mermaid 路径。
|
||||
|
||||
### 第二步:whiteboard 类型优先判断
|
||||
### 第一步:图表类型优先判断
|
||||
|
||||
以下类型**推荐 Mermaid**,自动布局、代码简洁;如需精确匹配品牌配色或自定义节点样式,可改用 SVG:
|
||||
|
||||
@@ -56,19 +50,20 @@ SVG 内的坐标相对于 whiteboard 自身左上角(0,0),与 slide 坐标
|
||||
| 流程图、决策树、架构图 | `flowchart TD` / `flowchart LR` |
|
||||
| 时序图 | `sequenceDiagram` |
|
||||
| 类图 | `classDiagram` |
|
||||
| 饼图 | `pie` |
|
||||
| 甘特图 | `gantt` |
|
||||
| 状态图 | `stateDiagram-v2` |
|
||||
| 思维导图 | `mindmap` |
|
||||
| ER 图 | `erDiagram` |
|
||||
|
||||
### 第三步:非原生图表与装饰元素按模型身份选路径
|
||||
### 第二步:数据图表与装饰元素按模型身份选路径
|
||||
|
||||
上表以外的场景(散点图、漏斗图、进度条、时间线、波浪背景、星点纹理等)需要精确控制坐标和配色,SVG 表达力更强,但各模型生成 SVG 的能力有差异:
|
||||
|
||||
| 模型身份 | 路径 |
|
||||
|----------|------|
|
||||
| Claude / Gemini / GPT / GLM | **SVG** — 精确控制坐标、颜色、透明度 |
|
||||
| Doubao / Seed / Other | **Mermaid** — 用 `gantt`、`flowchart` 等近似表达;确实无法用 Mermaid 表达时才回退到简单 SVG 矩形/线条 |
|
||||
| Doubao / Seed / Other | **Mermaid** — 用 `pie`、`gantt` 等近似表达;确实无法用 Mermaid 表达时才回退到简单 SVG 矩形/线条 |
|
||||
|
||||
> **先自报身份再选路径**:在决定使用 SVG 之前,确认当前模型属于哪一类。不要跳过这一步。
|
||||
|
||||
@@ -78,13 +73,13 @@ SVG 内的坐标相对于 whiteboard 自身左上角(0,0),与 slide 坐标
|
||||
|
||||
### ⚠️ 设计品质要求
|
||||
|
||||
在 slide 里嵌入 `<whiteboard>` 的目的是**表达原生 `<chart>` 或基础 `<shape>` 难以覆盖的视觉关系**,不是把标准数据图表手绘一遍。
|
||||
在 slide 里嵌入 `<whiteboard>` 的目的是**提升视觉质量**,不是把数字堆进去。
|
||||
|
||||
- **不要只用矩形加文字应付**:通篇纯白底色 + 方块 + 黑字等于白做,这是不及格输出
|
||||
- **非原生数据视觉必须有坐标系**:散点、漏斗等仍要有必要的坐标轴、刻度、数值标注或分段说明,不要只画点或色块
|
||||
- **数据图表必须有坐标系**:坐标轴、网格线、数值标注缺一不可,不要只画柱子或点
|
||||
- **字号必须有层级**:标题 ≠ 标签 ≠ 数值,混用同一字号会消灭视觉焦点
|
||||
- **配色要与 slide 主题呼应**:深色 slide 背景下图表用透明底或深色卡片;浅色背景下避免再加纯白底块
|
||||
- **每个 whiteboard 都是设计机会**:主动用圆角、半透明填充、清晰分组、节点状态等细节拉开与默认模板的差距
|
||||
- **每个 whiteboard 都是设计机会**:主动用圆角、半透明填充、折线面积、点装饰等细节拉开与默认模板的差距
|
||||
- **写 SVG 前先判断背景亮度**:背景亮度 < 30% 时,装饰元素"对比不足"比"过强"危害更大,宁重勿轻;
|
||||
- **装饰层次用亮度跳跃,不用线性叠透明度**:`α=0.04→0.08→0.12` 的等差递增在深色底上几乎看不出差异(相邻层亮度差 ≈20);正确做法是非线性跳跃如 `0.10→0.40→0.70→1.0`,相邻层亮度差 ≥60。
|
||||
|
||||
@@ -111,11 +106,11 @@ whiteboard 渲染时以**所有子元素的几何包围盒合并结果**为内
|
||||
|
||||
| 元素 | 说明 | 典型用途 |
|
||||
|------|------|---------|
|
||||
| `<rect>` | 矩形,支持 `rx` 圆角 | 卡片、进度条、分段色块 |
|
||||
| `<rect>` | 矩形,支持 `rx` 圆角 | 柱图、卡片、进度条 |
|
||||
| `<circle>` | 圆 | 节点、装饰点、环形图 |
|
||||
| `<ellipse>` | 椭圆 | 自定义轮廓图形 |
|
||||
| `<line>` | 直线 | 轴线、分隔线、连接线 |
|
||||
| `<path>` | 任意路径(支持 Q/C 曲线) | 波浪、曲线、弧形 |
|
||||
| `<line>` | 直线 | 坐标轴、分隔线 |
|
||||
| `<path>` | 任意路径(支持 Q/C 曲线) | 波浪、折线、弧形 |
|
||||
| `<text>` | 文本,支持中文 | 标签、数值 |
|
||||
| `<polygon>` | 多边形 | 箭头、星形、面积填充 |
|
||||
| `<g>` | 分组 | 批量变换、语义分组 |
|
||||
@@ -128,25 +123,27 @@ whiteboard 渲染时以**所有子元素的几何包围盒合并结果**为内
|
||||
---
|
||||
### 元素计算
|
||||
|
||||
SVG 中只要涉及批量定位、等间距排布或数据映射,**建议额外运行一个 Python 脚本把坐标算出来再填入 SVG**,而不是手动估值。适用范围包括散点、漏斗、装饰性点阵、等间距圆、重复图案等;普通柱状图、折线图、饼图仍应回到原生 `<chart>`。
|
||||
SVG 中只要涉及批量定位、等间距排布或数据映射,**建议额外运行一个 Python 脚本把坐标算出来再填入 SVG**,而不是手动估值。适用范围不限于数据图表——装饰性点阵、等间距圆、重复图案同样适用。
|
||||
|
||||
> **主动去算**:写 SVG 之前先运行脚本,把输出当注释贴在 `<svg>` 开头,再照着填坐标。估值几乎每次都需要反复调整,跳过这步反而更慢。
|
||||
|
||||
**散点图 / 装饰性点阵范式**
|
||||
**数据图表(柱状图范式)**
|
||||
|
||||
```python
|
||||
W, H = 360, 260
|
||||
origin_x, origin_y = 50, 216 # 左下角,SVG Y 轴向下
|
||||
cw, ch = 290, 184
|
||||
|
||||
points = [(12, 40), (28, 80), (45, 65)]
|
||||
x_min, x_max, y_min, y_max = 0, 50, 0, 100
|
||||
for i, (xv, yv) in enumerate(points):
|
||||
x = round(origin_x + (xv - x_min) / (x_max - x_min) * cw)
|
||||
y = round(origin_y - (yv - y_min) / (y_max - y_min) * ch)
|
||||
print(f"point-{i}: cx={x} cy={y}")
|
||||
data, y_max = [120, 160, 90], 200
|
||||
bar_w = int(cw / len(data) * 0.62)
|
||||
for i, v in enumerate(data):
|
||||
cx = round(origin_x + (i + 0.5) * cw / len(data))
|
||||
y = round(origin_y - v / y_max * ch)
|
||||
print(f"bar-{i}: x={cx - bar_w//2} y={y} w={bar_w} h={round(origin_y - y)}")
|
||||
```
|
||||
|
||||
折线图:`x = origin_x + i/(n-1)*cw`,`y = origin_y - (v-y_min)/(y_max-y_min)*ch`。
|
||||
|
||||
**装饰性元素(等间距范式)**
|
||||
|
||||
```python
|
||||
@@ -163,9 +160,9 @@ for i in range(n):
|
||||
```python
|
||||
# 每个元素登记 (x, y, w, h),含 stroke 外扩
|
||||
elements = [
|
||||
(10, 20, 80, 160), # item-0
|
||||
(107, 10, 80, 170), # item-1
|
||||
(204, 40, 80, 140), # item-2
|
||||
(10, 20, 80, 160), # bar-0
|
||||
(107, 10, 80, 170), # bar-1
|
||||
(204, 40, 80, 140), # bar-2
|
||||
(0, 0, 300, 1), # x-axis
|
||||
]
|
||||
|
||||
@@ -264,6 +261,7 @@ print(f"whiteboard width={wb_w} height={wb_h}")
|
||||
| 流程图 | `flowchart TD` / `flowchart LR` | 业务流程、决策树、工作流 |
|
||||
| 时序图 | `sequenceDiagram` | 系统交互、API 调用链 |
|
||||
| 甘特图 | `gantt` | 项目计划、里程碑 |
|
||||
| 饼图 | `pie` | 占比数据 |
|
||||
| 类图 | `classDiagram` | 对象关系、架构设计 |
|
||||
| ER 图 | `erDiagram` | 数据库结构 |
|
||||
| 状态图 | `stateDiagram-v2` | 状态机、生命周期 |
|
||||
@@ -281,6 +279,7 @@ Mermaid 图表会自动撑满 whiteboard 区域。建议:
|
||||
|---------|-----------|------------|
|
||||
| 流程图(5-8 节点) | 720-816 | 300-400 |
|
||||
| 时序图(3-5 参与者) | 720-816 | 320-420 |
|
||||
| 饼图 | 500-600 | 300-360 |
|
||||
| 甘特图 | 816 | 280-360 |
|
||||
| 思维导图 | 816 | 380-480 |
|
||||
|
||||
@@ -308,7 +307,7 @@ Mermaid 语法包含 `[`、`>`、`-->`,不用 CDATA 直接写会破坏 XML 解
|
||||
- [ ] 文字 `y` 坐标为 baseline 位置,最小值 ≥ font-size(避免被裁切)
|
||||
|
||||
**SVG 模式——视觉品质检查:**
|
||||
- [ ] 非原生数据视觉有必要的坐标轴、网格线、数值标注或分段说明,没有"裸点"或无解释色块
|
||||
- [ ] 坐标轴、网格线、数值标注齐全,没有"裸柱子"或"裸折线"
|
||||
- [ ] 字号有层级:标题 > 数值 > 轴标签,非全部相同
|
||||
- [ ] 单一数据系列用同一颜色,多系列用不同颜色且对比充足
|
||||
- [ ] 轴标签与图表元素互不遮挡,留有足够空间
|
||||
|
||||
@@ -119,34 +119,6 @@ Each slide must include:
|
||||
- `text_density`: `low`, `medium`, or `high`.
|
||||
- `speaker_intent`: why the speaker needs this page and how it advances the story.
|
||||
|
||||
Optional slide fields:
|
||||
|
||||
- `chart_contract`: required when the page plan includes a standard data chart that `<chart>` supports. Use this shape:
|
||||
|
||||
```json
|
||||
{
|
||||
"chart_contract": {
|
||||
"required": true,
|
||||
"render_as": "native_chart",
|
||||
"chart_type": "line",
|
||||
"data_source": "mock_placeholder",
|
||||
"data_series_required": true,
|
||||
"placeholder_label_required": true,
|
||||
"manual_shape_fallback_allowed": false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
When `chart_contract.required == true`, XML generation must produce a `<chart>` element on that slide. A shape, line, polyline, or whiteboard approximation does not satisfy the plan.
|
||||
|
||||
`data_source` must be one of:
|
||||
|
||||
- `user_provided`: the user supplied concrete values, tables, CSV, or metric lists; use them and do not replace them with mock data.
|
||||
- `mock_placeholder`: the user asked for a placeholder, template, example, or later-replaceable chart position; use mock data in native `<chart>`.
|
||||
- `mock_required_by_intent`: the user did not provide concrete values but asked for data expression, charts, trends, comparisons, or distributions; use mock data in native `<chart>`.
|
||||
|
||||
`data_series_required` means the generated XML must include `<chartData>`. It does not require user-provided real-world values. When real values are unavailable but chart expression is part of the user's intent, write mock or placeholder values into native `<chart>` and label them clearly instead of switching to manual drawing primitives or metric blocks.
|
||||
|
||||
## Layout Vocabulary
|
||||
|
||||
Use one of these `layout_type` values unless the user explicitly needs a custom structure:
|
||||
@@ -211,7 +183,6 @@ Use an object for one planned asset, an array for multiple real needs, or `asset
|
||||
- `purpose`: why this asset helps the page's key message.
|
||||
- `suggested_query`: short future lookup hint only; do not execute it unless separately requested.
|
||||
- `fallback_if_missing`: concrete XML-native visual plan using shapes, labels, tables, whiteboard diagrams, or placeholder panels.
|
||||
- `chart_contract`: when `asset_type` is `chart` and the visual is a supported standard data chart, set this optional slide-level field so generation is locked to native `<chart>`.
|
||||
|
||||
For detailed rules and examples, read `asset-planning.md`.
|
||||
|
||||
@@ -219,7 +190,7 @@ Good examples:
|
||||
|
||||
- `{"asset_type":"architecture_diagram","purpose":"Explain component relationships.","suggested_query":"service architecture diagram","fallback_if_missing":"Draw a component diagram with grouped boxes, connector arrows, and short labels."}`
|
||||
- `{"asset_type":"logo","purpose":"Identify the customer context.","suggested_query":"customer logo","fallback_if_missing":"Use a text label in a small badge."}`
|
||||
- `{"asset_type":"chart","purpose":"Show adoption trend.","suggested_query":"monthly adoption trend chart","fallback_if_missing":"Render a native `<chart>` using the provided series when available; otherwise render a native `<chart>` with mock placeholder values and label it as 模拟数据,仅占位,待替换真实数据."}`
|
||||
- `{"asset_type":"chart","purpose":"Show adoption trend.","suggested_query":"monthly adoption trend chart","fallback_if_missing":"Draw a simple trend line chart with axis labels and data points."}`
|
||||
|
||||
## XML Generation Contract
|
||||
|
||||
@@ -230,7 +201,6 @@ Before writing each slide XML, map the plan fields to concrete decisions:
|
||||
- `visual_focus` determines the largest visual region or emphasized object.
|
||||
- `text_density` caps visible text volume.
|
||||
- `asset_need` informs placeholder diagrams, icons, charts, screenshots, or shape-based fallback visuals only. Missing real assets must use `fallback_if_missing`, not blank regions.
|
||||
- `chart_contract` locks supported standard data charts to native `<chart>` output. Manual approximations are allowed only when the planned chart type is unsupported by `<chart>` or when the visual is explicitly non-data/decorative.
|
||||
|
||||
After creating the PPT, fetch the presentation and verify:
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -3018,7 +3018,7 @@
|
||||
|
||||
子元素(mermaid 与 svg 二选一):
|
||||
- mermaid: Mermaid 源码文本, 可使用 CDATA 包裹
|
||||
适用场景: 流程图、时序图、思维导图、类图、甘特图、ER 图、用户旅程等结构图
|
||||
适用场景: 流程图、时序图、思维导图、类图、甘特图、饼图等结构化图表
|
||||
特点: 用简短的文本声明描述图表逻辑, 由渲染引擎自动布局, 无需手动计算坐标
|
||||
示例: <mermaid><![CDATA[flowchart TD\n A[开始] --> B[结束]]]></mermaid>
|
||||
- svg: SVG 内容
|
||||
|
||||
@@ -263,56 +263,7 @@
|
||||
- `<chartLegend>`
|
||||
- `<chartTooltip>`
|
||||
|
||||
完整图表类型覆盖示例见 [slides_chart_demo.xml](slides_chart_demo.xml),其中包含柱状、条形、折线、面积、饼 / 环、雷达等原生 `<chart>` 示例,以及散点、气泡、漏斗、帕累托、瀑布等 `<whiteboard>` SVG 图表示例。
|
||||
|
||||
组合图示例(来自 [slides_chart_demo.xml](slides_chart_demo.xml)):
|
||||
|
||||
```xml
|
||||
<chart width="556" height="350" topLeftX="42" topLeftY="132">
|
||||
<chartPlotArea>
|
||||
<chartPlot type="combo">
|
||||
<chartExtra/>
|
||||
<chartSeriesList>
|
||||
<chartSeries index="1" comboType="column"/>
|
||||
<chartSeries index="2" comboType="line" yAxisPosition="right">
|
||||
<chartTooltip format="0%"/>
|
||||
</chartSeries>
|
||||
</chartSeriesList>
|
||||
</chartPlot>
|
||||
<chartAxes>
|
||||
<chartAxis type="x">
|
||||
<chartLabel fontSize="10"/>
|
||||
</chartAxis>
|
||||
<chartAxis type="y" position="left">
|
||||
<chartGridLine color="rgb(226, 232, 240)"/>
|
||||
<chartLabel fontSize="10"/>
|
||||
</chartAxis>
|
||||
<chartAxis type="y" position="right">
|
||||
<chartLabel fontSize="10" format="0%"/>
|
||||
</chartAxis>
|
||||
</chartAxes>
|
||||
</chartPlotArea>
|
||||
<chartLegend position="bottom" fontSize="11"/>
|
||||
<chartData>
|
||||
<dim1>
|
||||
<chartField name="季度">24Q1,24Q2,24Q3,24Q4,25Q1,25Q2,25Q3,25Q4</chartField>
|
||||
</dim1>
|
||||
<dim2>
|
||||
<chartField name="营收">180,195,210,245,220,238,258,296</chartField>
|
||||
<chartField name="增速">0.08,0.12,0.15,0.18,0.22,0.22,0.23,0.21</chartField>
|
||||
</dim2>
|
||||
</chartData>
|
||||
<chartTitle fontSize="12" color="rgba(15, 30, 58, 1)" bold="true">营收(亿美元, 左轴) · 同比增速(%, 右轴)</chartTitle>
|
||||
<chartStyle>
|
||||
<chartBackground color="rgba(0, 0, 0, 0)"/>
|
||||
<chartBorder color="rgb(222, 224, 227)" width="0"/>
|
||||
<chartColorTheme>
|
||||
<color value="rgb(28, 71, 120)"/>
|
||||
<color value="rgb(240, 129, 54)"/>
|
||||
</chartColorTheme>
|
||||
</chartStyle>
|
||||
</chart>
|
||||
```
|
||||
如果要写图表 XML,建议直接以 XSD 为准,不要自行发明更简化的 chart DSL。
|
||||
|
||||
## 样式元素
|
||||
|
||||
|
||||
@@ -147,7 +147,7 @@ XSD 中的 `title`、`headline`、`sub-headline`、`body`、`caption` 主要出
|
||||
### whiteboard
|
||||
|
||||
```xml
|
||||
<!-- SVG 模式:<chart> 不支持的图表或自定义视觉、装饰元素 -->
|
||||
<!-- SVG 模式:数据图表、装饰元素 -->
|
||||
<whiteboard topLeftX="580" topLeftY="120" width="340" height="280">
|
||||
<svg xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="60" y="80" width="40" height="140" rx="3" fill="rgba(59,130,246,0.85)"/>
|
||||
|
||||
@@ -24,7 +24,7 @@ metadata:
|
||||
|
||||
## 快速决策
|
||||
|
||||
- 用户要**整理 / 盘点 / 归类 / 重构知识库、个人文档库、文档库目录或 Wiki 节点结构**,或要生成整理方案、目标目录树、移动计划时,不要只使用 Wiki 节点 API。必须先阅读 [`../lark-drive/references/lark-drive-workflow.md`](../lark-drive/references/lark-drive-workflow.md),再按其中 `Workflow Registry` 进入 [`knowledge_organize`](../lark-drive/references/lark-drive-workflow-knowledge-organize.md) workflow;该 workflow 负责 Drive / Wiki / 个人文档库的统一入口解析、资源盘点、分类计划、写前确认和结果验证。
|
||||
- 用户要**整理 / 盘点 / 归类 / 重构知识库、个人文档库、文档库目录或 Wiki 节点结构**,或要生成整理方案、目标目录树、移动计划时,不要只使用 Wiki 节点 API。必须先阅读 [`../lark-drive/references/lark-drive-workflow-knowledge-organize.md`](../lark-drive/references/lark-drive-workflow-knowledge-organize.md),该 workflow 负责 Drive / Wiki / 个人文档库的统一入口解析、资源盘点、分类计划、写前确认和结果验证。
|
||||
- 用户给的是知识库 URL(`.../wiki/<token>`),且后续要查成员/加成员/删成员:先调用 `lark-cli wiki spaces get_node --params '{"token":"<wiki_token>"}'` 获取 `space_id`,后续成员接口统一使用 `space_id`。
|
||||
- 用户要**删除**知识空间(`wiki +delete-space`)但只给了名称或 URL:**不能**把名称 / URL 原样传给 `--space-id`,必须先解析出真实 `space_id`。解析方式:
|
||||
- URL(`.../wiki/<token>`):`lark-cli wiki spaces get_node --params '{"token":"<wiki_token>"}' --format json`,读 `data.node.space_id`。
|
||||
|
||||
Reference in New Issue
Block a user