@folienbaukasten/extension-toolkit 1.0.0
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/README.md +109 -0
- package/abi/README.md +20 -0
- package/abi/abi.json +151 -0
- package/abi/pack.d.ts +196 -0
- package/abi/pack.js +126 -0
- package/abi/provenance.json +5 -0
- package/abi/shims/jsx-runtime.js +13 -0
- package/abi/shims/platform.js +48 -0
- package/abi/shims/react.js +18 -0
- package/abi/types/charts/geometry.d.ts +43 -0
- package/abi/types/charts/option.d.ts +15 -0
- package/abi/types/charts/scale.d.ts +14 -0
- package/abi/types/charts/theme.d.ts +16 -0
- package/abi/types/charts/waterfall.d.ts +35 -0
- package/abi/types/components/Block.d.ts +31 -0
- package/abi/types/components/BlockArrow.d.ts +16 -0
- package/abi/types/components/Card.d.ts +25 -0
- package/abi/types/components/ChartView.d.ts +4 -0
- package/abi/types/components/DriverTree.d.ts +9 -0
- package/abi/types/components/FindingsBanner.d.ts +21 -0
- package/abi/types/components/HarveyBall.d.ts +12 -0
- package/abi/types/components/HeaderTitle.d.ts +19 -0
- package/abi/types/components/InsightRail.d.ts +13 -0
- package/abi/types/components/KpiTile.d.ts +5 -0
- package/abi/types/components/Matrix.d.ts +5 -0
- package/abi/types/components/QuoteColumn.d.ts +5 -0
- package/abi/types/components/SourceLine.d.ts +6 -0
- package/abi/types/components/Table.d.ts +5 -0
- package/abi/types/components/Timeline.d.ts +5 -0
- package/abi/types/components/slide/ContentSlide.d.ts +29 -0
- package/abi/types/components/slide/MissingComponent.d.ts +26 -0
- package/abi/types/components/slide/SlideFrame.d.ts +20 -0
- package/abi/types/components/slide/SlotView.d.ts +30 -0
- package/abi/types/components/slide/registry.d.ts +27 -0
- package/abi/types/components/slide/views/AgendaSlideView.d.ts +8 -0
- package/abi/types/components/slide/views/CardsSlideView.d.ts +12 -0
- package/abi/types/components/slide/views/ChaptersSlideView.d.ts +11 -0
- package/abi/types/components/slide/views/ChartSlideView.d.ts +3 -0
- package/abi/types/components/slide/views/ComponentSlideView.d.ts +19 -0
- package/abi/types/components/slide/views/DriverTreeSlideView.d.ts +3 -0
- package/abi/types/components/slide/views/FreeformSlideView.d.ts +3 -0
- package/abi/types/components/slide/views/KpiSlideView.d.ts +3 -0
- package/abi/types/components/slide/views/MatrixSlideView.d.ts +3 -0
- package/abi/types/components/slide/views/QuoteSlideView.d.ts +3 -0
- package/abi/types/components/slide/views/QuotesSlideView.d.ts +5 -0
- package/abi/types/components/slide/views/SplitSlideView.d.ts +3 -0
- package/abi/types/components/slide/views/TableSlideView.d.ts +3 -0
- package/abi/types/components/slide/views/TimelineSlideView.d.ts +3 -0
- package/abi/types/components/slide/views/TitleSlideView.d.ts +7 -0
- package/abi/types/components/slide/views/TwoBlockSlideView.d.ts +3 -0
- package/abi/types/components/slide/views/WebSlideView.d.ts +23 -0
- package/abi/types/components/slide/views/WhileArrowSlideView.d.ts +14 -0
- package/abi/types/deck/data.d.ts +127 -0
- package/abi/types/deck/host.d.ts +9 -0
- package/abi/types/deck/layouts.d.ts +685 -0
- package/abi/types/deck/schema.d.ts +437 -0
- package/abi/types/deck/web-body.d.ts +47 -0
- package/abi/types/lib/format.d.ts +12 -0
- package/abi/types/sets/platform.d.ts +45 -0
- package/abi/types/sets/types.d.ts +128 -0
- package/dist/fbk-pack.js +756 -0
- package/package.json +49 -0
- package/tsconfig.extension.json +17 -0
|
@@ -0,0 +1,437 @@
|
|
|
1
|
+
import type { DataSource } from './data.ts';
|
|
2
|
+
export type { CellValue, DataRef, DataSheet, DataSource } from './data.ts';
|
|
3
|
+
/** Slide canvas in CSS pixels. Maps 1:1 to a 13.333in × 7.5in (16:9) PPTX
|
|
4
|
+
* slide at 96 DPI. */
|
|
5
|
+
export declare const SLIDE_WIDTH = 1280;
|
|
6
|
+
export declare const SLIDE_HEIGHT = 720;
|
|
7
|
+
/** 914400 EMU per inch / 96 px per inch. */
|
|
8
|
+
export declare const EMU_PER_PX = 9525;
|
|
9
|
+
export interface DeckMeta {
|
|
10
|
+
title: string;
|
|
11
|
+
/**
|
|
12
|
+
* One line saying what this deck is for. Shown as the subtitle of a
|
|
13
|
+
* starter-deck card in the New deck dialog, which is where a theme's
|
|
14
|
+
* templates are chosen — `tools/pack-set.ts` reads it straight off the
|
|
15
|
+
* deck when it builds the pack manifest, so a template describes itself
|
|
16
|
+
* rather than being described somewhere else that can drift.
|
|
17
|
+
*/
|
|
18
|
+
description?: string;
|
|
19
|
+
author?: string;
|
|
20
|
+
/** ISO date, e.g. "2026-08-16". Formatting is a renderer concern. */
|
|
21
|
+
date?: string;
|
|
22
|
+
/** Footer text shown on every content slide (e.g. confidentiality note). */
|
|
23
|
+
footer?: string;
|
|
24
|
+
/**
|
|
25
|
+
* Small mark shown in the header of every content slide, where the active
|
|
26
|
+
* theme has a place for one. Root-relative like every other image URL
|
|
27
|
+
* (`/logo.svg`, `/project/assets/logo.png`); themes without a logo slot
|
|
28
|
+
* ignore it, which is why it is optional and never a layout prop.
|
|
29
|
+
*/
|
|
30
|
+
logoUrl?: string;
|
|
31
|
+
/** Component set that defines this deck's layouts. Default "consulting". */
|
|
32
|
+
componentSet?: string;
|
|
33
|
+
/**
|
|
34
|
+
* Display name of an *installed* theme this deck was written in, recorded
|
|
35
|
+
* so a machine that lacks the theme can say "This deck uses Acme Theme by
|
|
36
|
+
* Acme GmbH" instead of "unknown component set 'acme'". Never set for
|
|
37
|
+
* built-in themes — the recipient has those.
|
|
38
|
+
*/
|
|
39
|
+
componentSetName?: string;
|
|
40
|
+
/** Publisher of the installed theme, for the same message. */
|
|
41
|
+
componentSetPublisher?: string;
|
|
42
|
+
/**
|
|
43
|
+
* Version of an *installed* theme (ui.md A9) this deck was written against,
|
|
44
|
+
* e.g. "2.1.0". Only its major matters: fixes and additions inside a major
|
|
45
|
+
* are picked up automatically, while a theme that changes how slides look
|
|
46
|
+
* ships a new major and installs beside the old one — so a deck someone has
|
|
47
|
+
* already presented keeps rendering as it did until they choose otherwise.
|
|
48
|
+
* Absent (the normal case, and every deck in a built-in theme) means "the
|
|
49
|
+
* newest installed".
|
|
50
|
+
*/
|
|
51
|
+
componentSetVersion?: string;
|
|
52
|
+
/**
|
|
53
|
+
* Web Mode: the deck trades its PowerPoint export for
|
|
54
|
+
* the whole browser. A validation profile, not a rendering mode — when
|
|
55
|
+
* true, the web-only vocabulary (extra chart types, web-only extension
|
|
56
|
+
* components) validates, and PPTX export is refused with a clear message.
|
|
57
|
+
* Absent or false = PowerPoint mode = the strict profile. Toggled by the
|
|
58
|
+
* editor's "Activate Web Mode" button, never by the model.
|
|
59
|
+
*/
|
|
60
|
+
webMode?: boolean;
|
|
61
|
+
}
|
|
62
|
+
/** A slide as the platform sees it; the selected component set gives
|
|
63
|
+
* `layout` meaning and types `props`. */
|
|
64
|
+
export interface SlideSpec {
|
|
65
|
+
layout: string;
|
|
66
|
+
props: unknown;
|
|
67
|
+
}
|
|
68
|
+
export interface Deck {
|
|
69
|
+
meta: DeckMeta;
|
|
70
|
+
slides: SlideSpec[];
|
|
71
|
+
/**
|
|
72
|
+
* Workbook data imported into this deck (src/deck/data.ts). Any prop in
|
|
73
|
+
* any slide may be a `{ "$ref": "source!Sheet!A1:D9" }` pointer into it
|
|
74
|
+
* instead of a literal; every render path resolves the deck before it
|
|
75
|
+
* renders, so components never see a ref.
|
|
76
|
+
*/
|
|
77
|
+
data?: DataSource[];
|
|
78
|
+
}
|
|
79
|
+
/** Action title + sub-action title shown at the top of content slides. */
|
|
80
|
+
export interface HeaderSpec {
|
|
81
|
+
/**
|
|
82
|
+
* A substring of `title` to render in the theme's highlight colour — the
|
|
83
|
+
* one phrase the slide turns on ("what we *found*"). Must appear in the
|
|
84
|
+
* title verbatim; ignored when it does not. One phrase per slide: a
|
|
85
|
+
* highlight that repeats stops being one.
|
|
86
|
+
*/
|
|
87
|
+
highlight?: string;
|
|
88
|
+
title: string;
|
|
89
|
+
subtitle?: string;
|
|
90
|
+
/** Short category label above the title, e.g. "Market entry" — a few words,
|
|
91
|
+
* not a sentence. Sets that have no place for one ignore it. */
|
|
92
|
+
kicker?: string;
|
|
93
|
+
}
|
|
94
|
+
/** Harvey ball fill level in quarters. */
|
|
95
|
+
export type HarveyValue = 0 | 25 | 50 | 75 | 100;
|
|
96
|
+
/**
|
|
97
|
+
* A content block. `box` is the neutral container, `callout` the
|
|
98
|
+
* accent-colored findings box. Body strings are separate paragraphs.
|
|
99
|
+
*/
|
|
100
|
+
export interface BlockSpec {
|
|
101
|
+
title?: string;
|
|
102
|
+
body: string | string[];
|
|
103
|
+
/**
|
|
104
|
+
* Bulleted points under the body. Use for parallel items a reader scans
|
|
105
|
+
* (evidence, criteria, steps); keep prose in `body`. Both may appear —
|
|
106
|
+
* body sets up the point, bullets enumerate it.
|
|
107
|
+
*/
|
|
108
|
+
bullets?: string[];
|
|
109
|
+
variant?: 'box' | 'callout';
|
|
110
|
+
/** Optional harvey-ball rating rendered next to the block title. */
|
|
111
|
+
rating?: HarveyValue;
|
|
112
|
+
}
|
|
113
|
+
/** Findings/recommendation banner: accent label segment + statement bar. */
|
|
114
|
+
export interface FindingSpec {
|
|
115
|
+
/** Label in the accent segment. Default "Findings". */
|
|
116
|
+
label?: string;
|
|
117
|
+
text: string;
|
|
118
|
+
}
|
|
119
|
+
/** One KPI stat tile. */
|
|
120
|
+
export interface KpiSpec {
|
|
121
|
+
/** Rendered uppercase. */
|
|
122
|
+
label: string;
|
|
123
|
+
/** Numbers are formatted per valueStyle; strings pass through. */
|
|
124
|
+
value: number | string;
|
|
125
|
+
valueStyle?: 'plain' | 'compact' | 'percent';
|
|
126
|
+
/** Fraction, e.g. 0.42 → "▲ 42%"; sign picks the arrow. */
|
|
127
|
+
delta?: number;
|
|
128
|
+
/** Context after the delta, e.g. "vs PY". */
|
|
129
|
+
deltaLabel?: string;
|
|
130
|
+
/** Which direction is good (colors the delta). Default 'up'; churn-like
|
|
131
|
+
* metrics use 'down' so a falling value shows green. */
|
|
132
|
+
deltaGood?: 'up' | 'down';
|
|
133
|
+
/**
|
|
134
|
+
* The run behind the number, oldest first, and ENDING AT THE PERIOD
|
|
135
|
+
* `value` REPORTS — the last point is the same figure the tile shows, in
|
|
136
|
+
* the same units. A series that ends anywhere else draws a line that
|
|
137
|
+
* contradicts the number printed above it, which is worse than no line.
|
|
138
|
+
*
|
|
139
|
+
* Supply the readings only. Every coordinate is derived from them: the run
|
|
140
|
+
* is normalised to its own min and max, so units never matter and no axis
|
|
141
|
+
* is drawn. Do not compute positions, percentages or a path.
|
|
142
|
+
*
|
|
143
|
+
* A delta says the number moved; a sparkline says how it moved, and the
|
|
144
|
+
* two answer different objections. Six to eight points read cleanly; fewer
|
|
145
|
+
* looks arbitrary, more turns to noise at tile size.
|
|
146
|
+
*
|
|
147
|
+
* @minItems 2
|
|
148
|
+
*/
|
|
149
|
+
series?: number[];
|
|
150
|
+
/**
|
|
151
|
+
* Goal for the period, in the value's units. Draws a meter under the
|
|
152
|
+
* number instead of a sparkline, and turns "24.3M" into "24.3M of the 30M
|
|
153
|
+
* we committed to".
|
|
154
|
+
*
|
|
155
|
+
* `deltaGood` sets which way the goal reads. The default 'up' makes it a
|
|
156
|
+
* TARGET to reach, and the meter fills toward it. 'down' makes it a CAP
|
|
157
|
+
* not to breach — the meter then fills with how much of the allowance is
|
|
158
|
+
* spent, and going over reads in the negative colour instead of showing a
|
|
159
|
+
* full bar, which is what a churn figure at 140% of its goal would
|
|
160
|
+
* otherwise look like.
|
|
161
|
+
*
|
|
162
|
+
* A tile takes a sparkline or a meter, not both — two rows of geometry
|
|
163
|
+
* under one number is the point at which a tile stops being readable from
|
|
164
|
+
* the third row. `series` wins if a deck sets both.
|
|
165
|
+
*/
|
|
166
|
+
target?: number;
|
|
167
|
+
/** One short line under the number: the caveat, the denominator, the
|
|
168
|
+
* thing an audience asks about before it believes the figure. */
|
|
169
|
+
note?: string;
|
|
170
|
+
/** The one tile the slide is actually about — drawn in the theme's
|
|
171
|
+
* emphasis treatment. Marking every tile marks none. */
|
|
172
|
+
emphasis?: boolean;
|
|
173
|
+
}
|
|
174
|
+
/** One numbered entry of an agenda / table of contents. */
|
|
175
|
+
export interface AgendaItemSpec {
|
|
176
|
+
title: string;
|
|
177
|
+
/** Optional one-line detail under the topic. */
|
|
178
|
+
note?: string;
|
|
179
|
+
}
|
|
180
|
+
/** One box on a 2×2 matrix. */
|
|
181
|
+
export interface MatrixPointSpec {
|
|
182
|
+
label: string;
|
|
183
|
+
/** Horizontal position, 0 (left) – 100 (right). */
|
|
184
|
+
x: number;
|
|
185
|
+
/** Vertical position, 0 (bottom) – 100 (top). */
|
|
186
|
+
y: number;
|
|
187
|
+
/** Draws the box in the accent color — the option being argued for. */
|
|
188
|
+
emphasis?: boolean;
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* 2×2 positioning matrix: two named axes, optional quadrant captions and the
|
|
192
|
+
* boxes placed on it. Five to eight points stay readable; beyond that the
|
|
193
|
+
* boxes start to overlap, which the renderer will not resolve for you.
|
|
194
|
+
*/
|
|
195
|
+
export interface MatrixSpec {
|
|
196
|
+
/** Axis names, rendered at the low→high end of each axis. */
|
|
197
|
+
xAxis: string;
|
|
198
|
+
yAxis: string;
|
|
199
|
+
/** Quadrant captions, clockwise from top-left. */
|
|
200
|
+
quadrants?: [string, string, string, string];
|
|
201
|
+
points: MatrixPointSpec[];
|
|
202
|
+
}
|
|
203
|
+
/** One branch of a driver tree. */
|
|
204
|
+
export interface DriverSpec {
|
|
205
|
+
title: string;
|
|
206
|
+
/** Separate paragraphs, as in BlockSpec. */
|
|
207
|
+
body?: string | string[];
|
|
208
|
+
}
|
|
209
|
+
/** A verbatim quote with its attribution. */
|
|
210
|
+
export interface QuoteSpec {
|
|
211
|
+
text: string;
|
|
212
|
+
/** Who said it, e.g. "CFO, mid-market retailer". */
|
|
213
|
+
attribution?: string;
|
|
214
|
+
}
|
|
215
|
+
/** One themed column of quotes. `tone` colors the column's accent bar. */
|
|
216
|
+
export interface QuoteColumnSpec {
|
|
217
|
+
title: string;
|
|
218
|
+
tone?: 'positive' | 'negative';
|
|
219
|
+
quotes: QuoteSpec[];
|
|
220
|
+
}
|
|
221
|
+
/** One numbered chapter on a section-divider slide. */
|
|
222
|
+
export interface ChapterSpec {
|
|
223
|
+
title: string;
|
|
224
|
+
/** One or two lines on what the chapter covers. */
|
|
225
|
+
body?: string;
|
|
226
|
+
}
|
|
227
|
+
/** One card in a card grid. */
|
|
228
|
+
export interface CardSpec {
|
|
229
|
+
title?: string;
|
|
230
|
+
body: string | string[];
|
|
231
|
+
/** Accent bar + tinted fill — the card that carries the argument. */
|
|
232
|
+
emphasis?: boolean;
|
|
233
|
+
}
|
|
234
|
+
/** One entry of the insight rail: an accent lead-in and its explanation. */
|
|
235
|
+
export interface InsightSpec {
|
|
236
|
+
label: string;
|
|
237
|
+
text: string;
|
|
238
|
+
}
|
|
239
|
+
/** One event on a timeline, in chronological order. */
|
|
240
|
+
export interface TimelineEventSpec {
|
|
241
|
+
/** Short date or phase label, e.g. "Q1 2027". Display string. */
|
|
242
|
+
date?: string;
|
|
243
|
+
title: string;
|
|
244
|
+
/** Why the event matters — one or two lines. */
|
|
245
|
+
body?: string;
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* `line`, `bar`, `stacked-bar` and `waterfall` work in every deck — they map
|
|
249
|
+
* to native PowerPoint charts. `pie`, `donut` and `radar` are **Web Mode
|
|
250
|
+
* only**: they render in the browser (preview, HTML and
|
|
251
|
+
* PDF export) but have no faithful pptxgenjs counterpart, so a PowerPoint
|
|
252
|
+
* mode deck rejects them at validation.
|
|
253
|
+
*/
|
|
254
|
+
export type ChartType = 'line' | 'bar' | 'stacked-bar' | 'waterfall' | 'pie' | 'donut' | 'radar';
|
|
255
|
+
/**
|
|
256
|
+
* A colour slot, resolved by the theme — never a literal colour. `series`
|
|
257
|
+
* is the series' own place in the theme's ramp; `accent` is the theme's
|
|
258
|
+
* accent (`--color-accent`) for the one series that carries the argument;
|
|
259
|
+
* the rest are the tokens a controlling chart reasons in ("this point is
|
|
260
|
+
* bad news"), so a deck says what a mark *means* and the set says what
|
|
261
|
+
* that looks like.
|
|
262
|
+
*/
|
|
263
|
+
export type ChartTone = 'series' | 'positive' | 'negative' | 'neutral' | 'muted' | 'accent';
|
|
264
|
+
export interface ChartSeries {
|
|
265
|
+
name: string;
|
|
266
|
+
/**
|
|
267
|
+
* One value per category. `null` is a gap — a plan that stops where the
|
|
268
|
+
* forecast starts, an actual that has not happened yet. A line breaks
|
|
269
|
+
* there; a bar is simply absent. Never write 0 to mean "no value".
|
|
270
|
+
*/
|
|
271
|
+
values: Array<number | null>;
|
|
272
|
+
/**
|
|
273
|
+
* How the series is painted, bar-like types only. `hollow` is an outline
|
|
274
|
+
* with no fill, `hatched` a diagonal pattern — between them, plus solid
|
|
275
|
+
* and a tone, every IBCS scenario notation (actual, previous year, plan,
|
|
276
|
+
* forecast) can be expressed without the platform knowing what a scenario
|
|
277
|
+
* is.
|
|
278
|
+
*/
|
|
279
|
+
fill?: 'solid' | 'hollow' | 'hatched';
|
|
280
|
+
/** Colour slot for the whole series. Default: the theme's ramp. */
|
|
281
|
+
tone?: ChartTone;
|
|
282
|
+
/**
|
|
283
|
+
* Colour slot per point; `null` keeps the series colour. What a variance
|
|
284
|
+
* series needs: the same bar is good news or bad news by point, and the
|
|
285
|
+
* sign alone does not say which.
|
|
286
|
+
*/
|
|
287
|
+
pointTones?: (ChartTone | null)[];
|
|
288
|
+
/** Dashed stroke, line charts only — a series that is not actual. */
|
|
289
|
+
dash?: boolean;
|
|
290
|
+
}
|
|
291
|
+
/**
|
|
292
|
+
* Which way a bar chart reads. `columns` is vertical (the default);
|
|
293
|
+
* `bars` is horizontal, first category at the TOP — the shape a variance
|
|
294
|
+
* breakdown reads in. Line charts ignore it.
|
|
295
|
+
*/
|
|
296
|
+
export type ChartOrientation = 'columns' | 'bars';
|
|
297
|
+
/**
|
|
298
|
+
* The inner plot rect as fractions of the chart box (see
|
|
299
|
+
* `ChartSpec.plotArea`). Re-declared here rather than imported so the deck
|
|
300
|
+
* schema stays one file for the JSON-Schema generator.
|
|
301
|
+
*/
|
|
302
|
+
export interface PlotArea {
|
|
303
|
+
/** @minimum 0 @maximum 1 */
|
|
304
|
+
x: number;
|
|
305
|
+
/** @minimum 0 @maximum 1 */
|
|
306
|
+
y: number;
|
|
307
|
+
/** @minimum 0 @maximum 1 */
|
|
308
|
+
w: number;
|
|
309
|
+
/** @minimum 0 @maximum 1 */
|
|
310
|
+
h: number;
|
|
311
|
+
}
|
|
312
|
+
/**
|
|
313
|
+
* Our own chart contract — never raw ECharts options — so the same data
|
|
314
|
+
* renders identically in ECharts (HTML) and as a native PPTX chart.
|
|
315
|
+
*
|
|
316
|
+
* Waterfall: exactly one series whose values are deltas. Indices listed in
|
|
317
|
+
* `totalIndices` are checkpoint bars drawn from zero showing the running
|
|
318
|
+
* total; their value is ignored except at index 0, where it seeds the start.
|
|
319
|
+
* `pointTones` on that series recolours individual bars — how a cost bridge
|
|
320
|
+
* says that a decrease is the good news (default: increase `positive`,
|
|
321
|
+
* decrease `negative`, totals `neutral`).
|
|
322
|
+
*
|
|
323
|
+
* Pie/donut: one series; `categories` name the slices, the series values
|
|
324
|
+
* size them. Radar: `categories` are the spokes, each series one polygon.
|
|
325
|
+
*/
|
|
326
|
+
export interface ChartSpec {
|
|
327
|
+
type: ChartType;
|
|
328
|
+
categories: string[];
|
|
329
|
+
series: ChartSeries[];
|
|
330
|
+
/** How values are formatted on axes and labels. percent expects fractions. */
|
|
331
|
+
valueStyle?: 'plain' | 'compact' | 'percent';
|
|
332
|
+
showDataLabels?: boolean;
|
|
333
|
+
/** Bar/stacked-bar only. Default `columns`. */
|
|
334
|
+
orientation?: ChartOrientation;
|
|
335
|
+
/**
|
|
336
|
+
* Pins the inner plot rect — the data area, excluding axis labels — as
|
|
337
|
+
* fractions of the chart's own box. Both renderers take the same four
|
|
338
|
+
* numbers (ECharts an explicit `grid`, PowerPoint a `<c:manualLayout>`
|
|
339
|
+
* with `layoutTarget="inner"`), so the position of a category band stops
|
|
340
|
+
* being each renderer's private business and becomes arithmetic anyone
|
|
341
|
+
* can repeat — which is what lets something OUTSIDE the chart line up
|
|
342
|
+
* with the bars (`src/charts/geometry.ts`).
|
|
343
|
+
*
|
|
344
|
+
* Leave it out and each renderer auto-fits, which looks better on its own
|
|
345
|
+
* and cannot be aligned against. Set it, and leave room: axis labels are
|
|
346
|
+
* drawn outside this rect, so long category names want a bigger `x`.
|
|
347
|
+
*/
|
|
348
|
+
plotArea?: PlotArea;
|
|
349
|
+
/** Line charts: draw a symbol at each data point. Default true. */
|
|
350
|
+
showSymbol?: boolean;
|
|
351
|
+
/**
|
|
352
|
+
* Where the value axis starts. Default `zero` — a truncated axis is the
|
|
353
|
+
* commonest way a chart misleads, so `auto` has to be asked for.
|
|
354
|
+
*/
|
|
355
|
+
baseline?: 'zero' | 'auto';
|
|
356
|
+
/**
|
|
357
|
+
* Explicit value-axis bounds — the words a shared scale is made of. A
|
|
358
|
+
* grid of small multiples that does not pass the same `valueMax` to every
|
|
359
|
+
* chart is nine different rulers. `valueMin` overrides `baseline`.
|
|
360
|
+
*/
|
|
361
|
+
valueMin?: number;
|
|
362
|
+
valueMax?: number;
|
|
363
|
+
/**
|
|
364
|
+
* Distance between gridlines, in value units. Pin it alongside `valueMax`
|
|
365
|
+
* or each renderer picks its own ladder and then draws the pinned maximum
|
|
366
|
+
* as an extra tick on top of the one nearest it — two labels in the same
|
|
367
|
+
* three pixels at the top of the chart.
|
|
368
|
+
*/
|
|
369
|
+
valueStep?: number;
|
|
370
|
+
/**
|
|
371
|
+
* Default true. `false` deletes the value axis AND its gridlines, in both
|
|
372
|
+
* renderers — the IBCS look is labels on the marks and no axis furniture,
|
|
373
|
+
* and it is also what lets two stacked charts align: with no value-axis
|
|
374
|
+
* labels there is no label width to disagree about.
|
|
375
|
+
*/
|
|
376
|
+
showValueAxis?: boolean;
|
|
377
|
+
/** Default true. `false` for the upper panel of a pair that shares its
|
|
378
|
+
* category axis with the chart below it. */
|
|
379
|
+
showCategoryAxis?: boolean;
|
|
380
|
+
/** Default: shown when there is more than one series. */
|
|
381
|
+
showLegend?: boolean;
|
|
382
|
+
/**
|
|
383
|
+
* Line charts only: print each series' value at its last non-null point.
|
|
384
|
+
* The renderer places the label — no coordinate of ours is involved. The
|
|
385
|
+
* IBCS way to read a trend without hunting through a legend.
|
|
386
|
+
*/
|
|
387
|
+
endLabels?: boolean;
|
|
388
|
+
/**
|
|
389
|
+
* Gap between bars as a percentage of bar width (PowerPoint's own unit,
|
|
390
|
+
* mirrored into ECharts). Lower is fatter; 0 makes bars touch.
|
|
391
|
+
* @minimum 0
|
|
392
|
+
* @maximum 500
|
|
393
|
+
*/
|
|
394
|
+
barGapPct?: number;
|
|
395
|
+
/**
|
|
396
|
+
* Overlap between adjacent bars WITHIN one category group, in
|
|
397
|
+
* PowerPoint's unit: 0 (the default) makes them touch — the IBCS pair —
|
|
398
|
+
* negative opens a gap, positive overlaps them.
|
|
399
|
+
* @minimum -100
|
|
400
|
+
* @maximum 100
|
|
401
|
+
*/
|
|
402
|
+
barOverlapPct?: number;
|
|
403
|
+
/** waterfall only */
|
|
404
|
+
totalIndices?: number[];
|
|
405
|
+
}
|
|
406
|
+
/**
|
|
407
|
+
* A slide body supplied by an installed extension: a bespoke pricing sheet, framework
|
|
408
|
+
* diagram, disclosure block. The platform hosts it in the `component`
|
|
409
|
+
* layout's body region; it cannot touch the chrome bands.
|
|
410
|
+
*/
|
|
411
|
+
export interface SlideComponentSpec {
|
|
412
|
+
/**
|
|
413
|
+
* Namespaced component kind, `<extension-id>/<component-name>` — e.g.
|
|
414
|
+
* "acme/pricing-sheet". The namespace makes collisions impossible and
|
|
415
|
+
* tells the install prompt where the component comes from.
|
|
416
|
+
* @pattern ^[a-z][a-z0-9-]*\/[a-z][a-z0-9-]*$
|
|
417
|
+
*/
|
|
418
|
+
kind: string;
|
|
419
|
+
/** Props for the component, validated against the schema its extension
|
|
420
|
+
* ships. Unvalidated when the extension is not installed. */
|
|
421
|
+
props?: unknown;
|
|
422
|
+
}
|
|
423
|
+
/** A table cell: text, a number (formatted per column), or a harvey ball. */
|
|
424
|
+
export type TableCell = string | number | {
|
|
425
|
+
harvey: HarveyValue;
|
|
426
|
+
};
|
|
427
|
+
export interface TableColumn {
|
|
428
|
+
header: string;
|
|
429
|
+
/** Default: numbers right, harvey center, text left. */
|
|
430
|
+
align?: 'left' | 'center' | 'right';
|
|
431
|
+
/** Formatting for numeric cells in this column. */
|
|
432
|
+
valueStyle?: 'plain' | 'compact' | 'percent';
|
|
433
|
+
}
|
|
434
|
+
export interface TableSpec {
|
|
435
|
+
columns: TableColumn[];
|
|
436
|
+
rows: TableCell[][];
|
|
437
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { WebSlideSpec } from './layouts';
|
|
2
|
+
/** Everything in `html` + `css` + `js` together. A body larger than this is
|
|
3
|
+
* a mistake: it lands in deck.json, which is loaded, patched and diffed. */
|
|
4
|
+
export declare const WEB_BODY_MAX_BYTES: number;
|
|
5
|
+
/**
|
|
6
|
+
* Where an image may come from. A data URI, or the app's own root-relative
|
|
7
|
+
* space (`/project/assets/…`, `/logo.svg`) — which is also what the
|
|
8
|
+
* standalone export knows how to inline. Everything else is the network, and
|
|
9
|
+
* the network is not available to a slide (WEB-MODE.md rule 6).
|
|
10
|
+
*/
|
|
11
|
+
export declare function isSafeUrl(value: string): boolean;
|
|
12
|
+
/**
|
|
13
|
+
* Everything wrong with one web body, phrased for the model — the same
|
|
14
|
+
* round-trip that carries schema errors and fit findings back to it. `prefix` addresses the slide
|
|
15
|
+
* the way validation messages do: `slides[4].props`.
|
|
16
|
+
*/
|
|
17
|
+
export declare function lintWebBody(
|
|
18
|
+
/** `js` is tier B and not in the schema yet;
|
|
19
|
+
* the checks for it are written here because the day it lands is the wrong
|
|
20
|
+
* day to be inventing the message a model gets back. */
|
|
21
|
+
props: WebSlideSpec['props'] & {
|
|
22
|
+
js?: string;
|
|
23
|
+
}, prefix: string): string[];
|
|
24
|
+
/**
|
|
25
|
+
* Rewrite the author's stylesheet so every rule applies inside `scope` and
|
|
26
|
+
* nowhere else: one slide cannot restyle another slide, the chrome, or the
|
|
27
|
+
* editor around it.
|
|
28
|
+
*
|
|
29
|
+
* `:root`, `html` and `body` are rewritten to the scope itself rather than
|
|
30
|
+
* dropped — a body that declares its own custom properties on `:root` is
|
|
31
|
+
* doing the right thing, and it should land on the box it owns.
|
|
32
|
+
*/
|
|
33
|
+
export declare function scopeWebCss(css: string, scope: string): string;
|
|
34
|
+
/**
|
|
35
|
+
* The author's markup, rebuilt from a parsed tree with everything the body
|
|
36
|
+
* may not have removed. Browser only (it needs a parser); the lint above is
|
|
37
|
+
* what runs everywhere else.
|
|
38
|
+
*
|
|
39
|
+
* This is a *correctness* boundary rather than a security one — a tier-A
|
|
40
|
+
* body has no scripts by construction, so there is nothing to contain. Its
|
|
41
|
+
* job is to stop a confused model from breaking the page around it.
|
|
42
|
+
*
|
|
43
|
+
* `text` fills the elements the body marked `data-edit="text.…"`: the copy
|
|
44
|
+
* lives in the deck, so editing a line is a write to `props.text` and the
|
|
45
|
+
* markup never has to be rewritten.
|
|
46
|
+
*/
|
|
47
|
+
export declare function sanitizeWebHtml(html: string, text?: Record<string, string>): string;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export type NumberStyle = 'plain' | 'compact' | 'percent';
|
|
2
|
+
export interface NumberFormatOptions {
|
|
3
|
+
style?: NumberStyle;
|
|
4
|
+
/** Fraction digits. Defaults: plain 0, compact 1, percent auto (see below). */
|
|
5
|
+
decimals?: number;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* plain: 1234567 → "1,234,567"
|
|
9
|
+
* compact: 1234567 → "1.2M" (k / M / B)
|
|
10
|
+
* percent: 0.423 → "42%" (input is a fraction)
|
|
11
|
+
*/
|
|
12
|
+
export declare function formatNumber(value: number, opts?: NumberFormatOptions): string;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The platform surface an installed theme pack builds against (ui.md A9).
|
|
3
|
+
*
|
|
4
|
+
* A compiled-in set under src/sets/ imports whatever it likes by relative
|
|
5
|
+
* path. A pack cannot: it is built separately, ships as one prebuilt ESM
|
|
6
|
+
* file, and has to keep working against a later app. So the things a theme
|
|
7
|
+
* may reach for are enumerated here, and this module *is* the ABI — adding
|
|
8
|
+
* to it is a minor `PLATFORM_ABI` bump, removing from it a major one.
|
|
9
|
+
*
|
|
10
|
+
* Pack code imports it as `@folienbaukasten/platform`; tools/pack-set.ts
|
|
11
|
+
* rewrites that specifier to a shim reading the live objects off
|
|
12
|
+
* `globalThis` (src/sets/runtime.ts), because a pack cannot bundle its own
|
|
13
|
+
* React — two React copies in one page is two renderers, and the shared
|
|
14
|
+
* components below would be built by the wrong one.
|
|
15
|
+
*
|
|
16
|
+
* Everything here is *rendering* surface. Nothing gives a theme access to
|
|
17
|
+
* the project, the file system, or IPC — a theme styles slides.
|
|
18
|
+
*/
|
|
19
|
+
export { Block } from '../components/Block';
|
|
20
|
+
export { BlockArrow } from '../components/BlockArrow';
|
|
21
|
+
export { Card } from '../components/Card';
|
|
22
|
+
export { ChartView } from '../components/ChartView';
|
|
23
|
+
export { DriverTree } from '../components/DriverTree';
|
|
24
|
+
export { FindingsBanner } from '../components/FindingsBanner';
|
|
25
|
+
export { HarveyBall, HARVEY_SIZE_PX, HARVEY_STROKE_PX } from '../components/HarveyBall';
|
|
26
|
+
export { headerTitle } from '../components/HeaderTitle';
|
|
27
|
+
export { InsightRail } from '../components/InsightRail';
|
|
28
|
+
export { KpiTile } from '../components/KpiTile';
|
|
29
|
+
export { Matrix } from '../components/Matrix';
|
|
30
|
+
export { QuoteColumn } from '../components/QuoteColumn';
|
|
31
|
+
export { SourceLine } from '../components/SourceLine';
|
|
32
|
+
export { Table } from '../components/Table';
|
|
33
|
+
export { Timeline } from '../components/Timeline';
|
|
34
|
+
export { ContentSlide } from '../components/slide/ContentSlide';
|
|
35
|
+
export { SlideFrame } from '../components/slide/SlideFrame';
|
|
36
|
+
export { platformDefaults } from '../components/slide/registry';
|
|
37
|
+
export { formatNumber } from '../lib/format';
|
|
38
|
+
export { SLIDE_HEIGHT, SLIDE_WIDTH } from '../deck/schema';
|
|
39
|
+
export { bandCentre, bandCentreX, bandCentreY, spreadLabels, valueY, LABEL_MIN_GAP, } from '../charts/geometry';
|
|
40
|
+
export type { ComponentSet, SetChrome, SlideView, SlideViewProps, LayoutName } from './types';
|
|
41
|
+
export type { NumberFormatOptions, NumberStyle } from '../lib/format';
|
|
42
|
+
export type * from '../deck/layouts';
|
|
43
|
+
export type { DeckMeta, HeaderSpec, SlideSpec } from '../deck/schema';
|
|
44
|
+
export type { PlotArea } from '../charts/geometry';
|
|
45
|
+
export type { ChartOrientation, ChartSeries, ChartSpec, ChartTone, ChartType, } from '../deck/schema';
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import type { ComponentType } from 'react';
|
|
2
|
+
import type { SharedSlide } from '../deck/layouts';
|
|
3
|
+
import type { DeckMeta, HeaderSpec, SlideSpec } from '../deck/schema';
|
|
4
|
+
/**
|
|
5
|
+
* A component set is a swappable *styling* layer: chrome, tokens, and
|
|
6
|
+
* optional per-layout view overrides. The layout vocabulary is the
|
|
7
|
+
* platform's (src/deck/layouts.ts) — sets own no layouts, so any deck
|
|
8
|
+
* renders under any set by construction. The PPTX/PDF export works for
|
|
9
|
+
* every set unchanged, because sets render into the platform's measured
|
|
10
|
+
* `data-pptx` primitives — that tagging contract is the platform ABI
|
|
11
|
+
* (see src/export/types.ts).
|
|
12
|
+
*/
|
|
13
|
+
/** Every layout in the shared vocabulary. */
|
|
14
|
+
export type LayoutName = SharedSlide['layout'];
|
|
15
|
+
/** The chrome a set must supply: header and footer of every content slide.
|
|
16
|
+
* The frame, the bands and the overflow contract are platform-owned and not
|
|
17
|
+
* reachable from a set.
|
|
18
|
+
*
|
|
19
|
+
* The header receives `meta` as well as its own spec: deck-level marks —
|
|
20
|
+
* `meta.logoUrl` — belong to the document, not to a slide, and a header that
|
|
21
|
+
* had to be told the logo on every slide would be a layout prop pretending
|
|
22
|
+
* to be branding. A set with no place for one simply ignores it. */
|
|
23
|
+
export interface SetChrome {
|
|
24
|
+
Header: ComponentType<{
|
|
25
|
+
spec: HeaderSpec;
|
|
26
|
+
meta: DeckMeta;
|
|
27
|
+
}>;
|
|
28
|
+
Footer: ComponentType<{
|
|
29
|
+
meta: DeckMeta;
|
|
30
|
+
slideNumber: number;
|
|
31
|
+
}>;
|
|
32
|
+
}
|
|
33
|
+
/** Props every slide-layout component receives. */
|
|
34
|
+
export interface SlideViewProps<S extends SlideSpec = SlideSpec> {
|
|
35
|
+
slide: S;
|
|
36
|
+
meta: DeckMeta;
|
|
37
|
+
/** 1-based slide number, for footers. */
|
|
38
|
+
slideNumber: number;
|
|
39
|
+
/** The active set's chrome, passed through to `ContentSlide`. */
|
|
40
|
+
chrome: SetChrome;
|
|
41
|
+
/** Extension components reachable from this deck (`ResolvedSet.components`).
|
|
42
|
+
* Only the `component` layout reads it; optional so a view is renderable
|
|
43
|
+
* without it. */
|
|
44
|
+
components?: SetComponents;
|
|
45
|
+
}
|
|
46
|
+
/** A component that renders one layout. */
|
|
47
|
+
export type SlideView = ComponentType<SlideViewProps<any>>;
|
|
48
|
+
/**
|
|
49
|
+
* An extension-supplied slide body: hosted by the platform's `component` layout in the
|
|
50
|
+
* bounded body region. It owns a box, never the slide — the chrome bands
|
|
51
|
+
* and the overflow contract are not reachable from it. A component that
|
|
52
|
+
* renders measurable `data-pptx` primitives works in both modes; one that
|
|
53
|
+
* is interactive, animated or canvas-drawn declares `webOnly` and only
|
|
54
|
+
* validates in a Web Mode deck.
|
|
55
|
+
*/
|
|
56
|
+
export interface SetComponentDef {
|
|
57
|
+
component: ComponentType<{
|
|
58
|
+
props: unknown;
|
|
59
|
+
}>;
|
|
60
|
+
/** Needs the Web Mode validation profile. Default false. */
|
|
61
|
+
webOnly?: boolean;
|
|
62
|
+
}
|
|
63
|
+
/** Extension components by namespaced kind ("acme/pricing-sheet"). */
|
|
64
|
+
export type SetComponents = Record<string, SetComponentDef>;
|
|
65
|
+
export interface ComponentSet {
|
|
66
|
+
/** Folder name under src/sets/ — referenced by deck meta.componentSet. */
|
|
67
|
+
id: string;
|
|
68
|
+
/** Human-readable name. */
|
|
69
|
+
name: string;
|
|
70
|
+
/**
|
|
71
|
+
* One line for the theme chooser: what this theme is for, in the register
|
|
72
|
+
* a user picks by ("what a partner presents to a board"), not in tokens.
|
|
73
|
+
*/
|
|
74
|
+
description?: string;
|
|
75
|
+
/**
|
|
76
|
+
* A theme we offer to users. Sets without it are regression fixtures —
|
|
77
|
+
* they still render every deck, they are just not something anyone should
|
|
78
|
+
* be *choosing* (BUGS.md, production vs fixture separation). The catalog
|
|
79
|
+
* generator carries this through so the chooser and the theme panel can
|
|
80
|
+
* filter on it.
|
|
81
|
+
*/
|
|
82
|
+
production?: boolean;
|
|
83
|
+
/**
|
|
84
|
+
* Header/footer chrome rendered by the platform's ContentSlide.
|
|
85
|
+
*
|
|
86
|
+
* Optional for exactly one shape of pack: a components-only extension
|
|
87
|
+
* (`provides: ['components']`), which ships slide
|
|
88
|
+
* bodies and no styling layer. Such a set is never a deck's theme — the
|
|
89
|
+
* loader imports it only through `loadExtensionComponents` — so nothing
|
|
90
|
+
* ever asks it for a header. A set that *is* a theme has chrome, and
|
|
91
|
+
* `ThemeSet` is the type that says so.
|
|
92
|
+
*/
|
|
93
|
+
chrome?: SetChrome;
|
|
94
|
+
/** Layout views this set renders itself; the rest come from the platform
|
|
95
|
+
* (src/components/slide/registry.ts). May not add layouts. */
|
|
96
|
+
overrides?: Partial<Record<LayoutName, SlideView>>;
|
|
97
|
+
/**
|
|
98
|
+
* Slide components this set ships for the `component` layout, keyed by
|
|
99
|
+
* namespaced kind. A separate registry from `overrides`, because a
|
|
100
|
+
* component is not a slide view — it fills a body region the platform
|
|
101
|
+
* hands it.
|
|
102
|
+
*/
|
|
103
|
+
components?: SetComponents;
|
|
104
|
+
/**
|
|
105
|
+
* The theme keeps every HTML ↔ PPTX rule (AGENTS.md) and is safe for
|
|
106
|
+
* PowerPoint-mode decks. Default true. A web-only theme sets false and is
|
|
107
|
+
* offered only to Web Mode decks.
|
|
108
|
+
*/
|
|
109
|
+
pptxSafe?: boolean;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* A set that can dress a deck: a `ComponentSet` that actually has chrome.
|
|
113
|
+
* Every compiled-in set is one, and so is every pack that provides a theme.
|
|
114
|
+
* Requiring it at the door of `resolveSet` is what keeps the optionality on
|
|
115
|
+
* `ComponentSet.chrome` confined to the components-only case that needs it.
|
|
116
|
+
*/
|
|
117
|
+
export type ThemeSet = ComponentSet & {
|
|
118
|
+
chrome: SetChrome;
|
|
119
|
+
};
|
|
120
|
+
/** A set with its registry resolved: platform defaults + the set's
|
|
121
|
+
* overrides, and the extension components reachable from this deck (the
|
|
122
|
+
* set's own plus every installed components pack). What the renderer and
|
|
123
|
+
* export paths consume. */
|
|
124
|
+
export interface ResolvedSet extends ComponentSet {
|
|
125
|
+
chrome: SetChrome;
|
|
126
|
+
registry: Record<string, SlideView>;
|
|
127
|
+
components: SetComponents;
|
|
128
|
+
}
|