@classic-homes/slides 0.7.0 → 0.8.1
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 +21 -3
- package/dist/chunk-4NSKDQ5V.js +7 -0
- package/dist/chunk-4NSKDQ5V.js.map +1 -0
- package/dist/{chunk-HI5ZCAHO.js → chunk-R5HURANW.js} +87 -84
- package/dist/chunk-R5HURANW.js.map +1 -0
- package/dist/{chunk-UKRX7A2V.js → chunk-RWMCIAIW.js} +5 -3
- package/dist/chunk-RWMCIAIW.js.map +1 -0
- package/dist/{chunk-ORNJ4CTQ.js → chunk-TB4H2HYA.js} +2 -2
- package/dist/chunk-WTTBHOBM.js +38 -0
- package/dist/chunk-WTTBHOBM.js.map +1 -0
- package/dist/cli.js +7 -5
- package/dist/cli.js.map +1 -1
- package/dist/core.d.ts +235 -0
- package/dist/core.js +147 -0
- package/dist/core.js.map +1 -0
- package/dist/{images-rkJZtnjl.d.ts → images-G0nQyKJE.d.ts} +121 -1
- package/dist/index.d.ts +13 -320
- package/dist/index.js +17 -7
- package/dist/index.js.map +1 -1
- package/dist/{mcp-MG2E7J3I.js → mcp-FYZGFSBM.js} +5 -4
- package/dist/{mcp-MG2E7J3I.js.map → mcp-FYZGFSBM.js.map} +1 -1
- package/dist/node.d.ts +2 -2
- package/dist/node.js +3 -2
- package/dist/render-default-0SAePU6r.d.ts +5 -0
- package/package.json +5 -1
- package/dist/chunk-HI5ZCAHO.js.map +0 -1
- package/dist/chunk-UKRX7A2V.js.map +0 -1
- /package/dist/{chunk-ORNJ4CTQ.js.map → chunk-TB4H2HYA.js.map} +0 -0
package/README.md
CHANGED
|
@@ -96,7 +96,8 @@ Every slide also accepts `track` (value/red, build/teal, neutral/navy),
|
|
|
96
96
|
|
|
97
97
|
Charts are rendered to static SVG with ECharts (server-side rendering, themed
|
|
98
98
|
from `@classic-homes/charts-core`), so they work offline and in print. ECharts
|
|
99
|
-
is loaded only when a deck has charts
|
|
99
|
+
is loaded only when a deck has charts, and `/core` leaves it out entirely (see
|
|
100
|
+
[As a library](#as-a-library)).
|
|
100
101
|
|
|
101
102
|
### Video and embeds
|
|
102
103
|
|
|
@@ -160,8 +161,24 @@ import { buildDeckFile, fileImageResolver } from '@classic-homes/slides/node';
|
|
|
160
161
|
const { html } = await renderDeck(deck, { resolveImage: (src) => lookUp(src) });
|
|
161
162
|
```
|
|
162
163
|
|
|
163
|
-
The
|
|
164
|
-
`/node` adds file-based image loading and `buildDeckFile`.
|
|
164
|
+
The main entry has no Node dependencies (it runs in Workers and browsers);
|
|
165
|
+
`/node` adds file-based image loading and `buildDeckFile`.
|
|
166
|
+
|
|
167
|
+
`/core` is the main entry without ECharts (about 650 KB minified), for bundles
|
|
168
|
+
that must stay small, such as a Cloudflare Worker. Its `renderDeck` draws charts
|
|
169
|
+
with the `renderCharts` option you pass: a `ChartSvgRenderer` that gets every
|
|
170
|
+
chart in the deck in one call (`{ spec, width, height, palette?, title? }`,
|
|
171
|
+
plain data that survives RPC) and returns their SVGs in the same order. Run
|
|
172
|
+
`renderChartSvgs` from the main entry wherever ECharts can live (another Worker,
|
|
173
|
+
a server) and return its output. A deck with charts and no renderer throws.
|
|
174
|
+
|
|
175
|
+
````ts
|
|
176
|
+
import { renderDeck } from '@classic-homes/slides/core';
|
|
177
|
+
|
|
178
|
+
const { html } = await renderDeck(deck, {
|
|
179
|
+
renderCharts: (charts) => env.CHARTS.renderSlideChartSvgs(charts),
|
|
180
|
+
});
|
|
181
|
+
``` For editors,
|
|
165
182
|
`layoutEditorInfo()` / `deckEditorFields()` describe every field as a widget
|
|
166
183
|
(with `hint`s for chart, grid, url, video-url and point pickers),
|
|
167
184
|
`blankSlide(layout)` gives a valid starting slide, and `renderDeck(deck,
|
|
@@ -199,3 +216,4 @@ Colours come from `@classic-homes/theme-tokens`. Fonts (Figtree, Spectral) are
|
|
|
199
216
|
embedded from `assets/fonts`. Styling lives in `src/styles.ts`; add a layout in
|
|
200
217
|
`src/layouts/` and register it in `src/layouts/index.ts` — its example is
|
|
201
218
|
tested automatically.
|
|
219
|
+
````
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/generated/runtime-hash.ts"],"sourcesContent":["// Generated by scripts/embed-fonts.ts. Do not edit.\n/** CSP source for the inline presenter runtime: script-src '<hash>'. */\nexport const RUNTIME_SCRIPT_HASH = 'sha256-gElvQAb4VPvKu4UeyJRVKuoHYNp8NGhXwx2EhL7NB88=';\n"],"mappings":";AAEO,IAAM,sBAAsB;","names":[]}
|
|
@@ -147,6 +147,69 @@ function validateBrandProfile(input) {
|
|
|
147
147
|
return { ok: true, profile: parsed.data };
|
|
148
148
|
}
|
|
149
149
|
|
|
150
|
+
// src/inline.ts
|
|
151
|
+
var ESCAPES = {
|
|
152
|
+
"&": "&",
|
|
153
|
+
"<": "<",
|
|
154
|
+
">": ">",
|
|
155
|
+
'"': """,
|
|
156
|
+
"'": "'"
|
|
157
|
+
};
|
|
158
|
+
function escapeHtml(value) {
|
|
159
|
+
return value.replace(/[&<>"']/g, (c) => ESCAPES[c]);
|
|
160
|
+
}
|
|
161
|
+
function escapeAttr(value) {
|
|
162
|
+
return escapeHtml(value);
|
|
163
|
+
}
|
|
164
|
+
function escapeCssString(value) {
|
|
165
|
+
return value.replace(/["'\\\n\r\f]/g, (c) => `\\${c.charCodeAt(0).toString(16)} `);
|
|
166
|
+
}
|
|
167
|
+
var SAFE_URL = /^(https?:|mailto:|tel:|#|\/)/i;
|
|
168
|
+
function isSafeLinkUrl(url) {
|
|
169
|
+
return SAFE_URL.test(url);
|
|
170
|
+
}
|
|
171
|
+
function inline(text) {
|
|
172
|
+
if (!text) return "";
|
|
173
|
+
text = text.replace(/[\uE000\uE001]/g, "");
|
|
174
|
+
if (!text) return "";
|
|
175
|
+
const codes = [];
|
|
176
|
+
let s = text.replace(/`([^`]+)`/g, (_, code) => {
|
|
177
|
+
codes.push(code);
|
|
178
|
+
return `\uE000${codes.length - 1}\uE000`;
|
|
179
|
+
});
|
|
180
|
+
const links = [];
|
|
181
|
+
s = s.replace(/\[([^[\]]+)\]\(([^)\s[]+)\)/g, (match, label, url) => {
|
|
182
|
+
if (!isSafeLinkUrl(url)) return label;
|
|
183
|
+
links.push(`<a href="${escapeAttr(url)}">${formatEmphasis(escapeHtml(label))}</a>`);
|
|
184
|
+
return `\uE001${links.length - 1}\uE001`;
|
|
185
|
+
});
|
|
186
|
+
s = formatEmphasis(escapeHtml(s));
|
|
187
|
+
s = s.replace(/\r?\n/g, "<br>");
|
|
188
|
+
s = s.replace(/\uE001(\d+)\uE001/g, (_, i) => links[Number(i)]);
|
|
189
|
+
s = s.replace(
|
|
190
|
+
/\uE000(\d+)\uE000/g,
|
|
191
|
+
(_, i) => `<code>${escapeHtml(codes[Number(i)])}</code>`
|
|
192
|
+
);
|
|
193
|
+
return s;
|
|
194
|
+
}
|
|
195
|
+
function formatEmphasis(escaped) {
|
|
196
|
+
return escaped.replace(/\*\*(.+?)\*\*/g, "<b>$1</b>").replace(/(^|[^*])\*(?!\s)([^*\n]+?)(?<!\s)\*(?!\*)/g, '$1<em class="hl">$2</em>');
|
|
197
|
+
}
|
|
198
|
+
function prose(text) {
|
|
199
|
+
if (!text) return "";
|
|
200
|
+
return text.trim().split(/\n\s*\n/).map((para) => {
|
|
201
|
+
const lines = para.split(/\n/);
|
|
202
|
+
if (lines.every((l) => /^\s*[-*]\s+/.test(l))) {
|
|
203
|
+
return `<ul>${lines.map((l) => `<li>${inline(l.replace(/^\s*[-*]\s+/, ""))}</li>`).join("")}</ul>`;
|
|
204
|
+
}
|
|
205
|
+
return `<p>${inline(para)}</p>`;
|
|
206
|
+
}).join("");
|
|
207
|
+
}
|
|
208
|
+
function plain(text) {
|
|
209
|
+
if (!text) return "";
|
|
210
|
+
return text.replace(/`([^`]+)`/g, "$1").replace(/\[([^[\]]+)\]\([^)[]+\)/g, "$1").replace(/\*\*(.+?)\*\*/g, "$1").replace(/\*(.+?)\*/g, "$1");
|
|
211
|
+
}
|
|
212
|
+
|
|
150
213
|
// src/theme.ts
|
|
151
214
|
import { colors as colors2 } from "@classic-homes/theme-tokens";
|
|
152
215
|
|
|
@@ -1062,17 +1125,8 @@ ${fmt(p.value)}` : p.name
|
|
|
1062
1125
|
})
|
|
1063
1126
|
};
|
|
1064
1127
|
}
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
renderer ??= import("./echarts-render-GDCT5QAF.js").then((m) => m.createRenderer());
|
|
1068
|
-
return renderer;
|
|
1069
|
-
}
|
|
1070
|
-
async function renderChartSvg(spec, opts = {}) {
|
|
1071
|
-
const width = opts.width ?? 960;
|
|
1072
|
-
const height = opts.height ?? 520;
|
|
1073
|
-
const r = await loadChartRenderer();
|
|
1074
|
-
const svg = r.render(chartOption(spec, opts.palette), width, height);
|
|
1075
|
-
const label = `${opts.title ? `${opts.title}: ` : ""}${describeChart(spec)}`.replace(
|
|
1128
|
+
function finishChartSvg(svg, spec, title2) {
|
|
1129
|
+
const label = `${title2 ? `${title2}: ` : ""}${describeChart(spec)}`.replace(
|
|
1076
1130
|
/[&<>"]/g,
|
|
1077
1131
|
(c) => `&#${c.charCodeAt(0)};`
|
|
1078
1132
|
);
|
|
@@ -1098,69 +1152,6 @@ function describeChart(spec) {
|
|
|
1098
1152
|
return `${kind} ${parts.join(". ")}.`;
|
|
1099
1153
|
}
|
|
1100
1154
|
|
|
1101
|
-
// src/inline.ts
|
|
1102
|
-
var ESCAPES = {
|
|
1103
|
-
"&": "&",
|
|
1104
|
-
"<": "<",
|
|
1105
|
-
">": ">",
|
|
1106
|
-
'"': """,
|
|
1107
|
-
"'": "'"
|
|
1108
|
-
};
|
|
1109
|
-
function escapeHtml(value) {
|
|
1110
|
-
return value.replace(/[&<>"']/g, (c) => ESCAPES[c]);
|
|
1111
|
-
}
|
|
1112
|
-
function escapeAttr(value) {
|
|
1113
|
-
return escapeHtml(value);
|
|
1114
|
-
}
|
|
1115
|
-
function escapeCssString(value) {
|
|
1116
|
-
return value.replace(/["'\\\n\r\f]/g, (c) => `\\${c.charCodeAt(0).toString(16)} `);
|
|
1117
|
-
}
|
|
1118
|
-
var SAFE_URL = /^(https?:|mailto:|tel:|#|\/)/i;
|
|
1119
|
-
function isSafeLinkUrl(url) {
|
|
1120
|
-
return SAFE_URL.test(url);
|
|
1121
|
-
}
|
|
1122
|
-
function inline(text) {
|
|
1123
|
-
if (!text) return "";
|
|
1124
|
-
text = text.replace(/[\uE000\uE001]/g, "");
|
|
1125
|
-
if (!text) return "";
|
|
1126
|
-
const codes = [];
|
|
1127
|
-
let s = text.replace(/`([^`]+)`/g, (_, code) => {
|
|
1128
|
-
codes.push(code);
|
|
1129
|
-
return `\uE000${codes.length - 1}\uE000`;
|
|
1130
|
-
});
|
|
1131
|
-
const links = [];
|
|
1132
|
-
s = s.replace(/\[([^[\]]+)\]\(([^)\s[]+)\)/g, (match, label, url) => {
|
|
1133
|
-
if (!isSafeLinkUrl(url)) return label;
|
|
1134
|
-
links.push(`<a href="${escapeAttr(url)}">${formatEmphasis(escapeHtml(label))}</a>`);
|
|
1135
|
-
return `\uE001${links.length - 1}\uE001`;
|
|
1136
|
-
});
|
|
1137
|
-
s = formatEmphasis(escapeHtml(s));
|
|
1138
|
-
s = s.replace(/\r?\n/g, "<br>");
|
|
1139
|
-
s = s.replace(/\uE001(\d+)\uE001/g, (_, i) => links[Number(i)]);
|
|
1140
|
-
s = s.replace(
|
|
1141
|
-
/\uE000(\d+)\uE000/g,
|
|
1142
|
-
(_, i) => `<code>${escapeHtml(codes[Number(i)])}</code>`
|
|
1143
|
-
);
|
|
1144
|
-
return s;
|
|
1145
|
-
}
|
|
1146
|
-
function formatEmphasis(escaped) {
|
|
1147
|
-
return escaped.replace(/\*\*(.+?)\*\*/g, "<b>$1</b>").replace(/(^|[^*])\*(?!\s)([^*\n]+?)(?<!\s)\*(?!\*)/g, '$1<em class="hl">$2</em>');
|
|
1148
|
-
}
|
|
1149
|
-
function prose(text) {
|
|
1150
|
-
if (!text) return "";
|
|
1151
|
-
return text.trim().split(/\n\s*\n/).map((para) => {
|
|
1152
|
-
const lines = para.split(/\n/);
|
|
1153
|
-
if (lines.every((l) => /^\s*[-*]\s+/.test(l))) {
|
|
1154
|
-
return `<ul>${lines.map((l) => `<li>${inline(l.replace(/^\s*[-*]\s+/, ""))}</li>`).join("")}</ul>`;
|
|
1155
|
-
}
|
|
1156
|
-
return `<p>${inline(para)}</p>`;
|
|
1157
|
-
}).join("");
|
|
1158
|
-
}
|
|
1159
|
-
function plain(text) {
|
|
1160
|
-
if (!text) return "";
|
|
1161
|
-
return text.replace(/`([^`]+)`/g, "$1").replace(/\[([^[\]]+)\]\([^)[]+\)/g, "$1").replace(/\*\*(.+?)\*\*/g, "$1").replace(/\*(.+?)\*/g, "$1");
|
|
1162
|
-
}
|
|
1163
|
-
|
|
1164
1155
|
// src/video.ts
|
|
1165
1156
|
var YT = /^(?:https:\/\/)?(?:www\.|m\.)?(?:youtube\.com\/(?:watch\?(?:.*&)?v=|embed\/|shorts\/|live\/)|youtu\.be\/)([\w-]{11})/;
|
|
1166
1157
|
var VIMEO = /^(?:https:\/\/)?(?:www\.|player\.)?vimeo\.com\/(?:video\/)?(\d{6,12})/;
|
|
@@ -4045,15 +4036,25 @@ async function renderDeck(input, options = {}) {
|
|
|
4045
4036
|
})
|
|
4046
4037
|
);
|
|
4047
4038
|
const charts = /* @__PURE__ */ new Map();
|
|
4048
|
-
|
|
4049
|
-
|
|
4050
|
-
|
|
4051
|
-
|
|
4039
|
+
if (chartReqs.size) {
|
|
4040
|
+
if (!options.renderCharts) {
|
|
4041
|
+
throw new Error(
|
|
4042
|
+
"This deck has charts but no chart renderer: pass options.renderCharts (the @classic-homes/slides/core entry has no built-in one)."
|
|
4043
|
+
);
|
|
4044
|
+
}
|
|
4045
|
+
const reqs = [...chartReqs.values()];
|
|
4046
|
+
const svgs = await options.renderCharts(
|
|
4047
|
+
reqs.map((req) => ({
|
|
4048
|
+
spec: req.spec,
|
|
4052
4049
|
width: req.width,
|
|
4053
4050
|
height: req.height,
|
|
4054
4051
|
palette: brand.stock ? void 0 : brand.palette
|
|
4055
|
-
})
|
|
4052
|
+
}))
|
|
4056
4053
|
);
|
|
4054
|
+
if (svgs.length !== reqs.length) {
|
|
4055
|
+
throw new Error(`renderCharts returned ${svgs.length} SVGs for ${reqs.length} charts.`);
|
|
4056
|
+
}
|
|
4057
|
+
reqs.forEach((req, i) => charts.set(req.key, svgs[i]));
|
|
4057
4058
|
}
|
|
4058
4059
|
const imageBudget = options.maxInlineImageBytes;
|
|
4059
4060
|
if (imageBudget !== void 0 && (!Number.isFinite(imageBudget) || imageBudget < 0)) {
|
|
@@ -4498,19 +4499,21 @@ export {
|
|
|
4498
4499
|
resolveBrand,
|
|
4499
4500
|
checkPalette,
|
|
4500
4501
|
validateBrandProfile,
|
|
4502
|
+
escapeHtml,
|
|
4503
|
+
inline,
|
|
4504
|
+
prose,
|
|
4501
4505
|
PALETTE,
|
|
4502
4506
|
LOGO_SVG,
|
|
4503
4507
|
logoLockup,
|
|
4504
4508
|
CHART_TYPES,
|
|
4505
4509
|
SINGLE_SERIES_CHARTS,
|
|
4506
4510
|
ChartSpec,
|
|
4511
|
+
chartIssues,
|
|
4512
|
+
chartWarnings,
|
|
4507
4513
|
formatValue,
|
|
4508
4514
|
chartOption,
|
|
4509
|
-
|
|
4515
|
+
finishChartSvg,
|
|
4510
4516
|
describeChart,
|
|
4511
|
-
escapeHtml,
|
|
4512
|
-
inline,
|
|
4513
|
-
prose,
|
|
4514
4517
|
parseVideoUrl,
|
|
4515
4518
|
videoEmbedUrl,
|
|
4516
4519
|
LAYOUTS,
|
|
@@ -4553,4 +4556,4 @@ export {
|
|
|
4553
4556
|
inlineDeckImages,
|
|
4554
4557
|
externalizeDeckImages
|
|
4555
4558
|
};
|
|
4556
|
-
//# sourceMappingURL=chunk-
|
|
4559
|
+
//# sourceMappingURL=chunk-R5HURANW.js.map
|