ai.zerowidth/napkin

ZeroWidth Napkin

Draw boards, write docs and sheets, and build decks with your brand in Napkin.

1.0.0
Version
remote
Transport
59
Tools

Security review

Review passed

Reviewed 1d ago.

  • tools: 59 tools scanned
  • metadata: scanned

No findings.

Tools (59)

  • search_docs

    Search ZeroWidth product documentation. Returns matching pages with title, slug, public URL, and a query-relevant snippet. Use this when the user asks about a ZeroWidth product (Compass, Workbench, Caliper, Prism, Ledger, Napkin, zv1), an API behavior, or a policy. No authentication required — the docs corpus is public.

  • get_doc

    Fetch the full Markdown body of a specific docs page by its slug. Use this after `search_docs` when the user needs the complete content of a page. No authentication required.

  • list_docs

    Enumerate all available docs pages, optionally filtered by product (e.g. 'compass', 'legal', 'overview'). Use this to discover what slugs exist before calling `get_doc`. No authentication required.

  • comments_list

    Lists the comment threads on one workspace entity (open first, then resolved) with authors and timestamps. Read this before weighing in on contested work — the threads are where disagreement lives before it becomes a decision.

  • comments_create

    Posts a comment on a workspace entity — a new thread, or a reply when rootId is given. Use it to leave findings where the discussion already lives (an eval result on the flow being debated, a summary on a long thread). Mention people via mentionedUserIds (from workspace member ids) to ring their notification bell; never mention someone who didn't ask to be pulled in.

  • comments_resolve

    Sets a comment thread's resolved state (rootId = the thread's root comment id). Resolve ONLY when the human asked or the thread's question is demonstrably settled — and say what settled it in a reply first. Reopening is for new evidence.

  • napkin_boards_list

    Lists sketch boards in the active workspace — name, id, kind (sketch / deck), shape count, last activity. Boards are SPATIAL canvases (drawing, diagrams); the markdown DOCUMENTS are docs — napkin_docs_list. Archived boards are hidden unless `includeArchived`; `q` narrows by name. When the user mentions a sketch, drawing, whiteboard, or napkin, find it here, then LOOK at it with `napkin_boards_view` before discussing its contents. When referring the user to a sketch in your reply, put `[[board:ID]]` alone on its own line — it renders as a clickable card with a live thumbnail.

  • napkin_boards_get

    Returns a board's shapes as data — each with its id, kind, position, size, and text — so you can TARGET one to change or remove with napkin_draw (`updates` / `deletes` take these ids and absolute board coordinates). Pen strokes come back as a point count, not the points. For what the board LOOKS like, use napkin_boards_view instead. Board ids come from napkin_boards_list.

  • napkin_boards_view

    Renders the sketch board to an image and returns it so you can SEE what's drawn — layout, arrows, handwriting-style strokes, sticky notes — not just data about it. Use this before answering any question about a sketch's contents, and cite the board when you do — `[[board:ID]]` alone on its own line embeds the sketch card in your reply.

  • napkin_boards_update

    Edits a board's details: name, description, visibility, or `archived` (true takes it out of the gallery; false brings it back — the recovery move for a board you created by mistake or the user no longer wants). Board ids come from napkin_boards_list. Boards are working material: no approval card, every change attributed.

  • napkin_deck_outline

    Returns a Napkin deck as a markdown outline — one section per slide in presentation order, with slide titles, text content (bold + bullets preserved), empty layout stubs still awaiting content, and a summary of drawn marks. This is the cheap way to know what a deck SAYS; use `napkin_slide_view` when you need to see how a specific slide LOOKS. Works on any board that has slides. Cite the deck with `[[board:ID]]` alone on its own line.

  • napkin_slide_view

    Renders a single slide of a Napkin deck to an image — exactly what present mode shows, cropped to the slide. Navigate by 1-based `slide` number in presentation order (get the map from `napkin_deck_outline` first; the result echoes `slideIndex`/`slideCount` so you can step through a deck slide by slide). Use this to check visual layout, drawings, and images that the text outline can't carry.

  • napkin_boards_create

    Creates a blank Napkin board — a sketch (free canvas) or a deck (slides). NOT for written documents: 'draft/write a doc, note, memo' is napkin_docs_create. Use when the user asks for one, or proactively when a sketch/deck would carry the conversation better than words; hand it over with `[[board:ID]]` alone on its own line. For a deck with content, prefer `napkin_deck_write` (one call, whole deck).

  • napkin_deck_write

    Creates a deck from a markdown outline — the SAME dialect `napkin_deck_outline` reads back, so any deck you've read shows the format. Decks are SLIDES for presenting; a prose document ('write this up', 'draft a doc') is napkin_docs_create instead. Rules: `## Heading` starts a slide (heading becomes the slide title and its biggest text block); optional trailing `[layout: title | section | title-body | title-lead | two-column | three-column | comparison | statement | quote | closing | blank]`; body lines fill the layout's remaining text areas in order, split into areas by a line containing only `---`; `- ` bullets are kept as bullets. Slides default to title-body (or title, when bodyless). Layout text areas you don't fill stay as visible prompts for the user. Example: ## Q3 Review [layout: title] What happened, what's next ## Revenue [layout: title-body] - up 40% QoQ - churn flat ## Bets [layout: two-column] Double down on decks --- Sunset legacy plans After writing, cite the deck with

  • napkin_slide_add

    Appends one slide to an existing deck. Pick a layout (title | section | title-body | title-lead | two-column | three-column | comparison | statement | quote | closing | blank), give the title, and optionally `content` — markdown for the layout's remaining text areas, split by `---` lines (two-column/comparison take one block per column). Check the deck first with `napkin_deck_outline`.

  • napkin_slide_fill

    Fills a slide's empty layout text areas by NAME — the names `napkin_deck_outline` surfaces as `[empty text stub: "…"]`. Pass `texts` mapping those exact names to content (markdown `- ` bullets welcome). Read the outline first to see which stubs a slide still has open.

  • napkin_draw

    Draws shapes, strokes, and labels on a board — diagram on a sketch, annotate a deck slide — and, via `updates` / `deletes`, changes or removes shapes that are already there (ids from napkin_boards_get; the fix for something you drew wrong). For NEW elements never compute absolute board coordinates: with `slide`, coordinates are slide-local — (0, 0) top-left to (960, 540) bottom-right of that slide. Without `slide` (sketches), (0, 0) is the top-left of the existing content — the same area `napkin_boards_view` renders, so place by what you saw there; on an empty board just start at (0, 0). Example — circle a slide's title and margin-note it: {"boardId": "…", "slide": 2, "elements": [ {"type": "ellipse", "x": 60, "y": 40, "w": 400, "h": 90, "color": "#dc2626"}, {"type": "arrow", "x": 560, "y": 140, "x2": 470, "y2": 90, "color": "#dc2626"}, {"type": "text", "x": 575, "y": 130, "w": 260, "text": "tighten this claim", "color": "#dc2626"} ]} After drawing, cite the board with `[[board

  • napkin_deck_set_theme

    Paints every slide of a deck with a theme — slide background, heading and body typefaces, and text colors — in one call, the same as picking it in the deck's theme menu. Font sizes are kept. Prefer "brand" (the workspace's own brand kit) unless the user asks for something else. Built-in themes: plain (Helvetica on white. Gets out of the way.); editorial (Garamond headings on cream. Reads like a document.); stage (Helvetica, light on dark, for a projected room.); blueprint (Consolas headings on mist. For technical decks.); warm (Rounded on amber. Softer than it sounds.); notebook (Marker headings. Keeps the sketchbook feel.). For one-off colors or sizes on a single shape, use napkin_draw's `updates` instead. Check the result with napkin_slide_view.

  • napkin_slide_update

    Changes one slide as a whole: move it to another position in the deck (`moveTo`, 1-based — the other slides shift around it), hide it from the show without deleting it (`skipped`), show or hide its slide number (`pageNumber`), or replace its speaker notes (`notes`, markdown). Slide numbers come from napkin_deck_outline. To change what's ON the slide, use napkin_slide_fill or napkin_draw.

  • napkin_brand_list

    Lists the workspace's brand kits: id, name, description, and which one is the workspace's brand (`isDefault`). Most workspaces have one. Use napkin_brand_get to read a kit.

  • napkin_brand_get

    Reads a brand kit — the workspace's brand unless you name another. READ THIS BEFORE writing or designing anything for the workspace: a deck, a doc, an interface, copy. Returns `brief` (the whole kit as markdown: every section — overview, voice, colors, typography, imagery, motion, and whatever else the team wrote — with the reason behind each rule; follow the reasons when a case isn't covered), `css` (the `--brand-*` variables; interfaces already link it as brand.css, so style with var(--brand-accent) etc. rather than hex values), and `tokens` (the resolved values Napkin uses). Before any kit exists it returns values from the workspace's colors with `kit: null`.

  • napkin_brand_check

    Finds words the brand says to avoid in a piece of writing, with what to say instead and why. Pass `text`, or a deck (`boardId`) or doc (`docId`) to check its words. Run it on your own drafts before handing them back.

  • napkin_brand_apply

    Restyles a piece in the brand — the workspace's brand unless you name a kit. `deck` (a board id): every slide gets the brand's background, typefaces, and text colors; sizes are kept. `interface` (an interface id): rewrites its brand.css, so anything styled with var(--brand-*) updates. `accent` makes one of the kit's colors the piece's accent — use it when the piece is about one product or campaign that has its own color in the brand (read the kit's Colors section to see which). Docs need nothing — they read the brand when exported. Check a deck afterwards with napkin_slide_view.

  • napkin_brand_draft

    Creates a new brand kit, or edits one that is NOT the workspace's brand. Use it when someone asks you to put their brand together (from their site, a deck, or what they tell you) or to try a variation. You can't change the workspace's brand itself — make a draft and tell the user they can choose it in Napkin's Brand section. A kit is a document of sections, each with `title`, `kind` (overview, voice, colors, typography, logo, imagery, motion, layout, examples, custom), markdown `body`, and `rules` ({rule, why}); colors sections hold `swatches` ({name, value: 6-digit hex, note}), typography sections hold `faces` ({name: what it's for, family: a font name or sans / serif / mono / rounded / marker, note}), voice sections hold `use` and `avoid` ({term, instead, why}), and any section can hold `assets` ({fileId: an image already in the workspace's files, name, note, backdrop: hex}) — logo versions, example images — and `links` ({url, title, note}). An examples section holds work that gets t

  • napkin_brand_fonts

    Gets the font files for a kit's typefaces from Google Fonts and keeps them in the kit, so decks, slide pictures and the canvas draw the brand's real typefaces instead of a stand-in. Only typefaces that name a family (like Poppins) and don't have files yet are fetched. Works on any kit, including the workspace's brand: it adds the files for the typefaces the kit already names and changes nothing else. A typeface Google doesn't have is reported back; the user can upload its files in the kit's typography section.

  • napkin_brand_examples

    Shows you the pictures in a kit's Examples sections — screenshots of work that gets the brand right — with each one's note on what makes it good, plus the example links. Look before you design a deck, page or interface in the brand, and hold your work to the same bar: the layouts, density, type sizes and use of color you see there.

  • napkin_brand_add_files

    Adds pictures to a brand kit section — logo versions, product marks, example screenshots, imagery. Each file comes from a public https `url` (fetched by the server) or, for a small file that isn't online, base64 `data` with a `filename`. PNG, JPEG, WebP, GIF and SVG, up to 20 MB each. Every file becomes a workspace file and an asset on the section, appended after the ones already there. Give each a `name` and a `note` saying what it's for; on logo sections set `backdrop` to the hex of the ground it's made for (#ffffff for a dark logo, #000000 for a reversed one), which is how pages pick the right version. Same rule as napkin_brand_draft: you can change any kit except the workspace's brand. Files that fail are reported and the rest still land.

  • napkin_brand_view

    Look at the pictures in a brand kit — logos, product marks, footage stills, layout references, examples — before you use them. Without `section` or `names` it lists every picture by section with its note, so you know what exists. With `section` (a section title, like Imagery) or `names` (pictures' names as the brief lists them) it shows you those pictures, up to 8 at a time. Look before you build a design around a picture: whether a photo has room for text, which part of it carries the color, and whether it suits texture inside type are things you can only tell by seeing it.

  • napkin_deck_export

    Exports a deck's slides as PNG files at each slide's real pixels — a square post at 1080×1080, a story at 1080×1920, a leaderboard at 728×90 — ready to upload to an ad platform or a social post. One slide comes back as a PNG; several as a .zip named by deck, slide and size. Hidden slides are left out. The file is saved to the workspace's files; give the user `downloadUrl` (it opens for anyone signed in to the workspace) and say it's also in their files. Slide numbers and the workspace mark aren't drawn. For a PDF or PowerPoint, the user exports from the deck's File menu.

  • napkin_layouts_list

    Lists the slide layouts napkin_deck_compose can build from — the brand kit's own first, then the built-in ones — with when to use each and the slots it takes (`needs` must be filled; `takes` are optional). Every layout adapts to every size, fits its text, and uses the brand's colors, faces and logos; the logo fills itself in. Pick by what the slide has to say: one line (statement), a number (stat), a quote, a list (points, steps), an event, a carousel (cover, inner, closing), a picture (image-top, image-full, split, corner, product-shot), or a display ad (banner).

  • napkin_deck_compose

    Builds a designed deck: each slide is a layout with its slots filled, or HTML and CSS you write; a real browser lays it out, and it becomes an ordinary Napkin deck people can edit. Use this whenever a deck should look designed — it's how to get layout, big numbers, color blocks, and pictures right. LAYOUTS FIRST - For ads, posts, carousels and title slides, start from a layout (napkin_layouts_list): give `layout` and `slots` instead of `html`. Layouts adapt to every size, fit their text, keep to each size's safe zones, and use the brand's own logos. A slide made from a layout remembers it, so when someone resizes it, it's laid out again instead of scaled. - A set of ads is one slide with `sizes`: ["1:1","4:5","9:16","1.91:1"] — each size gets its own arrangement. - Write HTML only for something no layout does. HOW TO WRITE A SLIDE - `html` is the inside of one slide: a `.slide` box 960×540 px (the deck's own size if it has one). Margins are reset; lay out with flexbox or grid, paddin

  • napkin_docs_list

    Lists the workspace's Napkin docs (quick markdown documents — drafts, notes, working text; the LINEAR surface, distinct from boards which are drawing canvases) newest first, with excerpts. Archived docs are hidden unless `includeArchived`; `q` narrows by title. Fetch one with napkin_docs_get. When referring the user to a doc in your reply, put `[[doc:ID]]` alone on its own line — it renders as a clickable card. NEVER cite a doc as [[board:ID]]; boards are sketches.

  • napkin_docs_get

    Fetch one Napkin doc's full markdown body by id (from napkin_docs_list). Read before editing — napkin_docs_update replaces or appends against the current body.

  • napkin_docs_create

    Create a Napkin doc — a markdown DOCUMENT (prose, structure, code blocks), not a drawing canvas (that's napkin_boards_create) and not slides (napkin_deck_write). Optionally pass a title and initial markdown body. Use for drafts the user asked you to start. After creating, cite it as `[[doc:ID]]` alone on its own line — it renders as a clickable card. NEVER cite it as [[board:ID]].

  • napkin_docs_update

    Edit a Napkin doc. For a change to existing prose use `edits` — a list of exact find/replace pairs, which is the SAFEST option and the one to reach for by default: it leaves everything you did not target untouched, and it fails loudly rather than clobbering a doc somebody else is typing in. `append` adds a section at the end. `body` replaces the WHOLE document and should be a last resort. Also retitle, describe, set visibility, or `archived` (true takes it out of the gallery, false brings it back). Read the doc with napkin_docs_get first — `edits` match the current text exactly. Doc ids come from napkin_docs_list.

  • napkin_sheets_list

    Lists the workspace's Napkin sheets (multi-tab spreadsheets — the small-data surface) newest first with tab/chart counts. Archived sheets are hidden unless `includeArchived`; `q` narrows by title. Inspect one with napkin_sheets_schema, then query it with napkin_sheets_query.

  • napkin_sheets_schema

    Returns a sheet's tabs as SQL table definitions — CREATE TABLE scripts with row counts (each tab is a table; its header row names the columns; formula cells contribute computed values). Read this BEFORE writing a napkin_sheets_query so your SQL matches the real tables.

  • napkin_sheets_query

    Runs a SQLite SELECT over a sheet's tabs-as-tables (get the table/column names from napkin_sheets_schema first). Full SQLite dialect: WHERE, GROUP BY, ORDER BY, JOINs across tabs, aggregates. Results cap at 200 rows. This is THE way to answer questions about a sheet's data — never eyeball cells when a query can answer precisely.

  • napkin_sheets_view_chart

    Renders one of a sheet's pinned charts against the LIVE data and returns the image so you can SEE it — the exact chart the user sees. The text part carries `imageUrl`; to show the user the chart inline in your reply, put a markdown image on its own line: `![<chart title>](imageUrl)`. Use after napkin_sheets_add_chart to confirm the chart reads well, or whenever the user asks about a chart.

  • napkin_sheets_set_cells

    Sets cells in one tab of a sheet, by A1 address — values or formulas ('=SUM(B2:B9)'). Additive and surgical: only the addressed cells change. Check napkin_sheets_schema first so you know the tab names and where data ends; put headers in row 1 when creating a new region. To build a whole small table, write header cells + data cells in one call.

  • napkin_sheets_add_chart

    Adds a LIVE chart to a sheet — its SQL re-runs against the tabs on every view, so it never goes stale. Write the query with napkin_sheets_query first to confirm the shape (first column = x axis, numeric columns = series, or set x/series explicitly). Prefer aggregated queries (GROUP BY) — a chart of raw rows is rarely the answer.

  • napkin_sheets_create

    Creates a Napkin sheet (multi-tab spreadsheet), optionally titled. Then populate it with napkin_sheets_set_cells (headers in row 1). Tell the user where it landed — the Sheets tab in Napkin.

  • napkin_sheets_update

    Edits a sheet's details: title, description, visibility, or `archived` (true takes it out of the gallery; false brings it back — the recovery move for a sheet created by mistake or no longer wanted). Cells are edited with napkin_sheets_set_cells, not here. Sheet ids come from napkin_sheets_list.

  • napkin_diagrams_list

    Lists the workspace's Napkin diagrams (interactive flowcharts — processes, decision trees, system maps) newest first. Archived diagrams are hidden unless `includeArchived`; `q` narrows by title. Fetch one's mermaid source with napkin_diagrams_get. When referring the user to a diagram in your reply, put `[[diagram:ID]]` alone on its own line — it renders as a clickable card. NEVER cite a diagram as [[board:ID]]; boards are sketches.

  • napkin_diagrams_get

    Fetch one Napkin diagram's mermaid source by id (from napkin_diagrams_list). Read before editing — napkin_diagrams_update replaces the whole source.

  • napkin_diagrams_create

    Create a Napkin diagram — an interactive FLOWCHART (process, decision tree, system map), not a doc (napkin_docs_create) or drawing canvas (napkin_boards_create). Pass the mermaid source. Use for process maps the user asked you to draw. After creating, cite it as `[[diagram:ID]]` alone on its own line — it renders as a clickable card. NEVER cite it as [[board:ID]].

  • napkin_diagrams_update

    Edit a Napkin diagram: retitle, describe, REPLACE the whole mermaid source, set visibility, or `archived` (true takes it out of the gallery, false brings it back — the recovery move for a diagram created by mistake). Read the diagram first (napkin_diagrams_get) so your new source keeps the parts the user wants. Diagram ids come from napkin_diagrams_list.

  • napkin_interfaces_list

    List the interfaces in a workspace — small self-contained screens (a custom chat, a control panel, a prototype) that run on their own origin and can call the flows they've been granted. Returns ids, names and how many flows each may run.

  • napkin_interfaces_get

    Read one interface: its name, the flows it may run (with the published version each is pinned to), and the contents of its files. READ BEFORE EDITING — napkin_interfaces_write matches text exactly against what is there now. Pass a path to read one file when the interface has several. An interface is plain HTML, CSS and JavaScript in one or more files, served from its own origin. There is NO build step and NO library: no React, no Tailwind, no CDN. A <script src> to anywhere but this origin is blocked, so write vanilla JS and put styles in a <style> block. index.html is the entry point and must exist. THE BRAND: every interface carries the workspace brand as files — link it with <link rel="stylesheet" href="brand.css"> and style with its variables: var(--brand-ink), var(--brand-muted), var(--brand-background), var(--brand-surface), var(--brand-accent), var(--brand-accent-2), var(--brand-color-<colour name>), var(--brand-font-heading), var(--brand-font-body), var(--brand-radius). Never h

  • napkin_interfaces_view

    See what an interface LOOKS like right now: the whole page, drawn from its current files at desktop width under the same rules as the live page (no web fonts, no remote pictures). Use it after every write and before saying an interface is finished: reading the code tells you what you wrote, not what it renders. Look for text that's too small or runs together, stray system fonts, emoji standing in for icons, and anything that doesn't look like the brand.

  • napkin_interfaces_create

    Create an interface — a small screen of its own the user can open and send to people. Use when someone asks for a custom chat UI, a little tool, or a prototype of how something would feel in their product. It starts with a working index.html you then edit. It can reach NOTHING in the workspace until a flow is granted with napkin_interfaces_grant. Cite it in your reply as `[[interface:ID]]` alone on its own line — it renders as a card the user can click. Do NOT paste an image URL or try to embed the picture yourself; that renders as a broken image. An interface is plain HTML, CSS and JavaScript in one or more files, served from its own origin. There is NO build step and NO library: no React, no Tailwind, no CDN. A <script src> to anywhere but this origin is blocked, so write vanilla JS and put styles in a <style> block. index.html is the entry point and must exist. THE BRAND: every interface carries the workspace brand as files — link it with <link rel="stylesheet" href="brand.css"> and

  • napkin_interfaces_write

    Write one file of an interface. Use `edits` — exact find/replace pairs — which is the SAFEST option and the one to reach for by default: it leaves everything you did not target untouched, and it fails loudly rather than clobbering a file somebody is editing. `body` replaces the WHOLE file and is for a new file or a deliberate rewrite. `deleteFile` removes one (index.html can't be removed). Read with napkin_interfaces_get first — `edits` match the current text exactly. Also rename, describe, set visibility, or archive. An interface is plain HTML, CSS and JavaScript in one or more files, served from its own origin. There is NO build step and NO library: no React, no Tailwind, no CDN. A <script src> to anywhere but this origin is blocked, so write vanilla JS and put styles in a <style> block. index.html is the entry point and must exist. THE BRAND: every interface carries the workspace brand as files — link it with <link rel="stylesheet" href="brand.css"> and style with its variables: var

  • napkin_interfaces_publish

    Save where an interface is now as a numbered version. Share links point at a published version, so what someone was sent doesn't change while work continues. Do this when the user says it's ready to show people, not after every edit.

  • napkin_interfaces_grant

    Set what an interface may run — flows and shims, each pinned to a published version. This is the interface's ONLY reach into the workspace, and it replaces the whole list, so include everything it should keep. Ids and published versions come from workbench_flows_list / workbench_shim_list and their revisions. Ask the user before widening this.

  • search_workspace

    Finds entities across every tool by name in one call — Workbench flows, Compass pages, Caliper datasets, evals, rubrics, reviews, specs and sources (apps sending agent traces), Ledger entries, Napkin sketches and decks. Use it FIRST when the user names something without saying where it lives ('the onboarding flow', 'that invoice page'); reach for a tool's own list only when you already know the tool. Each hit carries its id, kind, and workspace-relative path, so the id feeds the matching *_get tool and the path makes a link. Results only include what the user can see, and only kinds this token may read.

  • entity_tags_get

    Returns the tags on a batch of entities of one kind — the labels galleries organize by. Ids come from the kind's list/get tool or from search_workspace. Use it before entity_tags_set so you replace the full set knowingly, and to answer 'what is this filed under'. Entities the user can't see are omitted.

  • entity_tags_browse

    Without a tag: every tag in use across the workspace with how many entities carry it, most-used first — the vocabulary the team already organizes by. With a tag: everything filed under it across every tool, each with its kind, id, title, and path. Use it to reuse existing labels instead of inventing near-duplicates, and to answer 'show me everything about X' when X is a label.

  • entity_tags_set

    Replaces the FULL tag set on one entity (an empty list clears it). Read the current tags with entity_tags_get first and pass the merged list — this is not additive. Tags are lowercase letters, numbers, spaces, and hyphens; prefer labels already in use (entity_tags_browse) so the workspace's vocabulary stays small. The id comes from the kind's list/get tool or search_workspace.

  • workspace_files_save_from_chat

    Save a picture the user attached in THIS conversation to the workspace's files, in the Home folder "From chat", and get back its file id. Use it when the user wants a picture they sent you used somewhere — an interface shows it as <img src="file:<fileId>"> (CSS: url(file:<fileId>)). Pick the picture by `name`: the name in its `[attached image: <name>]` marker. Omit `name` only when the latest message with pictures has exactly one. Saving the same picture again returns the file it was already saved as. Only pictures from this conversation can be saved; a picture on a website can't be fetched, so ask the user to attach it here.