jixoai-ui
the terminal design language
loading the style sheet…
Skip to content
docs · print medium: screen

docs · print

paged print — one pipeline, two exits, zero web change

打印不是另一棵树。现有文档页的网页流原样不动;打印层把内容根冻结成一份快照(live 干预 → 动画暂停 → 深克隆),把这份脱离 live tree 的克隆交给 paged.js 内核——sim 预览与直接打印共用同一份完成产物。

prepareSnapshot 事务 per-slot 帧转移 kernel-real 页码 懒加载 · SSR 零 pagedjs 白名单正式迁转

usage

The transaction

prepareSnapshot 是一个事务:预备媒介信号(sim 开关盖章 / 直接打印按所有权盖章)→ 插件干预落 live(density→sm、hue→钉缺省)→ 作用域动画捕获(只枚举 source 根 subtree,仅暂停 running 项)→ double-rAF 的 DOM-commit 屏障(断言干预 stamp 已落,失败 fail-loud)→ readiness 门(字体/图片/懒加载解除,超时预算带进度与取消)→ 深克隆 → 只染克隆的变换 → 幂等 restore token 恢复 live 动画。

the layer, assembled
import { PrintDoc, PrintControls } from '$lib/print';

<!-- the source root: medium + plugin root + the pipeline -->
<PrintDoc>
  <div data-jx-print="hide"><PrintControls /></div>
  <section id="usage"><h2>Usage</h2> … </section>
</PrintDoc>

// the controls drive TWO exits over ONE artifact:
//   sim          stamp → prepareSnapshot → preview → paper overlay
//   direct print prepareSnapshot → preview → window.print() → afterprint

protocol

Animation protocol

CSS 动画的相位按槽位转移到克隆上:delay′ = (c < d) ? (d − c) : −((c − d) mod D)——c 是捕获的 currentTime,d/D 是该槽的 computed delay/duration;写入 animation-play-state: paused + 逐槽组合的 animation-delay。WAAPI/JS 不可转移:结构化诊断行(sim 渲染 / 直接打印记 artifact metadata + console),事务继续不拒绝。

dual-slot fixture — jx-fx-a −0.8s/4s · jx-fx-b −2.7s/6.5s
pre-paused — currentTime 不被扰动
alternate — 诊断行
finished — 诊断行
WAAPI — 诊断行(不 throw)

stylesheets

Three sources, one kernel

kernel-print.css 是唯一喂给内核的站点样式(白名单表 + 意图头 + 代码换行/行号 + 目录页规则);@page 规则由 PrintPageConfig 编译;sim-shell.css 是文档侧的另一半(sim 纸张 chrome、standby 离屏、真打印时隐藏 app 根)——绝不出现在 preview() 入参里(AST gate + runtime spy 双检)。

the page grammar
const page: PrintPageConfig = {
  size: 'A4',                                            // or { width, length, unit }
  margin: { top: 18, right: 16, bottom: 18, left: 16, unit: 'mm' },
  marks: undefined,                                      // 'crop' | 'cross' | 'both'
  footer: { 'bottom-left': 'counter(page)',
            'bottom-right': 'counter(pages)' },
};
// parsePageConfig(page) validates (negatives, unknown units, illegal
// enums rejected — no @page rule emitted); compilePageCss(page)
// emits the kernel stylesheet. No string concatenation anywhere.

projection

Print verbs & the audited whitelist

hide / flatten 是受审计的白名单动词——unlayered :where() 零特异性、位置在 utilities 层之外,胜过同节点上的任何 utility。下面的探针条同时携带 display:flex、overflow:auto 与 max-block-size 三个 utility,分页产物里白名单必须全部胜出。

hide screen 可见;分页产物中 display:none 胜过 flex
flatten 产物中 overflow:visible + max-block-size:none
canvas-scroll 既有 hook:画布滚动层解绑(含 32rem/60vh 上限)
code-card-pre 既有 hook:CodeCard 的滚动口
props-table-scroll markup 合同:PropsTable 包装层强制输出

code

Line wrapping & the numbered gutter

克隆变换把纯文本 pre 拆成行 span(活 DOM 零接触);内核样式表让行换行并携带行号槽——lineNumbers 配置位随事务走。这段长样本同时 forcing 分页,让目录页的 target-counter 页码落在真页码上。

01 · print is a projection of the document tree — freeze, clone, chunk
02 · print is a projection of the document tree — freeze, clone, chunk
03 · print is a projection of the document tree — freeze, clone, chunk
04 · print is a projection of the document tree — freeze, clone, chunk
05 · print is a projection of the document tree — freeze, clone, chunk
06 · print is a projection of the document tree — freeze, clone, chunk
07 · print is a projection of the document tree — freeze, clone, chunk
08 · print is a projection of the document tree — freeze, clone, chunk
09 · print is a projection of the document tree — freeze, clone, chunk
10 · print is a projection of the document tree — freeze, clone, chunk
11 · print is a projection of the document tree — freeze, clone, chunk
12 · print is a projection of the document tree — freeze, clone, chunk
13 · print is a projection of the document tree — freeze, clone, chunk
14 · print is a projection of the document tree — freeze, clone, chunk
15 · print is a projection of the document tree — freeze, clone, chunk
16 · print is a projection of the document tree — freeze, clone, chunk
17 · print is a projection of the document tree — freeze, clone, chunk
18 · print is a projection of the document tree — freeze, clone, chunk
19 · print is a projection of the document tree — freeze, clone, chunk
20 · print is a projection of the document tree — freeze, clone, chunk
21 · print is a projection of the document tree — freeze, clone, chunk
22 · print is a projection of the document tree — freeze, clone, chunk
23 · print is a projection of the document tree — freeze, clone, chunk
24 · print is a projection of the document tree — freeze, clone, chunk
25 · print is a projection of the document tree — freeze, clone, chunk
26 · print is a projection of the document tree — freeze, clone, chunk
27 · print is a projection of the document tree — freeze, clone, chunk
28 · print is a projection of the document tree — freeze, clone, chunk
29 · print is a projection of the document tree — freeze, clone, chunk
30 · print is a projection of the document tree — freeze, clone, chunk
31 · print is a projection of the document tree — freeze, clone, chunk
32 · print is a projection of the document tree — freeze, clone, chunk
33 · print is a projection of the document tree — freeze, clone, chunk
34 · print is a projection of the document tree — freeze, clone, chunk
35 · print is a projection of the document tree — freeze, clone, chunk
36 · print is a projection of the document tree — freeze, clone, chunk
37 · print is a projection of the document tree — freeze, clone, chunk
38 · print is a projection of the document tree — freeze, clone, chunk
39 · print is a projection of the document tree — freeze, clone, chunk
40 · print is a projection of the document tree — freeze, clone, chunk
41 · print is a projection of the document tree — freeze, clone, chunk
42 · print is a projection of the document tree — freeze, clone, chunk
43 · print is a projection of the document tree — freeze, clone, chunk
44 · print is a projection of the document tree — freeze, clone, chunk
45 · print is a projection of the document tree — freeze, clone, chunk
46 · print is a projection of the document tree — freeze, clone, chunk
47 · print is a projection of the document tree — freeze, clone, chunk
48 · print is a projection of the document tree — freeze, clone, chunk
49 · print is a projection of the document tree — freeze, clone, chunk
50 · print is a projection of the document tree — freeze, clone, chunk
51 · print is a projection of the document tree — freeze, clone, chunk
52 · print is a projection of the document tree — freeze, clone, chunk
53 · print is a projection of the document tree — freeze, clone, chunk
54 · print is a projection of the document tree — freeze, clone, chunk
55 · print is a projection of the document tree — freeze, clone, chunk
56 · print is a projection of the document tree — freeze, clone, chunk

下面这张卡超过一页:屏幕上 figure 是 flex 列,投影把它放回块流——分页引擎能在卡身内断开,溢出的行流到下一页,不会被页盒裁掉。

the tall card — fragmentability fixture
function shard0(doc: Node): Node { return doc; }                
const page1 = await render(chunk, { size: 'A4' });              
// line 2: the fragment flows to the next sheet, nothing swallowed
function shard3(doc: Node): Node { return doc; }                
const page4 = await render(chunk, { size: 'A4' });              
// line 5: the fragment flows to the next sheet, nothing swallowed
function shard6(doc: Node): Node { return doc; }                
const page7 = await render(chunk, { size: 'A4' });              
// line 8: the fragment flows to the next sheet, nothing swallowed
function shard9(doc: Node): Node { return doc; }                
const page10 = await render(chunk, { size: 'A4' });             
// line 11: the fragment flows to the next sheet, nothing swallowed
function shard12(doc: Node): Node { return doc; }               
const page13 = await render(chunk, { size: 'A4' });             
// line 14: the fragment flows to the next sheet, nothing swallowed
function shard15(doc: Node): Node { return doc; }               
const page16 = await render(chunk, { size: 'A4' });             
// line 17: the fragment flows to the next sheet, nothing swallowed
function shard18(doc: Node): Node { return doc; }               
const page19 = await render(chunk, { size: 'A4' });             
// line 20: the fragment flows to the next sheet, nothing swallowed
function shard21(doc: Node): Node { return doc; }               
const page22 = await render(chunk, { size: 'A4' });             
// line 23: the fragment flows to the next sheet, nothing swallowed
function shard24(doc: Node): Node { return doc; }               
const page25 = await render(chunk, { size: 'A4' });             
// line 26: the fragment flows to the next sheet, nothing swallowed
function shard27(doc: Node): Node { return doc; }               
const page28 = await render(chunk, { size: 'A4' });             
// line 29: the fragment flows to the next sheet, nothing swallowed
function shard30(doc: Node): Node { return doc; }               
const page31 = await render(chunk, { size: 'A4' });             
// line 32: the fragment flows to the next sheet, nothing swallowed
function shard33(doc: Node): Node { return doc; }               
const page34 = await render(chunk, { size: 'A4' });             
// line 35: the fragment flows to the next sheet, nothing swallowed
function shard36(doc: Node): Node { return doc; }               
const page37 = await render(chunk, { size: 'A4' });             
// line 38: the fragment flows to the next sheet, nothing swallowed
function shard39(doc: Node): Node { return doc; }               
const page40 = await render(chunk, { size: 'A4' });             
// line 41: the fragment flows to the next sheet, nothing swallowed
function shard42(doc: Node): Node { return doc; }               
const page43 = await render(chunk, { size: 'A4' });             
// line 44: the fragment flows to the next sheet, nothing swallowed
function shard45(doc: Node): Node { return doc; }               
const page46 = await render(chunk, { size: 'A4' });             
// line 47: the fragment flows to the next sheet, nothing swallowed
function shard48(doc: Node): Node { return doc; }               
const page49 = await render(chunk, { size: 'A4' });             
// line 50: the fragment flows to the next sheet, nothing swallowed
function shard51(doc: Node): Node { return doc; }               
const page52 = await render(chunk, { size: 'A4' });             
// line 53: the fragment flows to the next sheet, nothing swallowed
function shard54(doc: Node): Node { return doc; }               
const page55 = await render(chunk, { size: 'A4' });             
// line 56: the fragment flows to the next sheet, nothing swallowed
function shard57(doc: Node): Node { return doc; }               
const page58 = await render(chunk, { size: 'A4' });             
// line 59: the fragment flows to the next sheet, nothing swallowed
function shard60(doc: Node): Node { return doc; }               
const page61 = await render(chunk, { size: 'A4' });             
// line 62: the fragment flows to the next sheet, nothing swallowed
function shard63(doc: Node): Node { return doc; }               
const page64 = await render(chunk, { size: 'A4' });             
// line 65: the fragment flows to the next sheet, nothing swallowed
function shard66(doc: Node): Node { return doc; }               
const page67 = await render(chunk, { size: 'A4' });             
// line 68: the fragment flows to the next sheet, nothing swallowed
function shard69(doc: Node): Node { return doc; }               
const page70 = await render(chunk, { size: 'A4' });             
// line 71: the fragment flows to the next sheet, nothing swallowed
function shard72(doc: Node): Node { return doc; }               
const page73 = await render(chunk, { size: 'A4' });             
// line 74: the fragment flows to the next sheet, nothing swallowed
function shard75(doc: Node): Node { return doc; }               
const page76 = await render(chunk, { size: 'A4' });             
// line 77: the fragment flows to the next sheet, nothing swallowed
function shard78(doc: Node): Node { return doc; }               
const page79 = await render(chunk, { size: 'A4' });             
// line 80: the fragment flows to the next sheet, nothing swallowed
function shard81(doc: Node): Node { return doc; }               
const page82 = await render(chunk, { size: 'A4' });             
// line 83: the fragment flows to the next sheet, nothing swallowed
function shard84(doc: Node): Node { return doc; }               
const page85 = await render(chunk, { size: 'A4' });             
// line 86: the fragment flows to the next sheet, nothing swallowed
function shard87(doc: Node): Node { return doc; }               
const page88 = await render(chunk, { size: 'A4' });             
// line 89: the fragment flows to the next sheet, nothing swallowed
function shard90(doc: Node): Node { return doc; }               
const page91 = await render(chunk, { size: 'A4' });             
// line 92: the fragment flows to the next sheet, nothing swallowed
function shard93(doc: Node): Node { return doc; }               
const page94 = await render(chunk, { size: 'A4' });             
// line 95: the fragment flows to the next sheet, nothing swallowed
function shard96(doc: Node): Node { return doc; }               
const page97 = await render(chunk, { size: 'A4' });             
// line 98: the fragment flows to the next sheet, nothing swallowed
function shard99(doc: Node): Node { return doc; }               
const page100 = await render(chunk, { size: 'A4' });            
// line 101: the fragment flows to the next sheet, nothing swallowed
function shard102(doc: Node): Node { return doc; }              
const page103 = await render(chunk, { size: 'A4' });            
// line 104: the fragment flows to the next sheet, nothing swallowed
function shard105(doc: Node): Node { return doc; }              
const page106 = await render(chunk, { size: 'A4' });            
// line 107: the fragment flows to the next sheet, nothing swallowed
function shard108(doc: Node): Node { return doc; }              
const page109 = await render(chunk, { size: 'A4' });            
// line 110: the fragment flows to the next sheet, nothing swallowed
function shard111(doc: Node): Node { return doc; }              
const page112 = await render(chunk, { size: 'A4' });            
// line 113: the fragment flows to the next sheet, nothing swallowed
function shard114(doc: Node): Node { return doc; }              
const page115 = await render(chunk, { size: 'A4' });            
// line 116: the fragment flows to the next sheet, nothing swallowed
function shard117(doc: Node): Node { return doc; }              
const page118 = await render(chunk, { size: 'A4' });            
// line 119: the fragment flows to the next sheet, nothing swallowed

toc

The injected ToC page

web 目录是站点自己的;打印目录由克隆变换注入——nav 条目 = h2[id] 序,页码经 target-counter 由内核回填为真页码。sim 态点击行为被接管(滚动到对应页),不依赖原生锚点(id 在源与产物间重复)。

打开「打印预览」后,产物首页的目录列出了本页全部小节与其真实页码——它们是分页引擎算出来的,不是估算。

determinism

Print determinism — the best-practice contract

打印面是「文档 + printConfig」的纯函数,永不依赖窗口。管线自带视口改辖域通道(宽度媒体查询与视口单位在姿势内对页面内容盒求值),这里说明它为你挡了什么、以及你自己写内容时还需要避开什么。

你仍然可以用 视口媒体查询(sm:/md:/lg:/max-* 与手写 @media)和视口单位(vw/vh/vmin/vmax,含流式字号 text-[clamp(…,Nvw,…)])——它们在 web 面的行为不受任何影响。web 应用型项目完全可以无视本节。

但不建议 让打印存活的内容依赖它们。管线分页发生在活窗口里: 尽管姿势内宽度查询已被改辖域到页面内容盒、视口单位被覆写为容器单位,仍有个别形态不可表达(height 族特征、screen 型宽度查询、被反转的怪异条件)——它们会被大声禁用并计入控制台告警,内容退回基础档。

为什么:分页测量对窗口求值、浏览器最终渲染对纸张求值,两者错位即产物漂移——同一文档在窄窗与宽窗打印出不同的断页。验收门禁是双视口差分(800×600 ≡ 1600×1200,逐字节一致);写作侧的最佳实践是:打印存活内容的自适应用容器查询(@container / cq 单位),尺寸用 rem/容器单位而非视口单位。

jixoai-ui ui.jixoai.com the jixoai design language

Theme

Brand hue

0°

Auto cycle · 24h