> ## Documentation Index
> Fetch the complete documentation index at: https://dev.marginnote.com.cn/llms.txt
> Use this file to discover all available pages before exploring further.

# 查询作答预览

> 以只读方式取某一趟作答中已退出的尝试的专用层中已保存的笔迹，可以附带一张有
尺寸上限的预览图。

输入要精确到趟与尝试。不要传层身份、路径或渲染范围。

`state` 的含义见 `WritingPreviewResponse`。该预览不含未保存的画布、未分层的文字与图片框。

主数据上下文还有没保存的改动时会被拒，这是写入接口里唯一表示稍后重试的错误码。

尝试记录由独立的练习插件保管，宿主另有一条判据，只允许该插件查看自己的尝试。
命令行发起的调用不受该判据约束，本机实测可以取到预览。




## OpenAPI

````yaml /openapi/plugin-api.yaml post /writing/session-preview
openapi: 3.1.0
info:
  title: 场景插件 API
  summary: 场景插件在网页面板里可以调用的接口。
  description: >
    场景插件的面板是一个网页。页面里向 `/bridge/v1` 发请求，就能读写 MarginNote 里的数据。

    请求由 MarginNote 处理，与外部命令行工具共用同一套处理代码。外部工具要带令牌，面板不必带。

    面板页面从 `mnpanel://panel/index.html` 加载，因此 `fetch('/bridge/v1' + path)`
    属于同源请求。


    面板调用受这些条件约束：


    - 请求不带令牌，本文件也不声明安全方案。

    - 页面能调用哪些接口，取决于两份名单。模板里的 `plugins` 名单和沉浸态决定面板能不能建好，
      网页面板白名单决定页面能调哪些接口。不在白名单里的请求返回 `403`，
      错误码为 `IN_PROCESS_NOT_ALLOWED`。
    - `GET /capabilities` 里的 `requiresMax` 只约束外部 Agent。面板的请求在 MarginNote 进程内处理，
      不受令牌、订阅状态与跨设备批准的限制，能否调用只看该路径是否在白名单内。
    - 写操作里的 `addon`，MarginNote 会改写为当前面板所属插件的 id；`confirm` 由 MarginNote 补上。
      页面填的值不会生效。

    用 `mn.api(path, opts)` 读取数据。该方法始终将 Response Body 按 JSON 解析，相当于

    `fetch('/bridge/v1' + path, opts).then(r => r.json())`。


    响应的内容不是 JSON 的接口，改用 `fetch`：


    - `GET /guide` 返回 Markdown 正文。

    - `GET /notes/{noteid}/render` 返回卡片长图或 PDF。

    - `GET /documents/{bookmd5}/pages/{index}/content/{contentId}` 返回图片。

    - `GET /bundles/{bundleId}/files` 带 `?path=` 时返回该文件的原始字节。


    调用失败时 Promise 不会进入 reject。MarginNote 把错误对象

    `{"error":{"code":"…","message":"…"}}` 当作 Response Body 返回，因此结果落在 resolve 里。

    判断调用结果看响应里是否带 `error`，写操作另看响应里的 `persisted`。


    接口清单、各项上限与 Request Body 结构随 MarginNote 的版本变化，运行时以 `GET /capabilities` 与

    `GET /guide` 返回的内容为准。
  version: '0.91'
servers: []
security: []
tags:
  - name: 能力与状态
    description: 读取 MarginNote 的能力、版本与当前上下文。调用其他接口之前先读这组。
  - name: 卡片与摘录
    description: 卡片的读取与写入、摘录的创建、批注、标签、元数据与留白。
  - name: 手写会话
    description: 手写层、作答纸、练习准备清单与会话记录的读取与提交。
  - name: 学习集与卡组
    description: 学习集结构、脑图树、子脑图、色板、快照与复习卡组。
  - name: 文档与页面
    description: 文档目录、页文本、页内注释面、摘录清单与同步诊断。
  - name: 导出与 Anki
    description: 文档导出计划、导出文件、Anki 导出能力、作业与产物。
  - name: 查找
    description: 跨库搜索、文件夹列表与学习集列表。
  - name: 界面与模板
    description: 读取当前界面状态与沉浸模板。
  - name: 插件辅助
    description: 面板辅助脚本、已配对设备与本机 helper 信息。
  - name: 任务包与同步诊断
    description: 拆书任务包与旧 CloudKit 同步诊断。
paths:
  /writing/session-preview:
    post:
      tags:
        - 手写会话
      summary: 查询作答预览
      description: |
        以只读方式取某一趟作答中已退出的尝试的专用层中已保存的笔迹，可以附带一张有
        尺寸上限的预览图。

        输入要精确到趟与尝试。不要传层身份、路径或渲染范围。

        `state` 的含义见 `WritingPreviewResponse`。该预览不含未保存的画布、未分层的文字与图片框。

        主数据上下文还有没保存的改动时会被拒，这是写入接口里唯一表示稍后重试的错误码。

        尝试记录由独立的练习插件保管，宿主另有一条判据，只允许该插件查看自己的尝试。
        命令行发起的调用不受该判据约束，本机实测可以取到预览。
      operationId: postWritingSessionPreview
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/WritingSessionPreviewRequest'
            examples:
              one:
                summary: 获取指定趟与尝试
                value:
                  runId: 2A1B3C4D-5E6F-4708-9A1B-2C3D4E5F6071
                  attemptId: 3B2C4D5E-6F70-4819-8A2B-3C4D5E6F7082
      responses:
        '200':
          description: 预览。
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WritingPreviewResponse'
              examples:
                assembled:
                  summary: 构造示例，未实测
                  value:
                    readOnly: true
                    scope: one-persisted-layer
                    coverage: stored-sketch-subnotes-only
                    state: no-stored-ink
                measured:
                  summary: 实测于 2026-10-07，App 0.91
                  value:
                    schemaVersion: 1
                    state: stored-ink
                    coverage: stored-sketch-subnotes-only
                    attemptId: 53bd4366-ea69-42ff-864f-efcc11cb1039
                    partCount: 1
                    topicid: 180F6ADB-5F06-4F09-9DA0-EEB6A967EF28
                    scope: one-persisted-layer
                    bridgeApiVersion: '0.91'
                    questionId: EB3BA5C0-4850-4713-9A73-BAE677983F5E
                    readOnly: true
                    runId: df8dbff4-0b24-488f-b2ff-30f844625882
                    number: 1
                    includesUnsavedCanvas: false
                    carrier: blank
                    pngBase64: >-
                      iVBORw0KGgoAAAANSUhEUgAAAeAAAACuCAIAAABhk0lqAAAAAXNSR0IArs4c……（该图
                      12191 字节，示例中省略余下部分）
        '422':
          $ref: '#/components/responses/UnprocessableEntity'
      x-codeSamples:
        - lang: javascript
          label: 获取作答预览
          source: >
            const r = await mn.api('/writing/session-preview', {
              method: 'POST',
              body: { runId, attemptId }
            });

            if (r.error && r.error.code === 'PREVIEW_SOURCE_DIRTY') {
              // 唯一明确可以稍后再来的拒绝：主数据上下文还有未保存的改动
            }

            if (r.pngBase64) document.querySelector('#shot').src =
            'data:image/png;base64,' + r.pngBase64;
components:
  schemas:
    WritingSessionPreviewRequest:
      type: object
      required:
        - runId
        - attemptId
      properties:
        windowId:
          type: string
        expectedTopicId:
          type: string
        runId:
          type: string
        attemptId:
          type: string
        windowId2:
          type: string
          description: 保留位，不使用。
    WritingPreviewResponse:
      type: object
      description: 某一趟作答中已退出的尝试所留下的笔迹预览。只读。
      properties:
        schemaVersion:
          type: integer
          description: 该响应的结构版本。
        state:
          type: string
          description: 没有笔迹时返回 `no-stored-ink`。该值仅表示该层没有保存的笔迹，不代表整道题没有内容。出现其他取值时按未知字符串处理。
          enum:
            - no-stored-ink
        scope:
          type: string
          description: 预览覆盖的范围。
        coverage:
          type: string
          description: 预览包含哪一类内容。仅包含专用层中已保存的笔迹项，不包含未分层的文字与图片框。
        readOnly:
          type: boolean
          description: 固定为 `true`。该接口不写任何内容。
        attemptId:
          type: string
          description: 该次作答的身份。
        runId:
          type: string
          description: 该次作答所属的趟。
        number:
          type: integer
          description: 该次作答在本趟里的次序。
        questionId:
          type: string
          description: 该次作答所属的题目。
        carrier:
          type: string
          description: 该次作答所在的位置。
        topicid:
          type: string
          description: 该次作答所属的学习集。
        partCount:
          type: integer
          description: 取到的笔迹项数。
        includesUnsavedCanvas:
          type: boolean
          description: 固定为 `false`。未保存的画布不在预览里。
        pngBase64:
          type: string
          description: base64 编码的预览图。有尺寸上限，超出上限时该字段不返回。
        bridgeApiVersion:
          type: string
          description: 宿主的接口版本。
    ErrorEnvelope:
      type: object
      description: >-
        失败时的 Response Body。MarginNote 将其当作正常 Response Body 返回，因此落在 resolve 里而不是
        reject 里。
      required:
        - error
      properties:
        error:
          $ref: '#/components/schemas/ErrorObject'
    ErrorObject:
      type: object
      required:
        - code
        - message
      properties:
        code:
          type: string
          description: 错误码。判断写入是否发生看该字段，不看 HTTP 状态码。
        message:
          type: string
          description: 人可读的说明。文案随 MarginNote 的版本变化，不要用该字段做分支判断。
        retryable:
          type: boolean
          description: >-
            是否明确可以稍后再试。只有外部调用的响应带该字段，面板本地调用的响应只有 `code` 与
            `message`。该布尔值表达不了结果未定，这类情况按各接口的说明处理，例如手写会话族看响应里的
            `layerLeaseUnresolved`。
        details:
          type: object
          description: >-
            附加事实。例如 `SCAN_BUSY` 带 `retryAfterSeconds`，`HASHTAG_ROUTE_REQUIRED` 带
            `writeRoute`。
          additionalProperties: true
  responses:
    UnprocessableEntity:
      description: 字段合法但组合不成立，例如缺少 `confirm`、序号越界、或写入顺序违反约定。
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
          examples:
            confirmRequired:
              summary: 没有带 confirm
              value:
                error:
                  code: CONFIRM_REQUIRED
                  message: 本次调用会改动数据，需要带 confirm。
            invalidOrder:
              summary: 写入顺序不成立
              value:
                error:
                  code: INVALID_ORDER
                  message: deleteComment 必须排在 removeHashtags 之前。

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.