import { figmaRequest } from './client.ts' import { mapComment } from './models.ts' import { normalizeNodeId, parseFileKey } from './setup.ts' import type { DryRunResult, FigmaAuthInput, FigmaComment, JsonRecord } from './types.ts' import { compactRecord, optionalBoolean, optionalNumber, optionalString, requireRecord, requireString, } from './types.ts' import { requireAuthMode } from './auth.ts' export type CreateCommentInput = FigmaAuthInput & { /** File key, or a figma.com file/design/board URL. */ fileKey: string /** Comment body. */ message: string /** Parent comment id when this is a reply. */ commentId?: string /** Absolute canvas X when pinning to a file coordinate. */ x?: number /** Absolute canvas Y when pinning to a file coordinate. */ y?: number /** Node id when pinning to a layer. Hyphenated URL ids are accepted. */ nodeId?: string offsetX?: number offsetY?: number confirm?: boolean dryRun?: boolean } function commentCreatePayload(input: CreateCommentInput): { path: string data: JsonRecord } { const fileKey = parseFileKey(input.fileKey) const nodeId = optionalString(input.nodeId, 'nodeId') let clientMeta: JsonRecord | undefined if (nodeId) { clientMeta = { node_id: normalizeNodeId(nodeId), node_offset: { x: input.offsetX ?? 0, y: input.offsetY ?? 0, }, } } else if (input.x !== undefined || input.y !== undefined) { clientMeta = { x: input.x ?? 0, y: input.y ?? 0, } } return { path: `/v1/files/${encodeURIComponent(fileKey)}/comments`, data: compactRecord({ message: requireString(input.message, 'message'), comment_id: optionalString(input.commentId, 'commentId'), client_meta: clientMeta, }), } } /** * Create a Figma file comment. Requires `confirm: true`, or use `dryRun: true`. * Needs `file_comments:write`. * @example * import createComment from 'kody:@kody/figma/create-comment' * const preview = await createComment({ * fileKey: 'abc123', * message: 'Ship the new empty state.', * dryRun: true, * }) */ export async function createComment( input: CreateCommentInput, ): Promise> { const payload = commentCreatePayload(input) if (input.dryRun) { return { dryRun: true, wouldCall: { method: 'POST', ...payload } } } if (input.confirm !== true) { throw new Error( 'Creating a Figma comment requires confirm: true after explicit user approval, or dryRun: true.', ) } const fileKey = parseFileKey(input.fileKey) const result = await figmaRequest({ ...input, operation: 'comments.write', method: 'POST', path: payload.path, body: payload.data, }) return mapComment(fileKey, result.body) } /** * Create a Figma file comment. Requires `confirm: true`, or use `dryRun: true`. * @example * import createComment from 'kody:@kody/figma/create-comment' * const preview = await createComment({ fileKey: 'abc123', message: 'Looks good', dryRun: true }) */ export default async function createCommentEntrypoint( params: Partial & Record = {}, ) { const input = requireRecord(params, 'create-comment') return createComment({ fileKey: String(input.fileKey ?? input.fileUrl ?? ''), message: requireString(input.message, 'message'), commentId: optionalString(input.commentId, 'commentId'), x: optionalNumber(input.x, 'x'), y: optionalNumber(input.y, 'y'), nodeId: optionalString(input.nodeId, 'nodeId'), offsetX: optionalNumber(input.offsetX, 'offsetX'), offsetY: optionalNumber(input.offsetY, 'offsetY'), integrationName: optionalString(input.integrationName, 'integrationName'), integration: optionalString(input.integration, 'integration'), account: optionalString(input.account, 'account'), secretName: optionalString(input.secretName, 'secretName'), auth: requireAuthMode(input.auth), confirm: optionalBoolean(input.confirm, 'confirm'), dryRun: optionalBoolean(input.dryRun, 'dryRun'), }) }