feat: 完善工作流 Public API 调用能力
- 支持 JSON 文件 URL 简写与 Multipart 单请求文件上传 - 完善执行拓扑、枚举状态、节点名称、恢复校验和安全错误响应 - 增加临时上传生命周期清理并升级 MinIO SDK - 重构工作流接口调用说明弹窗的扁平响应式布局
This commit is contained in:
@@ -511,16 +511,20 @@ function resolveApiFields(row: any): ApiFieldDoc[] {
|
||||
})
|
||||
.filter(Boolean) as ApiFieldDoc[];
|
||||
}
|
||||
function buildExampleVariables(row: any) {
|
||||
function isFileApiField(field: ApiFieldDoc) {
|
||||
return (
|
||||
field.type.trim().toLowerCase() === 'file' ||
|
||||
field.type.trim().toLowerCase() === 'files'
|
||||
);
|
||||
}
|
||||
function buildExampleVariables(row: any, includeFileReferences = true) {
|
||||
const variables: Record<string, any> = {};
|
||||
for (const field of resolveApiFields(row)) {
|
||||
if (field.type === 'file') {
|
||||
variables[field.key] = [
|
||||
{
|
||||
fileName: 'example.pdf',
|
||||
filePath: 'https://example.com/example.pdf',
|
||||
},
|
||||
];
|
||||
if (isFileApiField(field)) {
|
||||
if (!includeFileReferences) {
|
||||
continue;
|
||||
}
|
||||
variables[field.key] = 'https://files.example.com/example.pdf';
|
||||
continue;
|
||||
}
|
||||
if (field.type === 'checkbox') {
|
||||
@@ -541,12 +545,101 @@ function buildRunRequestExample(row: any) {
|
||||
2,
|
||||
);
|
||||
}
|
||||
function buildRunResponseExample() {
|
||||
function quoteShellFormValue(value: string) {
|
||||
return `'${value.replaceAll(`'`, String.raw`'"'"'`)}'`;
|
||||
}
|
||||
function buildMultipartCurlExample(row: any, url: string) {
|
||||
const fileFields = resolveApiFields(row).filter((field) =>
|
||||
isFileApiField(field),
|
||||
);
|
||||
const metadata = JSON.stringify({
|
||||
id: row?.id,
|
||||
variables: buildExampleVariables(row, false),
|
||||
});
|
||||
const parts = [
|
||||
`curl --request POST '${url}'`,
|
||||
` --header 'ApiKey: <your-api-key>'`,
|
||||
` --form ${quoteShellFormValue(
|
||||
`metadata=${metadata};type=application/json`,
|
||||
)}`,
|
||||
...fileFields.map(
|
||||
(field) =>
|
||||
` --form ${quoteShellFormValue(`files.${field.key}=@/path/to/example.pdf;type=application/pdf`)}`,
|
||||
),
|
||||
];
|
||||
return parts
|
||||
.map((line, index) => (index < parts.length - 1 ? `${line} \\` : line))
|
||||
.join('\n');
|
||||
}
|
||||
function buildRunResponseExample(row: any) {
|
||||
return JSON.stringify(
|
||||
{
|
||||
errorCode: 0,
|
||||
message: '成功',
|
||||
data: '执行ID',
|
||||
workflow: {
|
||||
workflowId: String(row?.id ?? ''),
|
||||
alias: row?.alias || null,
|
||||
title: row?.title || null,
|
||||
description: row?.description || null,
|
||||
revision: row?.revision ?? null,
|
||||
publishedAt: row?.publishedAt || null,
|
||||
nodes: [
|
||||
{
|
||||
nodeId: '<节点 ID>',
|
||||
nodeType: '<节点类型>',
|
||||
nodeName: '<节点名称>',
|
||||
description: null,
|
||||
parentNodeId: null,
|
||||
definitionIndex: 0,
|
||||
topologyIndex: 0,
|
||||
topologyLevel: 0,
|
||||
inDegree: 0,
|
||||
outDegree: 1,
|
||||
startNode: true,
|
||||
endNode: false,
|
||||
predecessorNodeIds: [],
|
||||
successorNodeIds: ['<后继节点 ID>'],
|
||||
incomingEdgeIds: [],
|
||||
outgoingEdgeIds: ['<边 ID>'],
|
||||
inputParameters: [
|
||||
{
|
||||
parameterId: '<参数 ID>',
|
||||
name: 'documents',
|
||||
label: '文档',
|
||||
dataType: 'File',
|
||||
contentType: 'file',
|
||||
required: true,
|
||||
description: null,
|
||||
multipartPartName: 'files.documents',
|
||||
},
|
||||
],
|
||||
outputParameters: [],
|
||||
},
|
||||
],
|
||||
edges: [
|
||||
{
|
||||
edgeId: '<边 ID>',
|
||||
edgeType: '<边类型>',
|
||||
label: null,
|
||||
sourceNodeId: '<源节点 ID>',
|
||||
targetNodeId: '<目标节点 ID>',
|
||||
sourceHandle: null,
|
||||
targetHandle: null,
|
||||
parentNodeId: null,
|
||||
definitionIndex: 0,
|
||||
dangling: false,
|
||||
},
|
||||
],
|
||||
topologicalOrder: ['<开始节点 ID>', '<处理节点 ID>', '<结束节点 ID>'],
|
||||
topologyLevels: [
|
||||
['<开始节点 ID>'],
|
||||
['<处理节点 ID>'],
|
||||
['<结束节点 ID>'],
|
||||
],
|
||||
hasCycle: false,
|
||||
unresolvedNodeIds: [],
|
||||
},
|
||||
},
|
||||
null,
|
||||
2,
|
||||
@@ -578,11 +671,18 @@ function handleApiDocClick(e: MouseEvent) {
|
||||
if (url) copyApiContent(url);
|
||||
}
|
||||
function apiUrlLine(method: string, url: string) {
|
||||
const escaped = url.replace(/"/g, '"');
|
||||
return `\`${method}\` \`${url}\` <button class="api-url-copy-btn" data-copy="${escaped}" title="复制">
|
||||
const escaped = url.replaceAll('"', '"');
|
||||
return `\`${method}\` \`${url}\` <button type="button" class="api-url-copy-btn" data-copy="${escaped}" title="复制接口地址" aria-label="复制接口地址">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1"/></svg></button>`;
|
||||
}
|
||||
|
||||
function escapeMarkdownTableCell(value: unknown) {
|
||||
return String(value ?? '')
|
||||
.replaceAll('|', String.raw`\|`)
|
||||
.replaceAll(/\r?\n/g, '<br>');
|
||||
}
|
||||
|
||||
/* eslint-disable unicorn/prefer-single-call -- 文档按接口章节顺序增量组装,可读性优先。 */
|
||||
const apiDocMarkdown = computed(() => {
|
||||
const row = apiInstructionRow.value;
|
||||
if (!row) return '';
|
||||
@@ -625,12 +725,36 @@ const apiDocMarkdown = computed(() => {
|
||||
`异步执行工作流,立即返回执行 ID。工作流必须已发布且存在发布快照。`,
|
||||
);
|
||||
lines.push(``);
|
||||
lines.push(`### 请求体`);
|
||||
lines.push(`### JSON 请求`);
|
||||
lines.push(``);
|
||||
lines.push(
|
||||
`> 顶层 \`Content-Type\` 必须为 \`application/json\`。多数 HTTP 客户端在发送 JSON 对象时会自动设置;直接发送文本或文件内容时请显式声明。`,
|
||||
);
|
||||
lines.push(``);
|
||||
lines.push('```json');
|
||||
lines.push(buildRunRequestExample(row));
|
||||
lines.push('```');
|
||||
lines.push(``);
|
||||
if (fields.some((field) => isFileApiField(field))) {
|
||||
lines.push(`### Multipart 文件直传`);
|
||||
lines.push(``);
|
||||
lines.push(
|
||||
`文件参数可直接通过 \`multipart/form-data\` 上传,无需先获取文件地址。\`metadata\` 只承载 API 请求元数据;工作流文件统一使用 \`files.<开始节点文件参数名>\`,例如参数 \`documents\` 对应 Part \`files.documents\`。`,
|
||||
);
|
||||
lines.push(``);
|
||||
lines.push(
|
||||
`> 请勿手工添加顶层 \`Content-Type: multipart/form-data\`。由 cURL、Postman 或 Hoppscotch 根据请求体自动生成包含 boundary 的请求头;\`metadata\` Part 必须使用 \`application/json\`。`,
|
||||
);
|
||||
lines.push(``);
|
||||
lines.push('```bash');
|
||||
lines.push(buildMultipartCurlExample(row, `${baseUrl}/runAsync`));
|
||||
lines.push('```');
|
||||
lines.push(``);
|
||||
lines.push(
|
||||
`同一文件参数需要上传多个文件时,重复传入同名 \`files.<参数名>\` Part。文件 Part 可声明合法 MIME;客户端发送空值、\`Other\` 或非法值时,服务端会按扩展名推断,仍无法识别时使用 \`application/octet-stream\`。`,
|
||||
);
|
||||
lines.push(``);
|
||||
}
|
||||
|
||||
// 入参说明
|
||||
lines.push(`### 入参说明`);
|
||||
@@ -649,9 +773,15 @@ const apiDocMarkdown = computed(() => {
|
||||
lines.push(`| 参数 | 类型 | 必填 | 说明 |`);
|
||||
lines.push(`| --- | --- | --- | --- |`);
|
||||
for (const f of fields) {
|
||||
const desc = [f.label, f.description].filter(Boolean).join(' · ');
|
||||
const fileDescription = isFileApiField(f)
|
||||
? 'JSON 可直接填写 HTTP/HTTPS 文件 URL;多文件使用 URL 数组。URL 路径需包含文件名和扩展名;继续兼容 fileName + filePath 文件对象'
|
||||
: '';
|
||||
const desc = [f.label, f.description, fileDescription]
|
||||
.filter(Boolean)
|
||||
.join(' · ');
|
||||
const type = isFileApiField(f) ? 'string / string[]' : f.type;
|
||||
lines.push(
|
||||
`| ${f.key} | ${f.type} | ${f.required ? '是' : '否'} | ${desc} |`,
|
||||
`| ${escapeMarkdownTableCell(f.key)} | ${escapeMarkdownTableCell(type)} | ${f.required ? '是' : '否'} | ${escapeMarkdownTableCell(desc)} |`,
|
||||
);
|
||||
}
|
||||
lines.push(``);
|
||||
@@ -660,13 +790,17 @@ const apiDocMarkdown = computed(() => {
|
||||
lines.push(`### 响应`);
|
||||
lines.push(``);
|
||||
lines.push('```json');
|
||||
lines.push(buildRunResponseExample());
|
||||
lines.push(buildRunResponseExample(row));
|
||||
lines.push('```');
|
||||
lines.push(``);
|
||||
lines.push(
|
||||
`\`data\` 为 **执行 ID**(executeId),后续查询和恢复均需使用此 ID。`,
|
||||
);
|
||||
lines.push(``);
|
||||
lines.push(
|
||||
`\`workflow.nodes\` 提供全部公开节点信息;\`topologicalOrder\` 提供稳定的节点 ID 拓扑顺序;\`topologyLevels\` 按可并行层级分组。节点配置中的提示词、脚本和连接密钥等运行配置不会返回。`,
|
||||
);
|
||||
lines.push(``);
|
||||
lines.push(`---`);
|
||||
lines.push(``);
|
||||
|
||||
@@ -686,7 +820,7 @@ const apiDocMarkdown = computed(() => {
|
||||
JSON.stringify(
|
||||
{
|
||||
executeId: '<runAsync 返回的执行 ID>',
|
||||
nodes: [{ nodeId: '<需要查询的节点 ID>' }],
|
||||
nodes: [{ nodeId: '<runAsync.workflow.nodes[].nodeId>' }],
|
||||
},
|
||||
null,
|
||||
2,
|
||||
@@ -695,7 +829,7 @@ const apiDocMarkdown = computed(() => {
|
||||
lines.push('```');
|
||||
lines.push(``);
|
||||
lines.push(
|
||||
`> \`nodes\` 参数可选。不传则只返回工作流整体状态;需要节点详情时传入对象数组,每项至少包含 \`nodeId\`。`,
|
||||
`> \`nodes\` 参数可选。不传则只返回工作流整体状态;需要节点详情时,可将 \`runAsync.workflow.nodes\` 中的 \`nodeId\` 组成对象数组传入,服务端会从该执行实例的定义快照补齐 \`nodeName\`。`,
|
||||
);
|
||||
lines.push(``);
|
||||
lines.push(`### 响应示例`);
|
||||
@@ -708,10 +842,25 @@ const apiDocMarkdown = computed(() => {
|
||||
message: '成功',
|
||||
data: {
|
||||
executeId: 'abc5358c-a310-4caa-97ec-455062b2235e',
|
||||
status: 20,
|
||||
message: null,
|
||||
result: { output: '工作流执行结果' },
|
||||
nodes: {},
|
||||
status: 'failed',
|
||||
terminal: true,
|
||||
message: '工作流执行失败,请检查输入或稍后重试',
|
||||
result: null,
|
||||
nodes: {
|
||||
'<节点 ID>': {
|
||||
nodeId: '<节点 ID>',
|
||||
nodeName: '文档解析',
|
||||
status: 'failed',
|
||||
message: '节点执行失败,请检查输入或稍后重试',
|
||||
},
|
||||
},
|
||||
error: {
|
||||
code: 'WORKFLOW_EXECUTION_FAILED',
|
||||
message: '工作流执行失败,请检查输入或稍后重试',
|
||||
nodeId: '<节点 ID>',
|
||||
nodeName: '文档解析',
|
||||
retryable: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
null,
|
||||
@@ -722,17 +871,20 @@ const apiDocMarkdown = computed(() => {
|
||||
lines.push(``);
|
||||
lines.push(`### 状态值说明`);
|
||||
lines.push(``);
|
||||
lines.push(`| 数值 | 状态 | 说明 |`);
|
||||
lines.push(`| status | terminal | 说明 |`);
|
||||
lines.push(`| --- | --- | --- |`);
|
||||
lines.push(`| 0 | READY | 就绪,尚未开始 |`);
|
||||
lines.push(`| 1 | RUNNING | 执行中 |`);
|
||||
lines.push(`| 5 | SUSPEND | 挂起,等待确认节点恢复 |`);
|
||||
lines.push(`| 10 | ERROR | 执行异常,可能仍在重试 |`);
|
||||
lines.push(`| 20 | SUCCEEDED | 执行成功,终态 |`);
|
||||
lines.push(`| 21 | FAILED | 执行失败,终态 |`);
|
||||
lines.push(`| 22 | CANCELLED | 已取消,终态 |`);
|
||||
lines.push(`| ready | false | 就绪,尚未开始 |`);
|
||||
lines.push(`| running | false | 执行中 |`);
|
||||
lines.push(`| suspended | false | 已暂停,等待确认参数恢复 |`);
|
||||
lines.push(`| error | false | 执行异常,运行时可能仍在处理 |`);
|
||||
lines.push(`| done | true | 执行成功 |`);
|
||||
lines.push(`| failed | true | 执行失败 |`);
|
||||
lines.push(`| cancelled | true | 已取消 |`);
|
||||
lines.push(`| unknown | false | 无法识别的兼容状态 |`);
|
||||
lines.push(``);
|
||||
lines.push(`轮询可在状态值为 \`20\`、\`21\` 或 \`22\` 时结束。`);
|
||||
lines.push(
|
||||
`调用方应优先根据 \`terminal\` 判断是否结束轮询;值为 \`true\` 时,状态为 \`done\`、\`failed\` 或 \`cancelled\`。`,
|
||||
);
|
||||
lines.push(``);
|
||||
|
||||
// ---- 3. 恢复执行 ----
|
||||
@@ -743,7 +895,7 @@ const apiDocMarkdown = computed(() => {
|
||||
lines.push(apiUrlLine('POST', `${baseUrl}/resume`));
|
||||
lines.push(``);
|
||||
lines.push(
|
||||
`当工作流包含**确认节点**时,执行到该节点后状态变为 \`5(SUSPEND)\`,需要调用此接口传入确认参数后恢复执行。若工作流不包含确认节点则无需调用。`,
|
||||
`当工作流包含**确认节点**时,执行到该节点后状态变为 \`suspended\`,需要调用此接口传入确认参数后恢复执行。仅暂停中的执行实例允许恢复;若工作流不包含确认节点则无需调用。`,
|
||||
);
|
||||
lines.push(``);
|
||||
lines.push(`### 请求体`);
|
||||
@@ -769,21 +921,43 @@ const apiDocMarkdown = computed(() => {
|
||||
lines.push(`## 错误处理`);
|
||||
lines.push(``);
|
||||
lines.push(
|
||||
`当请求失败时,\`errorCode\` 不为 0,\`message\` 包含错误原因。常见错误:`,
|
||||
`请求尚未受理时会返回真实的 HTTP 4xx/5xx,\`errorCode\` 为稳定业务码,\`data.requestId\` 可用于服务端排查。请求已返回 executeId 后的节点失败通过 \`getChainStatus.data.error\` 获取。`,
|
||||
);
|
||||
lines.push(``);
|
||||
lines.push(`| 场景 | 说明 |`);
|
||||
lines.push(`| --- | --- |`);
|
||||
lines.push(`| ApiKey 无效或过期 | 检查访问令牌状态与有效期 |`);
|
||||
lines.push(`| HTTP | errorCode | 场景与处理 |`);
|
||||
lines.push(`| --- | --- | --- |`);
|
||||
lines.push(
|
||||
`| 未授权工作流 API 调用 | 在访问令牌中开启「工作流 API 调用授权」 |`,
|
||||
`| 400 | 40011 | JSON 请求体无效;文件直传请改用 multipart/form-data |`,
|
||||
);
|
||||
lines.push(
|
||||
`| 工作流尚未发布 | 仅已发布且存在发布快照的工作流可通过 API 调用 |`,
|
||||
`| 400 | 40012 | Multipart 缺少或无法解析 boundary;删除手工设置的顶层 Content-Type |`,
|
||||
);
|
||||
lines.push(`| 400 | 40013 | 缺少 metadata Part |`);
|
||||
lines.push(`| 400 | 40014 | metadata Part 不是有效 JSON |`);
|
||||
lines.push(`| 400 | 40015 | metadata.id 缺失 |`);
|
||||
lines.push(`| 400 | 40016 | files.<参数名> 与开始节点文件参数不匹配 |`);
|
||||
lines.push(
|
||||
`| 400 | 40017 | 其他工作流运行参数不合法,例如文件 URL 无法识别文件名或扩展名 |`,
|
||||
);
|
||||
lines.push(`| 401 | 40101 | 缺少 ApiKey 请求头 |`);
|
||||
lines.push(`| 401 | 40102 / 40103 | ApiKey 无效、禁用或过期 |`);
|
||||
lines.push(`| 403 | 40301 / 40302 | 缺少接口权限或工作流调用权限 |`);
|
||||
lines.push(`| 404 | 40401 | 工作流不存在、未发布或不可公开调用 |`);
|
||||
lines.push(`| 404 | 40402 | 执行记录不存在、过期或不可访问 |`);
|
||||
lines.push(`| 409 | 40901 | 当前执行状态不允许恢复 |`);
|
||||
lines.push(`| 413 | 41301 | 文件、文件数量或请求总量超限 |`);
|
||||
lines.push(`| 415 | 41501 | 顶层 Content-Type 缺失或不支持 |`);
|
||||
lines.push(`| 415 | 41502 | metadata Part 未使用 application/json |`);
|
||||
lines.push(`| 500 | 50001 | 服务端内部错误;携带 requestId 联系管理员 |`);
|
||||
lines.push(`| 503 | 50301 | 文件存储暂时不可用,可稍后重试 |`);
|
||||
lines.push(``);
|
||||
lines.push(
|
||||
`Hoppscotch 等工具若显示自动生成的 \`multipart/form-data\` 请求头,请保持自动模式,不要覆盖该请求头;覆盖后通常会丢失 boundary。`,
|
||||
);
|
||||
|
||||
return lines.join('\n');
|
||||
});
|
||||
/* eslint-enable unicorn/prefer-single-call */
|
||||
|
||||
async function submitPublishAction(row: any) {
|
||||
if (
|
||||
@@ -1189,11 +1363,26 @@ function handleHeaderButtonClick(data: any) {
|
||||
/>
|
||||
<ElDialog
|
||||
v-model="apiInstructionVisible"
|
||||
:title="$t('aiWorkflow.apiInstruction')"
|
||||
width="780px"
|
||||
width="min(960px, calc(100vw - 32px))"
|
||||
class="workflow-api-dialog"
|
||||
:footer="false"
|
||||
>
|
||||
<template #header>
|
||||
<div class="workflow-api-dialog__header">
|
||||
<h2 class="workflow-api-dialog__title">
|
||||
{{ $t('aiWorkflow.apiInstruction') }}
|
||||
</h2>
|
||||
<p
|
||||
v-if="apiInstructionRow?.title"
|
||||
class="workflow-api-dialog__context"
|
||||
>
|
||||
{{ apiInstructionRow.title }}
|
||||
<span v-if="apiInstructionRow.alias">
|
||||
· {{ apiInstructionRow.alias }}
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
<div
|
||||
v-if="apiInstructionRow"
|
||||
class="workflow-api-markdown-wrap"
|
||||
@@ -1654,13 +1843,37 @@ button.workflow-scope-chip:disabled {
|
||||
box-shadow: 0 18px 34px -28px hsl(var(--foreground) / 20%);
|
||||
}
|
||||
|
||||
.workflow-api-dialog__header {
|
||||
min-width: 0;
|
||||
padding-right: 40px;
|
||||
}
|
||||
|
||||
.workflow-api-dialog__title {
|
||||
margin: 0;
|
||||
font-size: 18px;
|
||||
font-weight: 650;
|
||||
line-height: 1.4;
|
||||
color: hsl(var(--text-strong));
|
||||
}
|
||||
|
||||
.workflow-api-dialog__context {
|
||||
margin: var(--space-1) 0 0;
|
||||
overflow: hidden;
|
||||
font-size: 13px;
|
||||
line-height: 1.5;
|
||||
color: hsl(var(--text-muted));
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.workflow-api-markdown-wrap {
|
||||
max-height: 65vh;
|
||||
padding: 0 4px;
|
||||
max-height: min(76vh, 760px);
|
||||
padding: var(--space-5) 36px var(--space-8);
|
||||
overflow-y: auto;
|
||||
font-size: 14px;
|
||||
line-height: 1.7;
|
||||
line-height: 1.75;
|
||||
color: hsl(var(--foreground));
|
||||
scrollbar-gutter: stable;
|
||||
}
|
||||
|
||||
.workflow-api-markdown-wrap::-webkit-scrollbar {
|
||||
@@ -1673,13 +1886,12 @@ button.workflow-scope-chip:disabled {
|
||||
}
|
||||
|
||||
.workflow-api-markdown-wrap :deep(h2) {
|
||||
padding-bottom: 8px;
|
||||
margin-top: 28px;
|
||||
margin-bottom: 16px;
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
color: hsl(var(--foreground));
|
||||
border-bottom: 1px solid hsl(var(--border));
|
||||
margin: 40px 0 14px;
|
||||
font-size: 20px;
|
||||
font-weight: 650;
|
||||
line-height: 1.4;
|
||||
color: hsl(var(--text-strong));
|
||||
letter-spacing: -0.01em;
|
||||
}
|
||||
|
||||
.workflow-api-markdown-wrap :deep(h2:first-child) {
|
||||
@@ -1687,92 +1899,145 @@ button.workflow-scope-chip:disabled {
|
||||
}
|
||||
|
||||
.workflow-api-markdown-wrap :deep(h3) {
|
||||
margin-top: 20px;
|
||||
margin-bottom: 10px;
|
||||
font-size: 15px;
|
||||
margin: 28px 0 10px;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: hsl(var(--foreground));
|
||||
line-height: 1.5;
|
||||
color: hsl(var(--text-strong));
|
||||
}
|
||||
|
||||
.workflow-api-markdown-wrap :deep(h4) {
|
||||
margin-top: 16px;
|
||||
margin-bottom: 8px;
|
||||
margin: 22px 0 8px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: hsl(var(--foreground));
|
||||
color: hsl(var(--text-strong));
|
||||
}
|
||||
|
||||
.workflow-api-markdown-wrap :deep(hr) {
|
||||
margin: 24px 0;
|
||||
border: none;
|
||||
border-top: 1px solid hsl(var(--border));
|
||||
display: none;
|
||||
}
|
||||
|
||||
.workflow-api-markdown-wrap :deep(p) {
|
||||
margin: 8px 0;
|
||||
margin: 8px 0 12px;
|
||||
color: hsl(var(--foreground) / 84%);
|
||||
}
|
||||
|
||||
.workflow-api-markdown-wrap :deep(ul),
|
||||
.workflow-api-markdown-wrap :deep(ol) {
|
||||
padding-left: 22px;
|
||||
margin: 10px 0 16px;
|
||||
}
|
||||
|
||||
.workflow-api-markdown-wrap :deep(code) {
|
||||
padding: 2px 6px;
|
||||
padding: 2px 5px;
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
||||
font-size: 13px;
|
||||
font-size: 0.92em;
|
||||
color: hsl(var(--foreground));
|
||||
background: hsl(var(--muted) / 50%);
|
||||
border-radius: 4px;
|
||||
overflow-wrap: anywhere;
|
||||
background: hsl(var(--surface-contrast-soft));
|
||||
border-radius: var(--space-1);
|
||||
}
|
||||
|
||||
.workflow-api-markdown-wrap :deep(pre) {
|
||||
max-height: 280px;
|
||||
padding: 14px 16px;
|
||||
margin: 10px 0;
|
||||
max-height: 340px;
|
||||
padding: var(--space-4);
|
||||
margin: 12px 0 20px;
|
||||
overflow: auto;
|
||||
background: hsl(220 14% 96%);
|
||||
border: 1px solid hsl(var(--border));
|
||||
border-radius: 8px;
|
||||
line-height: 1.65;
|
||||
background: hsl(var(--surface-subtle));
|
||||
border: 0;
|
||||
border-radius: var(--radius-toolbar);
|
||||
}
|
||||
|
||||
.workflow-api-markdown-wrap :deep(pre code) {
|
||||
padding: 0;
|
||||
padding: 0 !important;
|
||||
font-size: 12.5px;
|
||||
color: hsl(var(--foreground));
|
||||
overflow-wrap: normal;
|
||||
background: transparent;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.workflow-api-markdown-wrap :deep(.pre-md) {
|
||||
background: transparent !important;
|
||||
border: 0 !important;
|
||||
border-radius: 0 !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
.workflow-api-markdown-wrap :deep(.markdown-elxLanguage-header-div) {
|
||||
padding: 0 0 var(--space-2) !important;
|
||||
background: transparent !important;
|
||||
border: 0 !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
.workflow-api-markdown-wrap :deep(table) {
|
||||
width: 100%;
|
||||
margin: 10px 0;
|
||||
margin: 12px 0 22px;
|
||||
border-collapse: collapse;
|
||||
border: 0 !important;
|
||||
border-top: 1px solid hsl(var(--table-row-border)) !important;
|
||||
}
|
||||
|
||||
.workflow-api-markdown-wrap :deep(tr) {
|
||||
background: transparent !important;
|
||||
}
|
||||
|
||||
.workflow-api-markdown-wrap :deep(th),
|
||||
.workflow-api-markdown-wrap :deep(td) {
|
||||
padding: 8px 12px;
|
||||
padding: 10px 12px;
|
||||
font-size: 13px;
|
||||
line-height: 1.55;
|
||||
text-align: left;
|
||||
border: 1px solid hsl(var(--border));
|
||||
vertical-align: top;
|
||||
border: 0 !important;
|
||||
border-bottom: 1px solid hsl(var(--table-row-border)) !important;
|
||||
}
|
||||
|
||||
.workflow-api-markdown-wrap :deep(th) {
|
||||
font-weight: 600;
|
||||
color: hsl(var(--foreground));
|
||||
background: hsl(var(--muted) / 40%);
|
||||
color: hsl(var(--text-strong));
|
||||
background: hsl(var(--table-header-bg));
|
||||
}
|
||||
|
||||
.workflow-api-markdown-wrap :deep(td) {
|
||||
color: hsl(var(--foreground) / 85%);
|
||||
color: hsl(var(--foreground) / 82%);
|
||||
background: transparent !important;
|
||||
}
|
||||
|
||||
.workflow-api-markdown-wrap :deep(th:nth-child(1)),
|
||||
.workflow-api-markdown-wrap :deep(td:nth-child(1)) {
|
||||
width: 22%;
|
||||
}
|
||||
|
||||
.workflow-api-markdown-wrap :deep(th:nth-child(2)),
|
||||
.workflow-api-markdown-wrap :deep(td:nth-child(2)) {
|
||||
width: 20%;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.workflow-api-markdown-wrap :deep(th:nth-child(3)),
|
||||
.workflow-api-markdown-wrap :deep(td:nth-child(3)) {
|
||||
width: 10%;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.workflow-api-markdown-wrap :deep(blockquote) {
|
||||
padding: 8px 16px;
|
||||
margin: 10px 0;
|
||||
color: hsl(var(--muted-foreground));
|
||||
border-left: 3px solid hsl(var(--primary) / 40%);
|
||||
padding: 2px 0 2px var(--space-4);
|
||||
margin: 12px 0 20px;
|
||||
color: hsl(var(--text-muted));
|
||||
border-left: 2px solid hsl(var(--primary) / 55%);
|
||||
}
|
||||
|
||||
.workflow-api-markdown-wrap :deep(blockquote p) {
|
||||
margin: 0;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.workflow-api-markdown-wrap :deep(strong) {
|
||||
font-weight: 600;
|
||||
color: hsl(var(--foreground));
|
||||
color: hsl(var(--text-strong));
|
||||
}
|
||||
|
||||
.workflow-api-markdown-wrap :deep(.api-url-copy-btn) {
|
||||
@@ -1787,19 +2052,96 @@ button.workflow-scope-chip:disabled {
|
||||
vertical-align: middle;
|
||||
cursor: pointer;
|
||||
background: transparent;
|
||||
border: 1px solid hsl(var(--border));
|
||||
border-radius: 6px;
|
||||
transition: all 0.15s;
|
||||
border: 0;
|
||||
border-radius: var(--space-2);
|
||||
transition:
|
||||
color var(--motion-duration-fast) var(--motion-ease-standard),
|
||||
background-color var(--motion-duration-fast) var(--motion-ease-standard);
|
||||
}
|
||||
|
||||
.workflow-api-markdown-wrap :deep(.api-url-copy-btn:hover) {
|
||||
color: hsl(var(--primary));
|
||||
background: hsl(var(--muted) / 50%);
|
||||
border-color: hsl(var(--primary) / 40%);
|
||||
background: hsl(var(--surface-contrast-soft));
|
||||
}
|
||||
|
||||
.workflow-api-markdown-wrap :deep(.api-url-copy-btn:focus-visible) {
|
||||
color: hsl(var(--primary));
|
||||
outline: 2px solid hsl(var(--primary) / 45%);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
:global(.workflow-api-dialog.el-dialog) {
|
||||
overflow: hidden;
|
||||
background: hsl(var(--modal-surface));
|
||||
border: 1px solid hsl(var(--modal-shell-border-soft));
|
||||
border-radius: var(--radius-modal);
|
||||
box-shadow: var(--modal-shadow);
|
||||
}
|
||||
|
||||
:global(.workflow-api-dialog .el-dialog__header) {
|
||||
padding: var(--space-5) var(--space-6);
|
||||
margin-right: 0;
|
||||
border-bottom: 1px solid hsl(var(--modal-divider));
|
||||
}
|
||||
|
||||
:global(.workflow-api-dialog .el-dialog__body) {
|
||||
padding: 0 20px 20px;
|
||||
max-height: none;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
background: hsl(var(--modal-content-surface));
|
||||
}
|
||||
|
||||
@media (max-width: 767px) {
|
||||
:global(.workflow-api-dialog.el-dialog) {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: calc(100vw - 16px) !important;
|
||||
max-height: calc(
|
||||
100vh - var(--easyflow-header-height, 0px) - var(--space-4)
|
||||
);
|
||||
margin: calc(var(--easyflow-header-height, 0px) + var(--space-2)) auto
|
||||
var(--space-2) !important;
|
||||
}
|
||||
|
||||
:global(.workflow-api-dialog .el-dialog__header) {
|
||||
flex: 0 0 auto;
|
||||
padding: var(--space-4) 18px;
|
||||
}
|
||||
|
||||
:global(.workflow-api-dialog .el-dialog__body) {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1 1 auto;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.workflow-api-markdown-wrap {
|
||||
flex: 1 1 auto;
|
||||
min-height: 0;
|
||||
max-height: none;
|
||||
padding: var(--space-4) 18px var(--space-6);
|
||||
scrollbar-gutter: auto;
|
||||
}
|
||||
|
||||
.workflow-api-markdown-wrap :deep(h2) {
|
||||
margin-top: var(--space-8);
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.workflow-api-markdown-wrap :deep(h3) {
|
||||
margin-top: var(--space-6);
|
||||
}
|
||||
|
||||
.workflow-api-markdown-wrap :deep(pre) {
|
||||
max-height: 300px;
|
||||
padding: var(--space-3);
|
||||
}
|
||||
|
||||
.workflow-api-markdown-wrap :deep(table) {
|
||||
display: block;
|
||||
max-width: 100%;
|
||||
overflow-x: auto;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user