本文へ移動
cccskills
無料GitHub で公開

block-print-system

Printing and PDF export for AI response blocks and messages via @ai-matrx/print. Use when adding a print button or BlockPrinter to a render block, wiring PrintOptionsDialog/usePrintOptions, captureBlockElement, or useDomCapturePrint, or when a print window shows no images or a print fails silently.

インストール方法を見る

含まれるファイル(1)

  • SKILL.md8.3 KB

SKILL.md(原文)

インストールする前に、エージェントに与えられる指示の中身を確認できます。

Block Print System

Three-tier architecture for printing AI response messages, plus the reusable printing core that other surfaces build on. The core lives in the @ai-matrx/print npm package (extracted 2026-08-29; source aidream/apps/shared/print) — chat-specific tiers live with the conversation feature.

Architecture Overview

Tier 1 — Quick Print (prose only)
  printMarkdownContent() → regex → HTML window
  File: ../aidream/apps/shared/chat/src/conversation/utils/markdown-print.ts
  Limitation: ignores all custom blocks

Tier 2 — Full Message (DOM screenshot)
  useDomCapturePrint → html2canvas + jsPDF
  File: ../aidream/apps/shared/chat/src/conversation/hooks/useDomCapturePrint.ts
  Hook returns: { captureRef, isCapturing, progress, captureAsPDF, error }
  isCapturing must be wired to the menu item (disabled state + label change)

Tier 3 — Per-Block (best quality, block owns its output)
  Print button in each block's header → either HTML template printer or DOM capture
  Dialog + hook: @ai-matrx/print/react (PrintOptionsDialog, usePrintOptions)

The BlockPrinter Interface

Source of truth: @ai-matrx/print/core (BlockPrinter, PrintVariant, PrintSetting, PrintSettings)

interface BlockPrinter {
    label: string;
    variants: PrintVariant[];
    settings?: PrintSetting[];
    print: (data: unknown, variantId?: string, settings?: PrintSettings) => void | Promise<void>;
}

interface PrintVariant { id: string; label: string; description?: string; }

// PrintSetting is a discriminated union of FOUR control types (all support
// appliesTo?: string[] — which variantIds the setting shows for):
//   { type: "boolean"; id; label; description?; defaultValue }
//   { type: "range";   id; label; fromId; toId; defaultFrom; defaultTo; min?;
//     fromPlaceholder?; toPlaceholder? }   // two number inputs; writes TWO
//     settings-map keys (fromId/toId); 0 = "no bound" by convention
//   { type: "number";  id; label; defaultValue; min?; max? }  // single numeric input
//   { type: "select";  id; label; options: {value,label}[]; defaultValue }
//     // compact segmented one-of-N row

type PrintSettings = Record<string, boolean | string | number>;

If variants.length === 0 AND settings.length === 0, usePrintOptions calls print(data) immediately with no dialog. Otherwise the PrintOptionsDialog opens first (Dialog on desktop, Drawer on mobile).

Existing Printers

Block / surfaceStrategyFile
FlashcardsBlockHTML template@ai-matrx/print/flashcards (flashcardsPrinter)
MultipleChoiceQuizHTML templatecomponents/mardown-display/blocks/quiz/quiz-printer.ts
MathProblemBlockHTML templatecomponents/mardown-display/blocks/math/math-printer.ts
DiagramBlockHTML templatecomponents/mardown-display/blocks/diagram/diagram-printer.ts
PD rating reportHTML templatefeatures/legal/wc/pd-ratings/print/pd-report-printer.ts
Performance review reportHTML templatefeatures/employee-performance-reviews/review-report.ts
QR label sheetsHTML template (registry-driven grid)@ai-matrx/print/labels (qrLabelsPrinter) — Avery + roll stock QR labels; template registry, calibration page, LabelSheetPreview (in /react) + jsPDF lane
Various display blocksDOM captureinline in component via @ai-matrx/alchemy/operate/capture (captureDocumentPdf) + kit downloadFile

Adding an HTML Template Printer

  1. Create <block-name>-printer.ts alongside the component file.
  2. Import buildPrintDocument, openPrintWindow, escapeHtml, type BlockPrinter, type PrintSettings from @ai-matrx/print/core.
  3. Define variants and optional settings arrays.
  4. In print(data, variantId = "default", settings?):
    • Cast and guard: if (!typed?.items?.length) { openPrintWindow(buildPrintDocument("<p>No data.</p>", ...), "fallback"); return; }
    • Call escapeHtml() on every user string.
    • Read settings as: const show = (settings?.showX ?? false) as boolean.
  5. In the component: call usePrintOptions(printer, data) (from @ai-matrx/print/react), wire triggerPrint to a <Printer> button, render <PrintOptionsDialog> outside (not inside) the block wrapper.

Full examples in the package source (aidream/apps/shared/print/src/): flashcards.ts (variants + boolean settings + fit-text auto-shrink), labels.ts (all four setting types, inch-exact @page geometry, data-URI images).

Adding a DOM Capture Block

Required pattern — copy exactly:

const blockContentRef = useRef<HTMLDivElement>(null);
const [isPrinting, setIsPrinting] = useState(false);

const handlePrint = useCallback(async () => {
    if (!blockContentRef.current || isPrinting) return;
    setIsPrinting(true);
    try {
        const [{ captureDocumentPdf }, { downloadFile }] = await Promise.all([
            import('@ai-matrx/alchemy/operate/capture'),
            import('@ai-matrx/kit/download'),
        ]);
        downloadFile('filename.pdf', await captureDocumentPdf(blockContentRef.current, { orientation: 'portrait' }), 'application/pdf'); // or 'landscape'
    } catch (err) {
        console.error('[BlockName] Print failed:', err);
    } finally {
        setIsPrinting(false);
    }
}, [isPrinting]);

Key rules:

  • captureDocumentPdf(element, { orientation }) returns the PDF bytes (orientation: "landscape" | "portrait"); save them with kit downloadFile. App code never imports @ai-matrx/print/pdf directly (guard: pnpm check:alchemy-doors)
  • Always await it — missing await means errors are invisible silent rejections
  • Attach ref to the content container, not the whole block shell or a stats summary
  • disabled={isPrinting} on the button; show "Saving…" label while active
  • If the block has fullscreen mode, add a print button in the fullscreen header too — the regular toolbar is hidden

Wiring Tier 2 isCapturing

In the assistant-message component:

const { captureRef, isCapturing, captureAsPDF } = useDomCapturePrint();

Pass isCapturing to the message options menu as a prop. The menu item should be disabled and show "Generating PDF…" while true.

Key Utilities

All in @ai-matrx/print (/core, /pdf, /markdown, /react). Since 0.3.0 the markdown converter and the print stylesheet ship IN the package — there is no host seam, and re-adding one is the defect this repo already made three times:

  • buildPrintDocument(bodyHtml, title?, extraStyles?) → complete <!DOCTYPE html> string
  • openPrintWindow(htmlDoc, filename?) → popup window; falls back to .html download if popup blocked. The window is a fresh unauthenticated document — inline every image as a data URI, never fetch.
  • printHtmlContent(bodyHtml, title?, extraStyles?) → shorthand combo
  • markdownToPdfBlob(markdown) (/pdf) → styled multi-page PDF Blob, no arguments beyond the markdown
  • markdownToHtml / getMarkdownStylesheet / printMarkdown (/markdown) → the shipped converter, the two default stylesheets ("document" and "article"), and the one-call print window. Brand it with tokens; never fork the sheet.
  • captureDocumentPdf(el, opts) (@ai-matrx/alchemy/operate/capture) → loads print's DOM-capture engine and returns the PDF bytes
  • Fixed-geometry sheets (@page { size: …; margin: 0 }, inch-exact cells, screen-only "100% scale, no margins" banner, FIT_TEXT auto-shrink): patterns in the package's flashcards.ts (Avery 5388) and, generalized to a template registry, labels.ts

Common Bugs to Watch For

  1. Missing await on captureDocumentPdf — errors silently swallowed
  2. ref on wrong element — attach to content, not stats/header/outer shell
  3. Print button absent in fullscreen — must add to fullscreen header explicitly
  4. Missing settings param in print() — use settings?.key ?? defaultValue pattern
  5. isCapturing not wired — users get no feedback during Tier 2 PDF generation
  6. Fetched images in a print window — the window has no auth; only data URIs render

レビュー

まだレビューはありません。使ってみた感想をお寄せください。

同じリポジトリのスキル

概要と使いどころ

Admin debug system wiring for the floating AdminIndicator's live debug data and Copy Full Context. Use when adding debug visibility to a route or feature, wiring useDebugContext or the debug panel, capturing console errors, or enabling the copy-context workflow for a page.

日本語の概要は準備中です。原文の説明を表示しています。

armanisadeghi/ai-matrx32026年10月13日 更新

Compact two-icon Copy / Copy-for-AI controls (components/agent-copy). Use when adding copy buttons to a row, card, list, or record; merging duplicate Copy/JSON/AI controls; continuing the copy rollout; or writing a Copy-for-AI payload. NOT for markdown content actions (use rich-document-actions).

日本語の概要は準備中です。原文の説明を表示しています。

armanisadeghi/ai-matrx32026年10月13日 更新

Disclosing a surface's existing fixed AI jobs in the shell's top Agents menu. Use when a page, panel, overlay, or window already runs a mandate behind a button, assist, automatic action, or mode; when the agent-disclosure guard names a file; or during a surface check.

日本語の概要は準備中です。原文の説明を表示しています。

armanisadeghi/ai-matrx32026年10月13日 更新

The watch-fix-rerun method for making an existing platform agent or automated process efficient and correct: baseline its ledger, run one unit yourself, fix the class behind every wasted call, rerun, record. Use when asked to improve, optimize, watch, or 'make efficient' an agent, a sandbox session, a sync job, or any recurring automated process.

日本語の概要は準備中です。原文の説明を表示しています。

armanisadeghi/ai-matrx32026年10月13日 更新

Redux state for agent execution and firing agent shortcuts. Use when editing features/agents/redux/, building agent UI, creating a conversation, touching assembleRequest or NDJSON stream state, wiring agent-state selectors, adding a per-conversation capability, or triggering a shortcut from a button, menu, or code (useShortcutTrigger, launchAgentExecution).

日本語の概要は準備中です。原文の説明を表示しています。

armanisadeghi/ai-matrx32026年10月13日 更新

The provision for a call site: the exhaustive menu of values that place in the code can realistically produce. Use before creating or fixing a mandate or agent that reasons about more than its own input, when deciding whether an agent can answer at all with what it is sent, or when its output 'looks right' but nobody checked it could be. NOT for building the agent (use create-agent).

日本語の概要は準備中です。原文の説明を表示しています。

armanisadeghi/ai-matrx32026年10月13日 更新

armanisadeghi のスキルをすべて見る

このスキルの問題を報告する