@topmindspace/tms-skills 0.1.17 → 0.1.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +39 -0
- package/README.en.md +3 -3
- package/README.md +3 -3
- package/package.json +1 -1
- package/top-ppt-html/README.en.md +2 -2
- package/top-ppt-html/README.md +1 -1
- package/top-ppt-html/SKILL.md +2 -2
- package/top-ppt-html/assets/examples/2026-09-09-architecture-graphite-dark.html +122 -48
- package/top-ppt-html/assets/examples/2026-09-09-presentation-business-blue.html +122 -48
- package/top-ppt-html/assets/examples/2026-09-09-research-mckinsey.html +122 -48
- package/top-ppt-html/assets/examples/2026-09-26-topmind-tms-skills-showcase.html +126 -52
- package/top-ppt-html/assets/examples/2026-09-26-topmind-tms-skills-showcase.model.json +3 -3
- package/top-ppt-html/assets/pptx-export.js +118 -45
- package/top-ppt-html/assets/style-gallery.html +19 -3
- package/top-ppt-html/assets/templates/architecture.html +122 -48
- package/top-ppt-html/assets/templates/engine.css +3 -2
- package/top-ppt-html/assets/templates/presentation.html +122 -48
- package/top-ppt-html/assets/templates/research.html +122 -48
- package/top-ppt-html/package-lock.json +2 -2
- package/top-ppt-html/package.json +1 -1
- package/top-ppt-html/references/chart-decision-tree.md +26 -25
- package/top-ppt-html/references/components-atoms.md +5 -4
- package/top-ppt-html/references/content-rules.md +3 -1
- package/top-ppt-html/references/failure-modes.md +29 -3
- package/top-ppt-html/references/layouts-combo.md +1 -1
- package/top-ppt-html/references/modes.md +1 -1
- package/top-ppt-html/references/page-type-matrix.md +10 -6
- package/top-ppt-html/references/pptx-export.md +3 -3
- package/top-ppt-html/scripts/build_pptx.js +101 -42
- package/top-ppt-html/scripts/cross_verify.py +84 -1
- package/top-ppt-html/scripts/extract_model.py +5 -0
- package/top-ppt-html/scripts/layout-constants.json +42 -2
- package/top-ppt-html/scripts/lib_layout_regions.js +6 -3
- package/top-ppt-html/scripts/model-schema.json +1 -0
- package/top-ppt-html/scripts/render_from_model.py +133 -17
- package/top-ppt-html/scripts/sync_runtime.py +19 -3
- package/top-ppt-html/scripts/test_feedback_gates.py +73 -0
- package/top-ppt-html/scripts/validate_pptx.py +148 -0
- package/top-ppt-html/scripts/validate_report.py +24 -0
|
@@ -817,8 +817,9 @@ color:var(--accent);font-variant-numeric:tabular-nums;line-height:1;min-width:2.
|
|
|
817
817
|
.agenda__a:hover .agenda__go{color:var(--accent);transform:translateX(4px)}
|
|
818
818
|
.agenda__go svg{width:20px;height:20px;display:block}
|
|
819
819
|
@media (min-width:900px){
|
|
820
|
-
|
|
821
|
-
.agenda--2col
|
|
820
|
+
/* R4:列优先(先左列后右列),与 PPTX Agenda 列填充一致——grid 行优先会打乱阅读顺序 */
|
|
821
|
+
.agenda--2col{display:block;column-count:2;column-gap:clamp(20px,3vw,48px)}
|
|
822
|
+
.agenda--2col .agenda__i{break-inside:avoid;border-top:1px solid var(--border-soft)}}
|
|
822
823
|
/* ── 页码指示(翻页导航) ── */
|
|
823
824
|
.pager{position:fixed;right:clamp(10px,1.6vw,26px);top:50%;transform:translateY(-50%);
|
|
824
825
|
z-index:60;display:flex;flex-direction:column;align-items:center;gap:10px}
|
|
@@ -1795,7 +1796,7 @@ window.REPORT_MODEL = {
|
|
|
1795
1796
|
|
|
1796
1797
|
/* __TOPPPT_RUNTIME_START__ */
|
|
1797
1798
|
/* ══ PPTX 导出运行时(assets/pptx-export.js 的内联副本 · 由 scripts/sync_runtime.py 注入,禁止手改) ══ */
|
|
1798
|
-
/* __TOPPPT_RUNTIME_SHA__:
|
|
1799
|
+
/* __TOPPPT_RUNTIME_SHA__:73cff2ca1dbc78a8 */
|
|
1799
1800
|
/* ═══════════════════════════════════════════════════════════════════════════
|
|
1800
1801
|
* TopPPT HTML· 浏览器端 PPTX 预览运行时
|
|
1801
1802
|
* 零依赖、随报告单文件分发;OOXML 序列化器(仅供预览,页面不导出文件)。
|
|
@@ -1954,6 +1955,15 @@ function regionOf(pageType, slotId, opts) {
|
|
|
1954
1955
|
return { x: MX, y: P.heroY || 2.9, w: CW * (P.heroW || 0.52), h: P.heroH || 1.6,
|
|
1955
1956
|
dividerX: P.dividerX, metricY0: P.metricY0, metricRowH: P.metricRowH };
|
|
1956
1957
|
}
|
|
1958
|
+
/* R3:kpi 支撑指标列——落在 dividerX 右侧(默认全宽返回会压住 hero) */
|
|
1959
|
+
if (type === 'kpi' && slot === 'metrics') {
|
|
1960
|
+
var dxK = P.dividerX != null ? P.dividerX : 0.56;
|
|
1961
|
+
return {
|
|
1962
|
+
x: MX + CW * dxK + 0.35, y: P.metricY0 || 2.95,
|
|
1963
|
+
w: CW * (1 - dxK) - 0.5,
|
|
1964
|
+
h: Math.max(0.6, bottomDefault - (P.metricY0 || 2.95)),
|
|
1965
|
+
};
|
|
1966
|
+
}
|
|
1957
1967
|
if (type === 'comparison' && (slot === 'left' || slot === 'primary' || slot === 'right')) {
|
|
1958
1968
|
var cg = P.panelGap || 0.4, pw = (CW - cg) / 2;
|
|
1959
1969
|
if (slot === 'right') return { x: MX + pw + cg, y: P.panelY || 2.55, w: pw, h: P.panelH || 3.4 };
|
|
@@ -1986,15 +1996,22 @@ function regionOf(pageType, slotId, opts) {
|
|
|
1986
1996
|
}
|
|
1987
1997
|
if (type === 'image' && slot === 'primary') {
|
|
1988
1998
|
var iy2 = Math.max(P.y || 2.3, opts.top || 0);
|
|
1989
|
-
|
|
1999
|
+
/* R2:有图注时图片不得越过 contentBottomWithNote(图注落注释带,属 annotation self) */
|
|
2000
|
+
var iBottom = (opts.bottom != null) ? opts.bottom
|
|
2001
|
+
: (opts.caption ? (L.contentBottomWithNote || 6.4) : bottomDefault);
|
|
2002
|
+
return { x: MX, y: iy2, w: CW, h: Math.max(1.2, Math.min(P.h || 4.2, iBottom - iy2)) };
|
|
1990
2003
|
}
|
|
1991
2004
|
if ((type === 'halftable') && (slot === 'left' || slot === 'primary')) {
|
|
1992
2005
|
var rs = PT.research || {};
|
|
1993
|
-
|
|
2006
|
+
var ht = opts.top != null ? opts.top : topDefault;
|
|
2007
|
+
var hb = opts.bottom != null ? opts.bottom : bottomDefault;
|
|
2008
|
+
return { x: MX, y: ht, w: CW * (rs.halfTableW || 0.52), h: Math.max(1, hb - ht) };
|
|
1994
2009
|
}
|
|
1995
2010
|
if ((type === 'halftable') && (slot === 'right' || slot === 'secondary')) {
|
|
1996
2011
|
var rs2 = PT.research || {};
|
|
1997
|
-
|
|
2012
|
+
var ht2 = opts.top != null ? opts.top : topDefault;
|
|
2013
|
+
var hb2 = opts.bottom != null ? opts.bottom : bottomDefault;
|
|
2014
|
+
return { x: MX + CW * (rs2.halfChartX || 0.57), y: ht2, w: CW * (rs2.halfChartW || 0.43), h: Math.max(1, hb2 - ht2) };
|
|
1998
2015
|
}
|
|
1999
2016
|
if (type === 'matrix' && slot === 'primary') {
|
|
2000
2017
|
var rm = PT.research || {};
|
|
@@ -2015,7 +2032,7 @@ function regionOf(pageType, slotId, opts) {
|
|
|
2015
2032
|
/* __TOPPPT_SCHEMA_START__ */
|
|
2016
2033
|
/* ── 由 scripts/sync_runtime.py 从 scripts/model-schema.json 注入 · 禁止手改 ── */
|
|
2017
2034
|
/* DSL schema 单源:浏览器端 validateModel 与 scripts/extract_model.py 消费同一份定义 */
|
|
2018
|
-
var MODEL_SCHEMA = {"version":"0.1","modes":["presentation","research","architecture"],"model":{"required":["title","sections:array","closing.title","closing.points:array"],"optional":["mode","style","theme","subtitle","meta","agenda:array"],"agendaMin":{"presentation":3,"research":3,"architecture":0},"sectionsMin":1,"sectionsRecommended":3,"agendaComfortMax":16},"commonSectionFields":["eyebrow","title","lead","soWhat","footnote","flags","image","exhibitNo","layoutPreset"],"flagsHint":"flags = 待核实/待二次修改条目字符串数组(如 '2027 增速 xx%(口径未定)');渲染为 accent 强调色标注行,提示用户核对。","imageHint":"image = 素材图片/配图占位对象。三选一必填:src(用户图,data: 内联或相对路径,零外链铁律禁 http(s))|items(多图版式:grid/compare/wall,元素 {src?, alt?, caption?, placeholder?})|placeholder:true(无图时出配图占位,原生形状渲染、pictures 不增)。其余可选:layout(full/half/bleed/grid/compare/wall,见 layout-constants.json imageSpec.layouts)、fit(cover 默认 / contain)、caption、alt、hint、ratio。","imageLayoutHint":"layout 与 HTML 版式对应:full=版心全宽图|half=左图右注(points 为右栏注解)|bleed=通栏出血|grid=多图网格(items 2/3/4/6 张)|compare=双图 A/B 对比(items 2 张)|wall=Logo 墙(items 3–12 张小图)。占位符标签文本由双引擎按 imageSpec 拼同一串,保证 A/B 通道逐页文本一致。","chartTypes":["bar","hbar","stack","stackline","line","dualline","area","donut","multidonut","pie","radar","scatter","bubble","waterfall","gauge","pareto","funnel","gantt","vsbar","progress","sparkline","slope","dumbbell","lollipop","dotplot","bulletchart","waffle","radialbar","rose","candlestick"],"chartTypesHint":"chart.type 可取值 = 本表(30 类:16 原生 + 14 形状还原)。其余 6 类复杂信息图(sankey/treemap/boxplot/network/marimekko/streamgraph)是**专属页型**而非 chart.type——它们要按列/段/节点等结构化载荷表达,故走 sections[].type。不变量:charts.registry.types == chartTypes ∪ 上述 6 类页型(sync_runtime.py 校验)。","chartDataTable":{"enum":["notes","inline","appendix","off"],"default":"notes","rule":"非原生图表(registry.pptx=shape)的 dataTable 不得为 off;缺失时按 registry 默认值补齐。"},"pageTypes":{"points":{"label":"要点列表页","modes":["presentation","research","architecture"],"required":["points:array"],"optional":["metrics:array"]},"metrics":{"label":"指标带页(4–6 个核心数字)","modes":["presentation","research"],"required":["metrics:array"]},"kpi":{"label":"大数指标页(hero 大数字 + 支撑指标行)","modes":["presentation","research"],"required":["hero:array"],"optional":["metrics:array"]},"table":{"label":"对比表页","modes":["presentation","research","architecture"],"required":["table.head:array","table.rows:array"],"optional":["table.colW:array"]},"timeline":{"label":"时间线 / 路线页","modes":["presentation","research"],"required":["phases:array"]},"steps":{"label":"步骤条页(N 步横排 + 箭头,可分组)","modes":["presentation","research","architecture"],"required":["steps:array"],"optional":["groups:array","note"]},"bar":{"label":"图表页(chart.type 见 chartTypes;native 通道走原生数据图表,shape 通道走高保真形状还原 + 数据表)","modes":["presentation","research"],"required":["chart.labels:array","chart.values:array"],"optional":["chart.type","chart.series:array","chart.points:array","chart.max","chart.unit","chart.colors:array","chart.start:array","chart.target:array","chart.dataTable","note"]},"donut":{"label":"环形图页(原生 pie 楔形 + 图例)","modes":["presentation","research"],"required":["chart.labels:array","chart.values:array"],"optional":["chart.unit","chart.centerLabel","chart.colors:array","chart.dataTable","note"]},"heatmap":{"label":"热力矩阵页(行 × 列 + 强度色阶)","modes":["presentation","research"],"required":["rowHeads:array","colHeads:array","cells:array"],"optional":["unit","scaleLabel:array","note"]},"bullet":{"label":"达成对比页(实际 vs 目标条)","modes":["presentation","research"],"required":["items:array"],"optional":["unit","max","note"]},"pyramid":{"label":"金字塔页(层级递进,顶层最窄)","modes":["presentation","research","architecture"],"required":["levels:array"],"optional":["note"]},"image":{"label":"素材图片页(full/half/bleed/grid/compare/wall,效果与位置锁定;支持配图占位)","modes":["presentation","research","architecture"],"required":["anyof:image.src:str|image.items:array|image.placeholder"],"optional":["image.caption","image.alt","image.layout","image.fit","image.ratio","image.hint","points:array","note"]},"cards":{"label":"卡片网格页(cards 元素须为 {title, points:[[k,v]…]},不接受元组)","modes":["presentation","research"],"required":["cards:array"],"optional":["columns"]},"split":{"label":"双区组合页(left / right 各可为 要点|图表|表格|图片:type = points(默认)| table | image | 图表类型名;图表/表格字段直接挂在对应侧)","modes":["presentation","research"],"required":["anyof:left.points:array|left.type:str"],"optional":["left.cap","left.image","left.labels:array","left.values:array","left.series:array","left.points:array","left.max","left.unit","left.colors:array","left.dataTable","left.head:array","left.rows:array","left.colW:array","right.type","right.cap","right.image","right.labels:array","right.values:array","right.series:array","right.points:array","right.max","right.unit","right.colors:array","right.dataTable","right.head:array","right.rows:array","right.colW:array"]},"comparison":{"label":"对比页(左右双栏 + 可选结论条)","modes":["presentation","research"],"required":["left.title","left.points:array","right.title","right.points:array"],"optional":["verdict"]},"quote":{"label":"引用 / 金句页(深色全幅)","modes":["presentation","research"],"required":["quote","author"],"optional":["context"]},"diagram":{"label":"分层架构页(architecture 模式走全幅几何)","modes":["presentation","research","architecture"],"required":["layers:array"],"optional":["legend:array"]},"exhibit":{"label":"Exhibit 编号图表页(research R2)","modes":["research"],"required":["chart.labels:array","chart.values:array"],"optional":["exhibitNo","chart.type","chart.series:array","chart.points:array","chart.max","chart.unit","chart.colors:array","chart.dataTable","note"]},"twocol":{"label":"双栏论证页(research R1)","modes":["research"],"required":["paragraphs:array"]},"threecol":{"label":"三栏证据页(research R6)","modes":["research"],"required":["paragraphs:array"]},"halftable":{"label":"半表半图页(research R7)","modes":["research"],"required":["table.head:array","table.rows:array","chart.labels:array","chart.values:array"],"optional":["table.colW:array","chart.type","chart.series:array","chart.points:array","chart.max","chart.unit","chart.colors:array","chart.dataTable","note"]},"matrix":{"label":"矩阵图页(research R8)","modes":["research"],"required":["rowHeads:array","colHeads:array","cells:array"]},"lane":{"label":"泳道页(architecture A2)","modes":["architecture"],"required":["lanes:array"]},"sankey":{"label":"桑基图页(节点-流带,流向与流量)","modes":["research","architecture"],"required":["flows:array"],"optional":["unit","chart.dataTable","note"]},"treemap":{"label":"树图页(面积编码的层级构成)","modes":["research","architecture"],"required":["items:array"],"optional":["unit","chart.dataTable","note"]},"boxplot":{"label":"箱线图页(分布对比:min/q1/median/q3/max)","modes":["research","architecture"],"required":["groups:array"],"optional":["unit","chart.dataTable","note"]},"network":{"label":"关系网络页(节点-边拓扑)","modes":["research","architecture"],"required":["nodes:array","edges:array"],"optional":["chart.dataTable","note"]},"marimekko":{"label":"马赛克图页(列宽 × 列高双重编码)","modes":["research","architecture"],"required":["cols:array","cells:array"],"optional":["unit","chart.dataTable","note"]},"streamgraph":{"label":"流带图页(时间上的构成演变)","modes":["research","architecture"],"required":["series:array"],"optional":["labels:array","chart.dataTable","note"]}}};
|
|
2035
|
+
var MODEL_SCHEMA = {"version":"0.1","modes":["presentation","research","architecture"],"model":{"required":["title","sections:array","closing.title","closing.points:array"],"optional":["mode","style","theme","subtitle","meta","agenda:array"],"agendaMin":{"presentation":3,"research":3,"architecture":0},"sectionsMin":1,"sectionsRecommended":3,"agendaComfortMax":16},"commonSectionFields":["eyebrow","title","lead","soWhat","footnote","flags","image","exhibitNo","layoutPreset"],"mutualExclusionHint":"verdict 与 soWhat 互斥:二者共用 annotation 结论条槽位,同页双填必叠印。comparison 页填 verdict 时不要再填 soWhat(渲染器已自动二选一,但模型侧应只给一个)。","flagsHint":"flags = 待核实/待二次修改条目字符串数组(如 '2027 增速 xx%(口径未定)');渲染为 accent 强调色标注行,提示用户核对。","imageHint":"image = 素材图片/配图占位对象。三选一必填:src(用户图,data: 内联或相对路径,零外链铁律禁 http(s))|items(多图版式:grid/compare/wall,元素 {src?, alt?, caption?, placeholder?})|placeholder:true(无图时出配图占位,原生形状渲染、pictures 不增)。其余可选:layout(full/half/bleed/grid/compare/wall,见 layout-constants.json imageSpec.layouts)、fit(cover 默认 / contain)、caption、alt、hint、ratio。","imageLayoutHint":"layout 与 HTML 版式对应:full=版心全宽图|half=左图右注(points 为右栏注解)|bleed=通栏出血|grid=多图网格(items 2/3/4/6 张)|compare=双图 A/B 对比(items 2 张)|wall=Logo 墙(items 3–12 张小图)。占位符标签文本由双引擎按 imageSpec 拼同一串,保证 A/B 通道逐页文本一致。","chartTypes":["bar","hbar","stack","stackline","line","dualline","area","donut","multidonut","pie","radar","scatter","bubble","waterfall","gauge","pareto","funnel","gantt","vsbar","progress","sparkline","slope","dumbbell","lollipop","dotplot","bulletchart","waffle","radialbar","rose","candlestick"],"chartTypesHint":"chart.type 可取值 = 本表(30 类:16 原生 + 14 形状还原)。其余 6 类复杂信息图(sankey/treemap/boxplot/network/marimekko/streamgraph)是**专属页型**而非 chart.type——它们要按列/段/节点等结构化载荷表达,故走 sections[].type。不变量:charts.registry.types == chartTypes ∪ 上述 6 类页型(sync_runtime.py 校验)。","chartDataTable":{"enum":["notes","inline","appendix","off"],"default":"notes","rule":"非原生图表(registry.pptx=shape)的 dataTable 不得为 off;缺失时按 registry 默认值补齐。"},"pageTypes":{"points":{"label":"要点列表页","modes":["presentation","research","architecture"],"required":["points:array"],"optional":["metrics:array"]},"metrics":{"label":"指标带页(4–6 个核心数字)","modes":["presentation","research"],"required":["metrics:array"]},"kpi":{"label":"大数指标页(hero 大数字 + 支撑指标行)","modes":["presentation","research"],"required":["hero:array"],"optional":["metrics:array"]},"table":{"label":"对比表页","modes":["presentation","research","architecture"],"required":["table.head:array","table.rows:array"],"optional":["table.colW:array"]},"timeline":{"label":"时间线 / 路线页","modes":["presentation","research"],"required":["phases:array"]},"steps":{"label":"步骤条页(N 步横排 + 箭头,可分组)","modes":["presentation","research","architecture"],"required":["steps:array"],"optional":["groups:array","note"]},"bar":{"label":"图表页(chart.type 见 chartTypes;native 通道走原生数据图表,shape 通道走高保真形状还原 + 数据表)","modes":["presentation","research"],"required":["chart.labels:array","chart.values:array"],"optional":["chart.type","chart.series:array","chart.points:array","chart.max","chart.unit","chart.colors:array","chart.start:array","chart.target:array","chart.dataTable","note"]},"donut":{"label":"环形图页(原生 pie 楔形 + 图例)","modes":["presentation","research"],"required":["chart.labels:array","chart.values:array"],"optional":["chart.unit","chart.centerLabel","chart.colors:array","chart.dataTable","note"]},"heatmap":{"label":"热力矩阵页(行 × 列 + 强度色阶)","modes":["presentation","research"],"required":["rowHeads:array","colHeads:array","cells:array"],"optional":["unit","scaleLabel:array","note"]},"bullet":{"label":"达成对比页(实际 vs 目标条)","modes":["presentation","research"],"required":["items:array"],"optional":["unit","max","note"]},"pyramid":{"label":"金字塔页(层级递进,顶层最窄)","modes":["presentation","research","architecture"],"required":["levels:array"],"optional":["note"]},"image":{"label":"素材图片页(full/half/bleed/grid/compare/wall,效果与位置锁定;支持配图占位)","modes":["presentation","research","architecture"],"required":["anyof:image.src:str|image.items:array|image.placeholder"],"optional":["image.caption","image.alt","image.layout","image.fit","image.ratio","image.hint","points:array","note"]},"cards":{"label":"卡片网格页(cards 元素须为 {title, points:[[k,v]…]},不接受元组)","modes":["presentation","research"],"required":["cards:array"],"optional":["columns"]},"split":{"label":"双区组合页(left / right 各可为 要点|图表|表格|图片:type = points(默认)| table | image | 图表类型名;图表/表格字段直接挂在对应侧)","modes":["presentation","research"],"required":["anyof:left.points:array|left.type:str"],"optional":["left.cap","left.image","left.labels:array","left.values:array","left.series:array","left.points:array","left.max","left.unit","left.colors:array","left.dataTable","left.head:array","left.rows:array","left.colW:array","right.type","right.cap","right.image","right.labels:array","right.values:array","right.series:array","right.points:array","right.max","right.unit","right.colors:array","right.dataTable","right.head:array","right.rows:array","right.colW:array"]},"comparison":{"label":"对比页(左右双栏 + 可选结论条)","modes":["presentation","research"],"required":["left.title","left.points:array","right.title","right.points:array"],"optional":["verdict"]},"quote":{"label":"引用 / 金句页(深色全幅)","modes":["presentation","research"],"required":["quote","author"],"optional":["context"]},"diagram":{"label":"分层架构页(architecture 模式走全幅几何)","modes":["presentation","research","architecture"],"required":["layers:array"],"optional":["legend:array"]},"exhibit":{"label":"Exhibit 编号图表页(research R2)","modes":["research"],"required":["chart.labels:array","chart.values:array"],"optional":["exhibitNo","chart.type","chart.series:array","chart.points:array","chart.max","chart.unit","chart.colors:array","chart.dataTable","note"]},"twocol":{"label":"双栏论证页(research R1)","modes":["research"],"required":["paragraphs:array"]},"threecol":{"label":"三栏证据页(research R6)","modes":["research"],"required":["paragraphs:array"]},"halftable":{"label":"半表半图页(research R7)","modes":["research"],"required":["table.head:array","table.rows:array","chart.labels:array","chart.values:array"],"optional":["table.colW:array","chart.type","chart.series:array","chart.points:array","chart.max","chart.unit","chart.colors:array","chart.dataTable","note"]},"matrix":{"label":"矩阵图页(research R8)","modes":["research"],"required":["rowHeads:array","colHeads:array","cells:array"]},"lane":{"label":"泳道页(architecture A2)","modes":["architecture"],"required":["lanes:array"]},"sankey":{"label":"桑基图页(节点-流带,流向与流量)","modes":["research","architecture"],"required":["flows:array"],"optional":["unit","chart.dataTable","note"]},"treemap":{"label":"树图页(面积编码的层级构成)","modes":["research","architecture"],"required":["items:array"],"optional":["unit","chart.dataTable","note"]},"boxplot":{"label":"箱线图页(分布对比:min/q1/median/q3/max)","modes":["research","architecture"],"required":["groups:array"],"optional":["unit","chart.dataTable","note"]},"network":{"label":"关系网络页(节点-边拓扑)","modes":["research","architecture"],"required":["nodes:array","edges:array"],"optional":["chart.dataTable","note"]},"marimekko":{"label":"马赛克图页(列宽 × 列高双重编码)","modes":["research","architecture"],"required":["cols:array","cells:array"],"optional":["unit","chart.dataTable","note"]},"streamgraph":{"label":"流带图页(时间上的构成演变)","modes":["research","architecture"],"required":["series:array"],"optional":["labels:array","chart.dataTable","note"]}}};
|
|
2019
2036
|
/* __TOPPPT_SCHEMA_END__ */
|
|
2020
2037
|
|
|
2021
2038
|
var _mode = 'presentation'; /* 当前模式(slidesOf 里按 model.mode 设置) */
|
|
@@ -2306,6 +2323,15 @@ function footnoteLine(sh, text, S) {
|
|
|
2306
2323
|
sh.push(txSp(R.x, R.y, R.w, R.h,
|
|
2307
2324
|
[[{ t: text, sz: 9.5, col: S.faint, font: S.font }]]));
|
|
2308
2325
|
}
|
|
2326
|
+
/* R2:sec.note 与 sec.footnote 共用注释带末行(footnoteY=6.72)。
|
|
2327
|
+
note.y=6.55 会压进 so-what 带(6.05–6.67)——同页二者必须合并为一行。 */
|
|
2328
|
+
function noteOrFootnote(sh, sec, S) {
|
|
2329
|
+
var parts = [];
|
|
2330
|
+
if (sec && sec.note) parts.push(String(sec.note));
|
|
2331
|
+
if (sec && sec.footnote) parts.push(String(sec.footnote));
|
|
2332
|
+
if (!parts.length) return;
|
|
2333
|
+
footnoteLine(sh, parts.join(' · '), S);
|
|
2334
|
+
}
|
|
2309
2335
|
/* 待核实清单条(accent 强调色标注 · 与 build_pptx.js / HTML .flagbar 同源) */
|
|
2310
2336
|
function flagBar(sh, items, S, yTop) {
|
|
2311
2337
|
var F = PT.flagbar || { hdH: 0.26, rowH: 0.24, maxRows: 3, gap: 0.12 };
|
|
@@ -2494,7 +2520,9 @@ function vbarShapes(sh, ec, S, x, y, w, h, dcols) {
|
|
|
2494
2520
|
: [{ name: '数值', values: ec.values || [] }];
|
|
2495
2521
|
var nb = Math.max(1, labels.length);
|
|
2496
2522
|
var ns = Math.max(1, series.length);
|
|
2497
|
-
|
|
2523
|
+
/* R2:类目标签带必须含在 h 内(原 baseY+0.08 画在图区外,会压进 so-what/来源行) */
|
|
2524
|
+
var labelH = 0.36;
|
|
2525
|
+
var baseY = y + Math.max(0.6, h - labelH), plotW = w, step = plotW / nb;
|
|
2498
2526
|
var cluster = Math.min(1.1, step * (ns > 1 ? 0.78 : 0.55));
|
|
2499
2527
|
var bw = cluster / ns;
|
|
2500
2528
|
var all = [];
|
|
@@ -2503,26 +2531,27 @@ function vbarShapes(sh, ec, S, x, y, w, h, dcols) {
|
|
|
2503
2531
|
});
|
|
2504
2532
|
var vmax = ec.max || Math.max.apply(null, all.concat([1]));
|
|
2505
2533
|
var top = all.length ? Math.max.apply(null, all) : 0;
|
|
2534
|
+
var plotH = Math.max(0.35, baseY - y);
|
|
2506
2535
|
sh.push(shape('rect', x, baseY, plotW, 0.02, S.line));
|
|
2507
2536
|
series.forEach(function (se, si) {
|
|
2508
2537
|
var vals = se.values || [];
|
|
2509
2538
|
var col = dcols ? dcols[si % 5] : (si === 0 ? S.accent : S.faint);
|
|
2510
2539
|
vals.forEach(function (v, i) {
|
|
2511
2540
|
var num = Number(v) || 0;
|
|
2512
|
-
var hh = Math.max(0.05, num / vmax * (
|
|
2541
|
+
var hh = Math.max(0.05, num / vmax * (plotH - 0.35));
|
|
2513
2542
|
var bx = x + i * step + (step - cluster) / 2 + si * bw;
|
|
2514
2543
|
var hot = ns === 1 && num === top;
|
|
2515
2544
|
sh.push(shape('roundRect', bx, baseY - hh, Math.max(0.04, bw * 0.92), hh,
|
|
2516
2545
|
hot ? (dcols ? dcols[i % 5] : S.accent) : col, { adj: 6000 }));
|
|
2517
2546
|
if (ns === 1) {
|
|
2518
|
-
sh.push(txSp(bx - 0.3, baseY - hh - 0.
|
|
2547
|
+
sh.push(txSp(bx - 0.3, baseY - hh - 0.36, bw + 0.6, 0.30,
|
|
2519
2548
|
[[{ t: valText(num, ec.unit), sz: 12.5, b: 1, col: hot ? S.accent : S.body, font: S.font }]], { align: 'ctr' }));
|
|
2520
2549
|
}
|
|
2521
2550
|
});
|
|
2522
2551
|
});
|
|
2523
2552
|
labels.forEach(function (lb, i) {
|
|
2524
2553
|
var lx = x + i * step;
|
|
2525
|
-
sh.push(txSp(lx, baseY + 0.
|
|
2554
|
+
sh.push(txSp(lx, baseY + 0.06, step, labelH - 0.08,
|
|
2526
2555
|
[[{ t: lb, sz: 10.5, col: S.faint, font: S.font }]], { align: 'ctr' }));
|
|
2527
2556
|
});
|
|
2528
2557
|
/* Multi-series legend along top of plot (keeps category labels; avoids blank chart). */
|
|
@@ -2545,7 +2574,10 @@ function lineShapes(sh, ec, S, x, y, w, h, dcols) {
|
|
|
2545
2574
|
var all = [];
|
|
2546
2575
|
series.forEach(function (se) { (se.values || []).forEach(function (v) { all.push(Number(v) || 0); }); });
|
|
2547
2576
|
var vmax = ec.max || Math.max.apply(null, all.concat([1]));
|
|
2548
|
-
|
|
2577
|
+
/* R2:类目标签带含在 h 内,禁画到图区外压 so-what */
|
|
2578
|
+
var labelH = 0.36;
|
|
2579
|
+
var baseY = y + Math.max(0.6, h - labelH);
|
|
2580
|
+
var plotH = Math.max(0.35, baseY - y - 0.2);
|
|
2549
2581
|
sh.push(shape('rect', x, baseY, w, 0.02, S.line));
|
|
2550
2582
|
var stepX = n > 1 ? w / (n - 1) : 0;
|
|
2551
2583
|
series.forEach(function (se, si) {
|
|
@@ -2565,7 +2597,7 @@ function lineShapes(sh, ec, S, x, y, w, h, dcols) {
|
|
|
2565
2597
|
});
|
|
2566
2598
|
labels.forEach(function (lb, i) {
|
|
2567
2599
|
var px = x + (n > 1 ? i * stepX : w / 2);
|
|
2568
|
-
sh.push(txSp(px - 0.5, baseY + 0.
|
|
2600
|
+
sh.push(txSp(px - 0.5, baseY + 0.06, 1.0, labelH - 0.08,
|
|
2569
2601
|
[[{ t: lb, sz: 10.5, col: S.faint, font: S.font }]], { align: 'ctr' }));
|
|
2570
2602
|
});
|
|
2571
2603
|
}
|
|
@@ -3017,25 +3049,44 @@ function slidesOf(model, S) {
|
|
|
3017
3049
|
c.push(txSp(MX, CV.subtitleY, CW - 1, CV.subtitleH, [[{ t: model.subtitle || '', sz: 18, col: S.soft, font: S.font }]]));
|
|
3018
3050
|
slides.push(c);
|
|
3019
3051
|
|
|
3020
|
-
/* Agenda(>8
|
|
3052
|
+
/* Agenda(>8 条自动双列;>12 条自动分页;长标题截断+全称入 notes;列优先与 HTML 一致) */
|
|
3021
3053
|
var items = model.agenda || [];
|
|
3022
3054
|
if (items.length) {
|
|
3023
|
-
var
|
|
3024
|
-
|
|
3025
|
-
var
|
|
3026
|
-
|
|
3027
|
-
|
|
3028
|
-
|
|
3029
|
-
|
|
3030
|
-
|
|
3031
|
-
var
|
|
3032
|
-
var
|
|
3033
|
-
|
|
3034
|
-
|
|
3035
|
-
|
|
3036
|
-
|
|
3055
|
+
var ROWS_PER_PAGE = 12;
|
|
3056
|
+
var agPages = [];
|
|
3057
|
+
for (var ai = 0; ai < items.length; ai += ROWS_PER_PAGE) agPages.push(items.slice(ai, ai + ROWS_PER_PAGE));
|
|
3058
|
+
agPages.forEach(function (pageItems, pi) {
|
|
3059
|
+
var ag = [];
|
|
3060
|
+
var sub = agPages.length > 1 ? '(' + (pi + 1) + '/' + agPages.length + ')' : '';
|
|
3061
|
+
head(ag, 'AGENDA', '报告大纲' + sub);
|
|
3062
|
+
var colN = pageItems.length > 8 ? 2 : 1;
|
|
3063
|
+
var perCol = Math.ceil(pageItems.length / colN) || 1;
|
|
3064
|
+
var rowH = Math.min(1.05, (CONTENT_BOTTOM - CONTENT_TOP - 0.25) / Math.max(1, perCol));
|
|
3065
|
+
var numSz = rowH >= 0.85 ? 30 : (rowH >= 0.65 ? 22 : 18);
|
|
3066
|
+
var tSz = rowH >= 0.85 ? 16 : 14, dSz = rowH >= 0.85 ? 12 : 11;
|
|
3067
|
+
var agNotes = [];
|
|
3068
|
+
pageItems.forEach(function (it, i) {
|
|
3069
|
+
var col = Math.floor(i / perCol), row = i % perCol;
|
|
3070
|
+
var x = MX + col * (CW / colN), y = CONTENT_TOP + row * rowH;
|
|
3071
|
+
var titleBoxW = CW / colN - 1.15;
|
|
3072
|
+
var rawTitle = String(it[1] || '');
|
|
3073
|
+
var titleEstW = estTextH(rawTitle, titleBoxW, tSz, 1.2);
|
|
3074
|
+
var maxTitleH = rowH * 0.55;
|
|
3075
|
+
var title = rawTitle;
|
|
3076
|
+
if (titleEstW > maxTitleH) {
|
|
3077
|
+
var maxChars = Math.max(10, Math.floor(rawTitle.length * (maxTitleH / titleEstW)));
|
|
3078
|
+
if (rawTitle.length > maxChars) {
|
|
3079
|
+
title = rawTitle.slice(0, maxChars - 1) + '…';
|
|
3080
|
+
agNotes.push('条目 ' + it[0] + ' 全称:' + rawTitle);
|
|
3081
|
+
}
|
|
3082
|
+
}
|
|
3083
|
+
ag.push(txSp(x, y, 0.9, Math.min(0.9, rowH), [[{ t: it[0], sz: numSz, b: 1, col: S.accent, font: S.font }]]));
|
|
3084
|
+
ag.push(txSp(x + 1.0, y + 0.03, titleBoxW, Math.min(0.95, rowH),
|
|
3085
|
+
[[{ t: title, sz: tSz, b: 1, col: S.ink, font: S.font }],
|
|
3086
|
+
[{ t: it[2] || '', sz: dSz, col: S.body, font: S.font }]], { lineSpacing: tSz + 5 }));
|
|
3087
|
+
});
|
|
3088
|
+
slides.push(ag);
|
|
3037
3089
|
});
|
|
3038
|
-
slides.push(ag);
|
|
3039
3090
|
}
|
|
3040
3091
|
|
|
3041
3092
|
/* 章节页 */
|
|
@@ -3121,12 +3172,13 @@ function slidesOf(model, S) {
|
|
|
3121
3172
|
{ t: p[1], sz: fz - 1, col: S.body, font: S.font }]]));
|
|
3122
3173
|
});
|
|
3123
3174
|
});
|
|
3175
|
+
/* R2:verdict 与 soWhat 共用 annotation 槽位——只画一个(verdict 优先) */
|
|
3124
3176
|
if (sec.verdict) {
|
|
3125
3177
|
var vR = regOf('exhibit', 'annotation') || { x: MX, y: PT.exhibit.soWhatY, w: CW, h: 0.62 };
|
|
3126
|
-
sh.push(shape('rect', vR.x, vR.y, vR.w, vR.h, S.accent));
|
|
3178
|
+
sh.push(shape('rect', vR.x, vR.y, vR.w, vR.h, S.accent, { name: 'tr:soWhat' }));
|
|
3127
3179
|
sh.push(txSp(vR.x + 0.22, vR.y + 0.06, vR.w - 0.44, vR.h - 0.12,
|
|
3128
3180
|
[[{ t: '结论 ', sz: 11, b: 1, col: S.onAccent, font: S.font, spc: 150 },
|
|
3129
|
-
{ t: sec.verdict, sz: 12.5, b: 1, col: S.onAccent, font: S.font }]], { anchor: 'ctr' }));
|
|
3181
|
+
{ t: sec.verdict, sz: 12.5, b: 1, col: S.onAccent, font: S.font }]], { anchor: 'ctr', name: 'tr:soWhat' }));
|
|
3130
3182
|
}
|
|
3131
3183
|
} else if (type === 'quote') {
|
|
3132
3184
|
var Q = PT.quote;
|
|
@@ -3159,7 +3211,6 @@ function slidesOf(model, S) {
|
|
|
3159
3211
|
} else if (type === 'donut') {
|
|
3160
3212
|
var dcn = sec.chart || {};
|
|
3161
3213
|
if (dcn.labels && dcn.values) donutChart(sh, dcn, S, dcn.colors || donutColors(model.style, S, model.theme), bodyBottom);
|
|
3162
|
-
if (sec.note) sh.push(txSp(MX, PT.note.y, CW, PT.note.h, [[{ t: sec.note, sz: 11, col: S.faint, font: S.font }]]));
|
|
3163
3214
|
} else if (type === 'heatmap') {
|
|
3164
3215
|
var H = PT.heatmap;
|
|
3165
3216
|
var hR = regOf('heatmap', 'primary', { bottom: bodyBottom }) || { x: MX, y: H.y, w: CW, h: bodyBottom - H.y };
|
|
@@ -3306,7 +3357,7 @@ function slidesOf(model, S) {
|
|
|
3306
3357
|
} else if (type === 'bar') {
|
|
3307
3358
|
var ch = sec.chart || {};
|
|
3308
3359
|
var dcols2 = (ch.colors || dataColors(model.style, model.theme));
|
|
3309
|
-
var botY = chartBottom(!!sec.soWhat, !!sec.footnote
|
|
3360
|
+
var botY = chartBottom(!!sec.soWhat, !!(sec.footnote || sec.note)) - 0.05;
|
|
3310
3361
|
var isHBar = ch.type === 'hbar';
|
|
3311
3362
|
var bRR = regOf('bar', isHBar ? 'hbar' : 'primary', { bottom: botY }) ||
|
|
3312
3363
|
{ x: isHBar ? MX : MX + PT.bar.chartX, y: isHBar ? PT.bar.hbarY0 : PT.bar.chartY,
|
|
@@ -3333,13 +3384,12 @@ function slidesOf(model, S) {
|
|
|
3333
3384
|
} else {
|
|
3334
3385
|
chartBlockShapes(sh, ch, S, bx, bRR.y, bw, Math.max(1.5, bRR.h), dcols2);
|
|
3335
3386
|
}
|
|
3336
|
-
if (sec.note) sh.push(txSp(MX, PT.note.y, CW, PT.note.h, [[{ t: sec.note, sz: 11, col: S.faint, font: S.font }]]));
|
|
3337
3387
|
} else if (type === 'twocol' || type === 'threecol') {
|
|
3338
3388
|
var ps2 = sec.paragraphs || [];
|
|
3339
3389
|
var nCol = (type === 'threecol') ? 3 : 2;
|
|
3340
3390
|
var gapC = (type === 'threecol') ? PT.research.col3Gap : PT.twocol.colGap;
|
|
3341
|
-
|
|
3342
|
-
|
|
3391
|
+
/* R3:多栏均分用版心全宽(regOf('twocol') 返回单栏宽,当总宽会压扁三栏) */
|
|
3392
|
+
var tcR = { x: MX, y: bodyY, w: CW, h: bodyBottom - bodyY };
|
|
3343
3393
|
var gC = cols(tcR.w, nCol, gapC);
|
|
3344
3394
|
var perC = Math.ceil(ps2.length / nCol);
|
|
3345
3395
|
var availC = tcR.h;
|
|
@@ -3535,7 +3585,7 @@ function slidesOf(model, S) {
|
|
|
3535
3585
|
var dStart = dgR.y;
|
|
3536
3586
|
var dEnd = dgR.y + dgR.h;
|
|
3537
3587
|
var nLay = Math.max(1, lys.length);
|
|
3538
|
-
var lh2 = Math.max(0.
|
|
3588
|
+
var lh2 = Math.max(0.5, Math.min(D.maxLayerH, (dEnd - dStart) / nLay - D.layerGap));
|
|
3539
3589
|
lys.forEach(function (lay, li) {
|
|
3540
3590
|
var y9 = dStart + li * (lh2 + D.layerGap);
|
|
3541
3591
|
var focus = lay[2] === 'focus';
|
|
@@ -3551,9 +3601,26 @@ function slidesOf(model, S) {
|
|
|
3551
3601
|
var ntt = isRec(nd) ? nd.t : nd;
|
|
3552
3602
|
var nds = isRec(nd) ? (nd.d || '') : '';
|
|
3553
3603
|
sh.push(shape('roundRect', x10, y9, nw2, lh2, ndacc ? S.soft : S.surface, { adj: 4000, line: S.line }));
|
|
3554
|
-
|
|
3555
|
-
|
|
3556
|
-
|
|
3604
|
+
/* R1: 注解不得「浮回」标题上。标题顶对齐,注解钳在标题下边;空间不足降级单框混排。 */
|
|
3605
|
+
var padXn = 0.12, padYn = 0.10;
|
|
3606
|
+
var innerWn = nw2 - padXn * 2;
|
|
3607
|
+
var titleHn = 0.32, noteHn = 0.34, gapTNn = 0.04;
|
|
3608
|
+
var needHn = titleHn + gapTNn + (nds ? noteHn : 0) + padYn * 2;
|
|
3609
|
+
if (!nds || lh2 >= needHn) {
|
|
3610
|
+
var tYn = y9 + padYn;
|
|
3611
|
+
sh.push(txSp(x10 + padXn, nds ? tYn : y9 + lh2 / 2 - 0.18, innerWn, titleHn,
|
|
3612
|
+
[[{ t: ntt, sz: nds ? 11 : 12.5, b: 1, col: ndacc ? S.accent : S.ink, font: S.font }]],
|
|
3613
|
+
{ anchor: 'ctr' }));
|
|
3614
|
+
if (nds) {
|
|
3615
|
+
var nYn = Math.max(tYn + titleHn + gapTNn, y9 + lh2 - padYn - noteHn);
|
|
3616
|
+
sh.push(txSp(x10 + padXn, nYn, innerWn, Math.min(noteHn, y9 + lh2 - padYn - nYn),
|
|
3617
|
+
[[{ t: nds, sz: 10, col: S.faint, font: S.font }]], { anchor: 't' }));
|
|
3618
|
+
}
|
|
3619
|
+
} else {
|
|
3620
|
+
sh.push(txSp(x10 + padXn, y9 + padYn * 0.5, innerWn, lh2 - padYn,
|
|
3621
|
+
[[{ t: ntt, sz: 11, b: 1, col: ndacc ? S.accent : S.ink, font: S.font }],
|
|
3622
|
+
[{ t: nds, sz: 9, col: S.faint, font: S.font }]], { anchor: 't', lineSpacing: 12 }));
|
|
3623
|
+
}
|
|
3557
3624
|
});
|
|
3558
3625
|
if (li < lys.length - 1) {
|
|
3559
3626
|
var cy2 = y9 + lh2 + D.layerGap / 2;
|
|
@@ -3563,10 +3630,14 @@ function slidesOf(model, S) {
|
|
|
3563
3630
|
}
|
|
3564
3631
|
});
|
|
3565
3632
|
if (sec.legend) {
|
|
3566
|
-
|
|
3567
|
-
|
|
3568
|
-
|
|
3569
|
-
|
|
3633
|
+
/* R2:图例进图区底部(与 HTML .arch__legend 同源),禁用 PH-0.62 固定偏移 */
|
|
3634
|
+
var lgH2 = 0.28;
|
|
3635
|
+
var lgY2 = Math.max(dStart, bodyBottom - lgH2 - 0.02);
|
|
3636
|
+
var nLg2 = Math.max(1, sec.legend.length);
|
|
3637
|
+
var step2 = Math.min(2.4, CW / nLg2);
|
|
3638
|
+
sec.legend.forEach(function (lg2, li2) {
|
|
3639
|
+
sh.push(txSp(MX + CW - nLg2 * step2 + li2 * step2, lgY2, step2 - 0.05, lgH2,
|
|
3640
|
+
[[{ t: '● ' + lg2, sz: 10, col: S.faint, font: S.font }]]));
|
|
3570
3641
|
});
|
|
3571
3642
|
}
|
|
3572
3643
|
} else if (type === 'exhibit') {
|
|
@@ -3623,8 +3694,10 @@ function slidesOf(model, S) {
|
|
|
3623
3694
|
}
|
|
3624
3695
|
}
|
|
3625
3696
|
/* research 通用可选件:so-what 结论条 + 页脚来源行 + 待核实条(共用同一槽位) */
|
|
3626
|
-
|
|
3627
|
-
if (sec.
|
|
3697
|
+
/* R2:verdict 已占 annotation 槽位时跳过 soWhat,禁止同槽双条叠印 */
|
|
3698
|
+
if (sec.soWhat && !sec.verdict) soWhatBar(sh, sec.soWhat, S);
|
|
3699
|
+
/* R2:note 与 footnote 共用注释带末行(禁 note.y=6.55 压 so-what) */
|
|
3700
|
+
noteOrFootnote(sh, sec, S);
|
|
3628
3701
|
if (flagH) flagBar(sh, flagItems, S, flagY);
|
|
3629
3702
|
slides.push(sh);
|
|
3630
3703
|
});
|
|
@@ -3794,9 +3867,10 @@ function validateModel(model) {
|
|
|
3794
3867
|
}
|
|
3795
3868
|
var agMax = (M.model && M.model.agendaComfortMax) || 16;
|
|
3796
3869
|
if ((model.agenda || []).length > agMax) {
|
|
3797
|
-
warnings.push('agenda ' + model.agenda.length + ' 条超出单页舒适上限 ' + agMax + '
|
|
3870
|
+
warnings.push('agenda ' + model.agenda.length + ' 条超出单页舒适上限 ' + agMax + ',将自动分页');
|
|
3798
3871
|
}
|
|
3799
|
-
var
|
|
3872
|
+
var agPageCount = hasAgenda ? Math.ceil(model.agenda.length / 12) : 0;
|
|
3873
|
+
var pages = 1 + agPageCount + secs.length + 1; /* 封面+大纲(可多页)+章节+收尾 */
|
|
3800
3874
|
return { ok: missing.length === 0, missing: missing, warnings: warnings, pages: pages };
|
|
3801
3875
|
}
|
|
3802
3876
|
|
|
@@ -791,8 +791,9 @@ color:var(--accent);font-variant-numeric:tabular-nums;line-height:1;min-width:2.
|
|
|
791
791
|
.agenda__a:hover .agenda__go{color:var(--accent);transform:translateX(4px)}
|
|
792
792
|
.agenda__go svg{width:20px;height:20px;display:block}
|
|
793
793
|
@media (min-width:900px){
|
|
794
|
-
|
|
795
|
-
.agenda--2col
|
|
794
|
+
/* R4:列优先(先左列后右列),与 PPTX Agenda 列填充一致——grid 行优先会打乱阅读顺序 */
|
|
795
|
+
.agenda--2col{display:block;column-count:2;column-gap:clamp(20px,3vw,48px)}
|
|
796
|
+
.agenda--2col .agenda__i{break-inside:avoid;border-top:1px solid var(--border-soft)}}
|
|
796
797
|
/* ── 页码指示(翻页导航) ── */
|
|
797
798
|
.pager{position:fixed;right:clamp(10px,1.6vw,26px);top:50%;transform:translateY(-50%);
|
|
798
799
|
z-index:60;display:flex;flex-direction:column;align-items:center;gap:10px}
|