import {z} from 'zod' export type InsertSlidesOp = {op: 'insert_slides', after?: (string | null) | undefined, slides: Array<{[key: string]: unknown}>}; export type ReplaceSlideOp = {op: 'replace_slide', slideId: string, slide: {[key: string]: unknown}}; export type UpdateSlideOp = {op: 'update_slide', slideId: string, set: {[key: string]: unknown}}; export type DeleteSlidesOp = {op: 'delete_slides', slideIds: Array}; export type MoveSlidesOp = {op: 'move_slides', slideIds: Array, after?: (string | null) | undefined}; export type BlockParent = {columnId: string}; export type InsertBlocksOp = {op: 'insert_blocks', slideId: string, parent?: (BlockParent | null) | undefined, after?: (string | null) | undefined, blocks: Array<{[key: string]: unknown}>}; export type UpdateBlockOp = {op: 'update_block', slideId: string, blockId: string, set: {[key: string]: unknown}}; export type ReplaceBlockOp = {op: 'replace_block', slideId: string, blockId: string, block: {[key: string]: unknown}}; export type DeleteBlocksOp = {op: 'delete_blocks', slideId: string, blockIds: Array}; export type MoveBlockOp = {op: 'move_block', blockId: string, toSlideId: string, parent?: (BlockParent | null) | undefined, after?: (string | null) | undefined, frame?: ({[key: string]: unknown} | null) | undefined}; export type SetSlidePositioningOp = {op: 'set_slide_positioning', slideId: string, positioning: 'flow' | 'free', frames?: ({[key: string]: {[key: string]: unknown}} | null) | undefined, dropShapesAndLines?: boolean | undefined}; export type SetPageSizeOp = {op: 'set_page_size', size: '16:9' | 'a4-landscape'}; export type SetThemeOp = {op: 'set_theme', theme: {[key: string]: unknown}}; export type SetTitleOp = {op: 'set_title', title: string}; export type SetFooterOp = {op: 'set_footer', footer: {[key: string]: unknown}}; export type PresentationPatchRequest = {expectedRevision: number, ops: Array}; export type PatchApiV2PublicDocumentsPresentationsPresentationIdArgs = {presentation_id: string, workspace_id?: (string | null) | undefined, 'X-Workspace-Code'?: string | undefined, body: PresentationPatchRequest}; export type PatchApiV2PublicDocumentsPresentationsPresentationIdOptions = {baseUrl?: string | undefined, bearerAuth?: string | undefined, fetch?: typeof fetch | undefined}; export const deckPage = z.object({size: z.enum(["16:9", "a4-landscape"]).optional()}); export const deckTheme = z.object({id: z.enum(["sanka-paper", "ink", "plain"]).optional(), version: z.number().int().nullable().optional()}); export const deckFooter = z.object({pageNumbers: z.boolean().optional(), text: z.string().max(120).optional()}); export const focus = z.object({x: z.number().gte(0).lte(1), y: z.number().gte(0).lte(1)}); export const imageAccent = z.object({type: z.literal("image"), assetId: z.string(), fit: z.enum(["cover", "contain"]).optional(), focus: focus.nullable().optional(), alt: z.string().max(300).optional()}); export const toneAccent = z.object({type: z.literal("tone"), tone: z.enum(["accent", "accent-soft", "inverse", "surface"])}); export const slideAlign = z.object({x: z.enum(["start", "center"]).optional(), y: z.enum(["start", "center", "end"]).optional()}); export const frame = z.object({x: z.number().gte(-100000).lte(100000), y: z.number().gte(-100000).lte(100000), w: z.number().gt(0).lte(100000), h: z.number().gt(0).lte(100000), sizing: z.enum(["fixed", "auto"]).nullable().optional(), anchor: z.enum(["top", "middle", "bottom"]).nullable().optional(), rotation: z.number().gte(0).lt(360).nullable().optional()}); export const headingBlock = z.object({type: z.literal("heading"), id: z.string().nullable().optional(), level: z.union([z.literal(1), z.literal(2), z.literal(3)]).optional(), text: z.string().max(200), kicker: z.string().max(120).nullable().optional(), fontSize: z.number().gte(8).lte(160).nullable().optional(), frame: frame.nullable().optional()}); export const textBlock = z.object({type: z.literal("text"), id: z.string().nullable().optional(), markdown: z.string().max(4000), size: z.enum(["s", "m", "l"]).nullable().optional(), tone: z.enum(["default", "muted"]).nullable().optional(), fontSize: z.number().gte(8).lte(160).nullable().optional(), frame: frame.nullable().optional()}); export const imageBlock = z.object({type: z.literal("image"), id: z.string().nullable().optional(), assetId: z.string(), alt: z.string().max(300).optional(), caption: z.string().max(300).nullable().optional(), fit: z.enum(["cover", "contain"]).nullable().optional(), focus: focus.nullable().optional(), aspect: z.enum(["auto", "16:9", "4:3", "1:1", "3:4"]).nullable().optional(), rounded: z.boolean().nullable().optional(), frame: frame.nullable().optional()}); export const tableColumn = z.object({align: z.enum(["start", "center", "end"]).nullable().optional(), width: z.number().int().gte(1).lte(12).nullable().optional()}); export const tableBlock = z.object({type: z.literal("table"), id: z.string().nullable().optional(), columns: z.array(tableColumn), header: z.boolean().optional(), rows: z.array(z.array(z.string().max(300))), emphasizeFirstColumn: z.boolean().nullable().optional(), textScale: z.number().gte(0.5).lte(2).nullable().optional(), frame: frame.nullable().optional()}); export const iconRef = z.object({set: z.literal("lucide").optional(), name: z.string()}); export const cardItem = z.object({id: z.string().nullable().optional(), icon: iconRef.nullable().optional(), title: z.string().max(200), body: z.string().max(1000).nullable().optional(), emphasis: z.boolean().nullable().optional()}); export const cardsBlock = z.object({type: z.literal("cards"), id: z.string().nullable().optional(), items: z.array(cardItem), columns: z.unknown(), style: z.enum(["outline", "filled", "plain"]).nullable().optional(), textScale: z.number().gte(0.5).lte(2).nullable().optional(), frame: frame.nullable().optional()}); export const statItem = z.object({id: z.string().nullable().optional(), value: z.string().min(1).max(40), label: z.string().max(120), detail: z.string().max(200).nullable().optional()}); export const statsBlock = z.object({type: z.literal("stats"), id: z.string().nullable().optional(), items: z.array(statItem), textScale: z.number().gte(0.5).lte(2).nullable().optional(), frame: frame.nullable().optional()}); export const stepItem = z.object({id: z.string().nullable().optional(), title: z.string().max(120), body: z.string().max(300).nullable().optional()}); export const stepsBlock = z.object({type: z.literal("steps"), id: z.string().nullable().optional(), items: z.array(stepItem), direction: z.enum(["horizontal", "vertical"]).nullable().optional(), textScale: z.number().gte(0.5).lte(2).nullable().optional(), frame: frame.nullable().optional()}); export const timelineItem = z.object({id: z.string().nullable().optional(), date: z.string().max(40), title: z.string().max(120), body: z.string().max(300).nullable().optional(), emphasis: z.boolean().nullable().optional()}); export const timelineBlock = z.object({type: z.literal("timeline"), id: z.string().nullable().optional(), items: z.array(timelineItem), direction: z.enum(["horizontal", "vertical"]).nullable().optional(), textScale: z.number().gte(0.5).lte(2).nullable().optional(), frame: frame.nullable().optional()}); export const quoteBlock = z.object({type: z.literal("quote"), id: z.string().nullable().optional(), text: z.string().max(400), attribution: z.string().max(200).nullable().optional(), fontSize: z.number().gte(8).lte(160).nullable().optional(), frame: frame.nullable().optional()}); export const calloutBlock = z.object({type: z.literal("callout"), id: z.string().nullable().optional(), tone: z.enum(["note", "takeaway", "warning"]).optional(), markdown: z.string().max(1000), fontSize: z.number().gte(8).lte(160).nullable().optional(), frame: frame.nullable().optional()}); export const diagramBlock = z.object({type: z.literal("diagram"), id: z.string().nullable().optional(), mermaid: z.string().min(1).max(20000), alt: z.string().max(300).optional(), caption: z.string().max(300).nullable().optional(), frame: frame.nullable().optional()}); export const sankaDiagramBlock = z.object({type: z.literal("sankaDiagram"), id: z.string().nullable().optional(), diagramId: z.string(), alt: z.string().max(300).optional(), caption: z.string().max(300).nullable().optional(), fit: z.enum(["contain", "cover"]).nullable().optional(), frame: frame.nullable().optional()}); export const dividerBlock = z.object({type: z.literal("divider"), id: z.string().nullable().optional(), frame: frame.nullable().optional()}); export const column = z.object({id: z.string().nullable().optional(), width: z.number().int().gte(1).lte(11), blocks: z.array(z.discriminatedUnion("type", [headingBlock, textBlock, imageBlock, tableBlock, cardsBlock, statsBlock, stepsBlock, timelineBlock, quoteBlock, calloutBlock, diagramBlock, sankaDiagramBlock, dividerBlock])).optional()}); export const columnsBlock = z.object({type: z.literal("columns"), id: z.string().nullable().optional(), columns: z.array(column)}); export const strokeStyle = z.object({tone: z.enum(["border", "line", "accent", "text", "none"]), width: z.number().gte(0.5).lte(12).optional(), dash: z.enum(["solid", "dash", "dot"]).nullable().optional()}); export const shapeBlock = z.object({type: z.literal("shape"), id: z.string().nullable().optional(), shape: z.enum(["rect", "roundRect", "ellipse", "triangle", "rightArrow"]), fill: z.enum(["accent", "accent-soft", "surface", "inverse", "background", "none"]).nullable().optional(), stroke: strokeStyle.nullable().optional(), text: z.string().max(1000).nullable().optional(), textAlign: z.enum(["start", "center", "end"]).nullable().optional(), fontSize: z.number().gte(8).lte(160).nullable().optional(), frame: frame.nullable().optional()}); export const point = z.object({x: z.number().gte(-100000).lte(100000), y: z.number().gte(-100000).lte(100000)}); export const lineBlock = z.object({type: z.literal("line"), id: z.string().nullable().optional(), from: point, to: point, stroke: strokeStyle, startArrow: z.boolean().nullable().optional(), endArrow: z.boolean().nullable().optional()}); export const slide = z.object({id: z.string().nullable().optional(), positioning: z.enum(["flow", "free"]).optional(), layout: z.enum(["none", "accent-left", "accent-right", "accent-top", "accent-background"]).optional(), accent: z.union([imageAccent, toneAccent]).nullable().optional(), accentSize: z.enum(["s", "m", "l"]).optional(), overlay: z.enum(["dark", "light", "none"]).nullable().optional(), align: slideAlign.optional(), background: z.enum(["default", "surface", "accent-soft", "inverse"]).optional(), blocks: z.array(z.discriminatedUnion("type", [headingBlock, textBlock, imageBlock, tableBlock, cardsBlock, statsBlock, stepsBlock, timelineBlock, quoteBlock, calloutBlock, diagramBlock, sankaDiagramBlock, dividerBlock, columnsBlock, shapeBlock, lineBlock])).optional(), notes: z.string().max(10000).optional(), hidden: z.boolean().optional()}); export const deck = z.object({schema: z.literal("sanka.deck/v1").optional(), page: deckPage.optional(), theme: deckTheme.optional(), footer: deckFooter.optional(), slides: z.array(slide)}); export const presentationWarning = z.object({code: z.string(), path: z.string(), message: z.string()}); export const presentationData = z.object({id: z.string(), programId: z.string().nullable().optional(), workspaceId: z.string(), product: z.enum(["sanka", "flow"]), kind: z.literal("presentation").optional(), title: z.string(), revision: z.number().int(), deck: deck, slideCount: z.number().int(), outline: z.string(), updatedVia: z.enum(["app", "api", "mcp", "system"]), sourceRef: z.string().nullable().optional(), pinned: z.boolean().optional(), archivedAt: z.union([z.string(), z.string()]).nullable().optional(), createdById: z.number().int().nullable().optional(), updatedById: z.number().int().nullable().optional(), createdAt: z.union([z.string(), z.string()]).nullable().optional(), updatedAt: z.union([z.string(), z.string()]).nullable().optional(), appPath: z.string(), warnings: z.array(presentationWarning).optional()}); export const paginationMeta = z.object({page: z.number().int().gte(1), page_size: z.number().int().gte(1).lte(500), total: z.number().int().gte(0), next_cursor: z.string().nullable().optional()}); export const toastMessageI18n = z.object({en: z.string(), ja: z.string()}); export const toastMessage = z.object({variant: z.enum(["success", "error", "info", "warning"]), code: z.string(), message: z.string(), i18n: toastMessageI18n}); export const envelopeMeta = z.object({ctx_id: z.string(), pagination: paginationMeta.optional(), toast: toastMessage.optional()}); export const updatePublicPresentation200Envelope = z.object({success: z.literal(true), data: presentationData, meta: envelopeMeta}); export const validateResponse = async ( schema: { readonly '~standard': { readonly validate: (value: unknown) => ({ readonly value: Output; readonly issues?: undefined } | { readonly issues: ReadonlyArray<{ readonly message: string; readonly path?: ReadonlyArray | undefined }> }) | Promise<{ readonly value: Output; readonly issues?: undefined } | { readonly issues: ReadonlyArray<{ readonly message: string; readonly path?: ReadonlyArray | undefined }> }> } }, value: unknown ): Promise => { const result = await schema['~standard'].validate(value) if (result.issues !== undefined) { const detail = result.issues .map(({ message, path }) => path?.length ? `${path.map((segment) => String(typeof segment === 'object' ? segment.key : segment)).join('.')}: ${message}` : message ) .join('; ') throw new Error(`Response does not match the schema: ${detail}`, { cause: { issues: result.issues } }) } return result.value }; /** * Update Public Flow Presentation * * PATCH /v2/public/documents/presentations/{presentation_id} */ export const patchApiV2PublicDocumentsPresentationsPresentationId = async (args: PatchApiV2PublicDocumentsPresentationsPresentationIdArgs, options?: PatchApiV2PublicDocumentsPresentationsPresentationIdOptions) => { const {baseUrl = "https://api.sanka.com", fetch: fetchFn = fetch, bearerAuth} = options ?? {} const query = new URLSearchParams() const headers = new Headers() headers.set('content-type', "application/json") headers.set('accept', "application/json") if (args.workspace_id != null) query.append("workspace_id", String(args.workspace_id)) if (args['X-Workspace-Code'] != null) headers.set("X-Workspace-Code", String(args['X-Workspace-Code'])) if (bearerAuth !== undefined) headers.set('authorization', `Bearer ${bearerAuth}`) const url = `${baseUrl.replace(/\/+$/, '')}/v2/public/documents/presentations/${encodeURIComponent(String(args.presentation_id))}${query.size > 0 ? `?${query}` : ''}` const res = await fetchFn(url, {method: "PATCH", headers, body: JSON.stringify(args.body)}) if (!res.ok) throw new Error(`Request failed with status ${res.status}`, {cause: {status: res.status, body: await res.text()}}) return validateResponse(updatePublicPresentation200Envelope, await res.json()) };