@braincrew-lab/langchain-canvas 0.1.13 → 0.2.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.
@@ -1,492 +0,0 @@
1
- import { resolveElements } from './chunk-6L3AL6W4.js';
2
- import { useArtifactPatch } from './chunk-TERWLUW3.js';
3
- import './chunk-KKLWKR5G.js';
4
- import { useState, useRef, useEffect } from 'react';
5
- import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
6
-
7
- function shapeStyle(el) {
8
- const fill = el.fill ?? "currentColor";
9
- if (el.shape === "ellipse") return { width: "100%", height: "100%", background: fill, borderRadius: "50%" };
10
- if (el.shape === "line") return { width: "100%", height: "100%", background: fill, borderRadius: 2 };
11
- return { width: "100%", height: "100%", background: fill, borderRadius: 8 };
12
- }
13
- var clamp = (v, min, max) => Math.max(min, Math.min(max, v));
14
- var dupSeq = 0;
15
- var dupId = (base) => `${base}_c${Date.now().toString(36)}${dupSeq++}`;
16
- var SNAP = 1.2;
17
- function snapAxis(pos, size, targets) {
18
- const anchors = [pos, pos + size / 2, pos + size];
19
- let best = null;
20
- for (const anchor of anchors) {
21
- for (const t of targets) {
22
- const delta = t - anchor;
23
- if (Math.abs(delta) <= SNAP && (!best || Math.abs(delta) < Math.abs(best.delta))) {
24
- best = { delta, guide: t };
25
- }
26
- }
27
- }
28
- return best ? { pos: pos + best.delta, guide: best.guide } : { pos, guide: null };
29
- }
30
- function FreeSlide({ elements, onChange, padding }) {
31
- const slideRef = useRef(null);
32
- const [els, setEls] = useState(elements);
33
- const [selected, setSelected] = useState(null);
34
- const [editingId, setEditingId] = useState(null);
35
- const [guides, setGuides] = useState({ x: null, y: null });
36
- const drag = useRef(null);
37
- useEffect(() => {
38
- if (!drag.current) setEls(elements);
39
- }, [elements]);
40
- const commit = (next) => {
41
- setEls(next);
42
- onChange(next);
43
- };
44
- const updateEl = (id, partial) => commit(els.map((el) => el.id === id ? { ...el, ...partial } : el));
45
- const duplicate = (el) => {
46
- const copy = {
47
- ...el,
48
- id: dupId(el.id),
49
- x: Math.min(el.x + 4, 100 - el.w),
50
- y: Math.min(el.y + 4, 100 - el.h)
51
- };
52
- commit([...els, copy]);
53
- setSelected(copy.id);
54
- };
55
- const zorder = (id, dir) => {
56
- const i = els.findIndex((e) => e.id === id);
57
- const j = i + dir;
58
- if (i < 0 || j < 0 || j >= els.length) return;
59
- const next = [...els];
60
- [next[i], next[j]] = [next[j], next[i]];
61
- commit(next);
62
- };
63
- const onDown = (e, el, mode) => {
64
- if (editingId === el.id && mode === "move") return;
65
- e.preventDefault();
66
- e.stopPropagation();
67
- setSelected(el.id);
68
- drag.current = { id: el.id, mode, sx: e.clientX, sy: e.clientY, orig: { ...el } };
69
- e.currentTarget.setPointerCapture(e.pointerId);
70
- };
71
- const onMove = (e) => {
72
- const d = drag.current;
73
- const rect = slideRef.current?.getBoundingClientRect();
74
- if (!d || !rect) return;
75
- const dx = (e.clientX - d.sx) / rect.width * 100;
76
- const dy = (e.clientY - d.sy) / rect.height * 100;
77
- if (d.mode === "resize") {
78
- setGuides({ x: null, y: null });
79
- setEls((prev) => prev.map((el) => el.id === d.id ? { ...el, w: clamp(d.orig.w + dx, 6, 100 - el.x), h: clamp(d.orig.h + dy, 5, 100 - el.y) } : el));
80
- return;
81
- }
82
- const others = els.filter((el) => el.id !== d.id);
83
- const xTargets = [0, 50, 100, ...others.flatMap((o) => [o.x, o.x + o.w / 2, o.x + o.w])];
84
- const yTargets = [0, 50, 100, ...others.flatMap((o) => [o.y, o.y + o.h / 2, o.y + o.h])];
85
- const rawX = clamp(d.orig.x + dx, 0, 100 - d.orig.w);
86
- const rawY = clamp(d.orig.y + dy, 0, 100 - d.orig.h);
87
- const sx = snapAxis(rawX, d.orig.w, xTargets);
88
- const sy = snapAxis(rawY, d.orig.h, yTargets);
89
- setGuides({ x: sx.guide, y: sy.guide });
90
- setEls(
91
- (prev) => prev.map(
92
- (el) => el.id === d.id ? { ...el, x: clamp(sx.pos, 0, 100 - el.w), y: clamp(sy.pos, 0, 100 - el.h) } : el
93
- )
94
- );
95
- };
96
- const onUp = () => {
97
- if (drag.current) {
98
- drag.current = null;
99
- setGuides({ x: null, y: null });
100
- onChange(els);
101
- }
102
- };
103
- useEffect(() => {
104
- if (!selected) return;
105
- const onKey = (e) => {
106
- if (editingId) return;
107
- const ae = document.activeElement;
108
- if (ae && (ae.tagName === "INPUT" || ae.tagName === "TEXTAREA" || ae.isContentEditable)) return;
109
- const el = els.find((x) => x.id === selected);
110
- if (!el) return;
111
- const step = e.shiftKey ? 5 : 1;
112
- const nudge = (ddx, ddy) => {
113
- e.preventDefault();
114
- commit(els.map((x) => x.id === selected ? { ...x, x: clamp(x.x + ddx, 0, 100 - x.w), y: clamp(x.y + ddy, 0, 100 - x.h) } : x));
115
- };
116
- if (e.key === "ArrowLeft") nudge(-step, 0);
117
- else if (e.key === "ArrowRight") nudge(step, 0);
118
- else if (e.key === "ArrowUp") nudge(0, -step);
119
- else if (e.key === "ArrowDown") nudge(0, step);
120
- else if (e.key === "Delete" || e.key === "Backspace") {
121
- e.preventDefault();
122
- commit(els.filter((x) => x.id !== selected));
123
- setSelected(null);
124
- } else if ((e.metaKey || e.ctrlKey) && (e.key === "d" || e.key === "D")) {
125
- e.preventDefault();
126
- duplicate(el);
127
- } else if (e.key === "Escape") {
128
- setSelected(null);
129
- }
130
- };
131
- window.addEventListener("keydown", onKey);
132
- return () => window.removeEventListener("keydown", onKey);
133
- }, [selected, editingId, els]);
134
- return /* @__PURE__ */ jsxs(
135
- "div",
136
- {
137
- className: "cv-free",
138
- ref: slideRef,
139
- style: padding ? { inset: `${padding}%` } : void 0,
140
- onPointerMove: onMove,
141
- onPointerUp: onUp,
142
- onPointerLeave: onUp,
143
- onClick: () => {
144
- setSelected(null);
145
- setEditingId(null);
146
- },
147
- children: [
148
- guides.x !== null && /* @__PURE__ */ jsx("span", { className: "cv-free__guide cv-free__guide--v", style: { left: `${guides.x}%` } }),
149
- guides.y !== null && /* @__PURE__ */ jsx("span", { className: "cv-free__guide cv-free__guide--h", style: { top: `${guides.y}%` } }),
150
- els.map((el) => /* @__PURE__ */ jsxs(
151
- "div",
152
- {
153
- className: `cv-free__el ${selected === el.id ? "is-selected" : ""}`,
154
- style: { left: `${el.x}%`, top: `${el.y}%`, width: `${el.w}%`, height: `${el.h}%` },
155
- onPointerDown: (e) => onDown(e, el, "move"),
156
- onDoubleClick: (e) => {
157
- if (el.type === "text") {
158
- e.stopPropagation();
159
- setEditingId(el.id);
160
- }
161
- },
162
- onClick: (e) => {
163
- e.stopPropagation();
164
- setSelected(el.id);
165
- },
166
- children: [
167
- el.type === "text" ? /* @__PURE__ */ jsx(
168
- "div",
169
- {
170
- className: "cv-free__text",
171
- contentEditable: editingId === el.id,
172
- suppressContentEditableWarning: true,
173
- style: {
174
- fontSize: el.fontSize ?? 24,
175
- fontWeight: el.bold ? 700 : 400,
176
- color: el.color,
177
- textAlign: el.align ?? "left"
178
- },
179
- onBlur: (e) => {
180
- setEditingId(null);
181
- updateEl(el.id, { text: e.currentTarget.textContent ?? "" });
182
- },
183
- children: el.text
184
- }
185
- ) : el.type === "shape" ? /* @__PURE__ */ jsx("div", { style: shapeStyle(el) }) : /* @__PURE__ */ jsx("img", { className: "cv-free__img", src: el.src, alt: "", draggable: false }),
186
- selected === el.id && el.type === "text" && /* @__PURE__ */ jsxs("div", { className: `cv-free__fmt ${el.y < 16 ? "cv-free__fmt--below" : ""}`, onPointerDown: (e) => e.stopPropagation(), onClick: (e) => e.stopPropagation(), children: [
187
- /* @__PURE__ */ jsx("button", { className: el.bold ? "is-on" : "", onClick: () => updateEl(el.id, { bold: !el.bold }), title: "Bold", children: /* @__PURE__ */ jsx("b", { children: "B" }) }),
188
- /* @__PURE__ */ jsx(
189
- "input",
190
- {
191
- type: "number",
192
- min: 8,
193
- max: 120,
194
- value: el.fontSize ?? 24,
195
- onChange: (e) => updateEl(el.id, { fontSize: Number(e.target.value) }),
196
- title: "Font size"
197
- }
198
- ),
199
- /* @__PURE__ */ jsx("input", { type: "color", value: el.color ?? "#1f2328", onChange: (e) => updateEl(el.id, { color: e.target.value }), title: "Text color" }),
200
- /* @__PURE__ */ jsx("button", { onClick: () => updateEl(el.id, { align: "left" }), title: "Align left", children: "\u27F8" }),
201
- /* @__PURE__ */ jsx("button", { onClick: () => updateEl(el.id, { align: "center" }), title: "Align center", children: "\u2261" }),
202
- /* @__PURE__ */ jsx("button", { onClick: () => updateEl(el.id, { align: "right" }), title: "Align right", children: "\u27F9" })
203
- ] }),
204
- selected === el.id && /* @__PURE__ */ jsxs(Fragment, { children: [
205
- /* @__PURE__ */ jsx("span", { className: "cv-free__resize", onPointerDown: (e) => onDown(e, el, "resize") }),
206
- /* @__PURE__ */ jsxs("div", { className: `cv-free__ctl ${el.y < 16 ? "cv-free__ctl--below" : ""}`, onPointerDown: (e) => e.stopPropagation(), children: [
207
- el.type === "shape" && /* @__PURE__ */ jsx("input", { className: "cv-free__ctl-fill", type: "color", value: el.fill ?? "#5b5bd6", onChange: (e) => updateEl(el.id, { fill: e.target.value }), onClick: (e) => e.stopPropagation(), title: "Fill color" }),
208
- /* @__PURE__ */ jsx("button", { onClick: (e) => {
209
- e.stopPropagation();
210
- duplicate(el);
211
- }, title: "Duplicate", children: "\u29C9" }),
212
- /* @__PURE__ */ jsx("button", { onClick: (e) => {
213
- e.stopPropagation();
214
- zorder(el.id, 1);
215
- }, title: "Bring forward", children: "\u2191" }),
216
- /* @__PURE__ */ jsx("button", { onClick: (e) => {
217
- e.stopPropagation();
218
- zorder(el.id, -1);
219
- }, title: "Send back", children: "\u2193" }),
220
- /* @__PURE__ */ jsx("button", { className: "cv-free__ctl-del", onClick: (e) => {
221
- e.stopPropagation();
222
- commit(els.filter((x) => x.id !== el.id));
223
- }, title: "Delete", children: "\xD7" })
224
- ] })
225
- ] })
226
- ]
227
- },
228
- el.id
229
- ))
230
- ]
231
- }
232
- );
233
- }
234
- var THEMES = [
235
- { id: "light", label: "Light", bg: "#ffffff", text: "#1f2328" },
236
- { id: "paper", label: "Paper", bg: "#f7f5ef", text: "#2b2a26" },
237
- { id: "dark", label: "Dark", bg: "#14171f", text: "#f0f2f5" },
238
- { id: "midnight", label: "Midnight", bg: "#0b1020", text: "#e6e8ef" },
239
- { id: "navy", label: "Navy", bg: "#0d1b3e", text: "#eef2ff" },
240
- { id: "forest", label: "Forest", bg: "#0f2a22", text: "#e6f2ec" },
241
- { id: "sunset", label: "Sunset", bg: "#2b1a2e", text: "#ffe8d6" },
242
- { id: "mint", label: "Mint", bg: "#0f2a24", text: "#d7f5ec" }
243
- ];
244
- var SHAPES = [
245
- { id: "rect", label: "\u25AD Rectangle", box: { w: 30, h: 20 } },
246
- { id: "ellipse", label: "\u25EF Ellipse", box: { w: 24, h: 24 } },
247
- { id: "line", label: "\u2014 Line", box: { w: 40, h: 2 } }
248
- ];
249
- var LAYOUTS = {
250
- title: { label: "Title", els: [
251
- { type: "text", x: 12, y: 34, w: 76, h: 18, text: "Presentation title", fontSize: 54, bold: true, align: "center" },
252
- { type: "text", x: 20, y: 56, w: 60, h: 10, text: "Subtitle or one-line summary", fontSize: 24, align: "center" }
253
- ] },
254
- section: { label: "Section", els: [
255
- { type: "shape", shape: "rect", x: 10, y: 34, w: 8, h: 3, fill: "#5b5bd6" },
256
- { type: "text", x: 10, y: 40, w: 80, h: 16, text: "Section title", fontSize: 46, bold: true }
257
- ] },
258
- bullets: { label: "Bullets", els: [
259
- { type: "text", x: 10, y: 12, w: 80, h: 12, text: "Heading", fontSize: 36, bold: true },
260
- { type: "text", x: 10, y: 30, w: 80, h: 50, text: "\u2022 First key point\n\u2022 Second key point\n\u2022 Third key point", fontSize: 26 }
261
- ] },
262
- "two-column": { label: "Two column", els: [
263
- { type: "text", x: 8, y: 16, w: 40, h: 60, text: "Left column\n\n\u2022 point\n\u2022 point", fontSize: 24 },
264
- { type: "text", x: 52, y: 16, w: 40, h: 60, text: "Right column\n\n\u2022 point\n\u2022 point", fontSize: 24 }
265
- ] },
266
- quote: { label: "Quote", els: [
267
- { type: "text", x: 12, y: 30, w: 76, h: 30, text: "\u201CA short, memorable quote.\u201D", fontSize: 40, bold: true },
268
- { type: "text", x: 12, y: 62, w: 50, h: 8, text: "\u2014 Attribution", fontSize: 22 }
269
- ] }
270
- };
271
- var elementSeq = 0;
272
- var newElementId = () => `el_${Date.now().toString(36)}_${elementSeq++}`;
273
- function SlidesRenderer({ artifact }) {
274
- const slides = artifact.data.slides ?? [];
275
- const patch = useArtifactPatch(artifact.id);
276
- const [index, setIndex] = useState(0);
277
- const [dragIndex, setDragIndex] = useState(null);
278
- const [presenting, setPresenting] = useState(false);
279
- const imgRef = useRef(null);
280
- const bgRef = useRef(null);
281
- useEffect(() => {
282
- if (!presenting) return;
283
- const onKey = (e) => {
284
- if (e.key === "ArrowRight" || e.key === " ") setIndex((i) => Math.min(i + 1, slides.length - 1));
285
- else if (e.key === "ArrowLeft") setIndex((i) => Math.max(i - 1, 0));
286
- else if (e.key === "Escape") setPresenting(false);
287
- };
288
- window.addEventListener("keydown", onKey);
289
- return () => window.removeEventListener("keydown", onKey);
290
- }, [presenting]);
291
- if (slides.length === 0) {
292
- return /* @__PURE__ */ jsx("div", { className: "cv-deck cv-deck--empty", children: "No slides yet\u2026" });
293
- }
294
- const at = Math.min(index, slides.length - 1);
295
- const slide = slides[at];
296
- const slideStyle = {
297
- ...slide.background ? { background: slide.background } : {},
298
- ...slide.textColor ? { color: slide.textColor } : {}
299
- };
300
- const setSlides = (next) => patch({ slides: next });
301
- const update = (partial) => setSlides(slides.map((s, i) => i === at ? { ...s, ...partial } : s));
302
- const addSlide = () => {
303
- const next = [...slides];
304
- next.splice(at + 1, 0, { elements: [{ id: newElementId(), type: "text", x: 8, y: 10, w: 80, h: 14, text: "New slide", fontSize: 36, bold: true }] });
305
- setSlides(next);
306
- setIndex(at + 1);
307
- };
308
- const duplicateSlide = () => {
309
- const next = [...slides];
310
- next.splice(at + 1, 0, { ...slide, elements: resolveElements(slide).map((e) => ({ ...e })) });
311
- setSlides(next);
312
- setIndex(at + 1);
313
- };
314
- const deleteSlide = () => {
315
- if (slides.length === 1) return;
316
- setSlides(slides.filter((_, i) => i !== at));
317
- setIndex(Math.max(0, at - 1));
318
- };
319
- const moveSlide = (dir) => {
320
- const j = at + dir;
321
- if (j < 0 || j >= slides.length) return;
322
- const next = [...slides];
323
- [next[at], next[j]] = [next[j], next[at]];
324
- setSlides(next);
325
- setIndex(j);
326
- };
327
- const reorder = (from, to) => {
328
- if (from === to) return;
329
- const next = [...slides];
330
- const [moved] = next.splice(from, 1);
331
- next.splice(to, 0, moved);
332
- setSlides(next);
333
- setIndex(to);
334
- };
335
- const addElement = (el) => update({ elements: [...resolveElements(slide), { ...el, id: newElementId() }] });
336
- const addTextEl = () => addElement({ type: "text", x: 12, y: 16, w: 45, h: 16, text: "Text", fontSize: 24 });
337
- const addImageEl = (file) => {
338
- if (!file) return;
339
- const reader = new FileReader();
340
- reader.onload = () => addElement({ type: "image", x: 22, y: 22, w: 40, h: 34, src: String(reader.result) });
341
- reader.readAsDataURL(file);
342
- };
343
- const addShapeEl = (shape) => {
344
- const s = SHAPES.find((x) => x.id === shape);
345
- if (s) addElement({ type: "shape", shape, x: 20, y: 20, w: s.box.w, h: s.box.h, fill: "#5b5bd6" });
346
- };
347
- const applyLayout = (key) => {
348
- const l = LAYOUTS[key];
349
- if (l) update({ elements: l.els.map((e) => ({ ...e, id: newElementId() })) });
350
- };
351
- const setSlideBgImage = (file) => {
352
- if (!file) return;
353
- const reader = new FileReader();
354
- reader.onload = () => update({ background: `#000 url("${String(reader.result)}") center/cover no-repeat` });
355
- reader.readAsDataURL(file);
356
- };
357
- return /* @__PURE__ */ jsxs("div", { className: "cv-deck", children: [
358
- /* @__PURE__ */ jsxs("aside", { className: "cv-deck__rail cv-chrome", children: [
359
- slides.map((s, i) => /* @__PURE__ */ jsx(
360
- "div",
361
- {
362
- className: `cv-deck__thumb-wrap ${i === at ? "is-active" : ""} ${dragIndex === i ? "is-dragging" : ""}`,
363
- draggable: true,
364
- onDragStart: () => setDragIndex(i),
365
- onDragOver: (e) => e.preventDefault(),
366
- onDrop: () => {
367
- if (dragIndex !== null) reorder(dragIndex, i);
368
- setDragIndex(null);
369
- },
370
- onDragEnd: () => setDragIndex(null),
371
- children: /* @__PURE__ */ jsxs("button", { className: "cv-deck__thumb", onClick: () => setIndex(i), children: [
372
- /* @__PURE__ */ jsx("span", { className: "cv-deck__thumb-n", children: i + 1 }),
373
- /* @__PURE__ */ jsx("div", { className: "cv-deck__thumb-slide", style: s.background ? { background: s.background } : void 0, children: /* @__PURE__ */ jsx("div", { style: { position: "absolute", inset: `${s.padding ?? 0}%` }, children: resolveElements(s).map(
374
- (el) => el.type === "text" ? /* @__PURE__ */ jsx(
375
- "span",
376
- {
377
- style: { position: "absolute", left: `${el.x}%`, top: `${el.y}%`, width: `${el.w}%`, fontSize: (el.fontSize ?? 24) * 0.12, fontWeight: el.bold ? 700 : 400, color: el.color ?? s.textColor, overflow: "hidden", whiteSpace: "pre-wrap" },
378
- children: el.text
379
- },
380
- el.id
381
- ) : el.type === "shape" ? /* @__PURE__ */ jsx("div", { style: { position: "absolute", left: `${el.x}%`, top: `${el.y}%`, width: `${el.w}%`, height: `${el.h}%`, color: s.textColor, ...shapeStyle(el) } }, el.id) : /* @__PURE__ */ jsx("img", { src: el.src, alt: "", style: { position: "absolute", left: `${el.x}%`, top: `${el.y}%`, width: `${el.w}%`, height: `${el.h}%`, objectFit: "contain" } }, el.id)
382
- ) }) })
383
- ] })
384
- },
385
- i
386
- )),
387
- /* @__PURE__ */ jsx("button", { className: "cv-deck__addslide", onClick: addSlide, children: "+ Add slide" })
388
- ] }),
389
- /* @__PURE__ */ jsxs("div", { className: "cv-deck__main", children: [
390
- /* @__PURE__ */ jsxs("div", { className: "cv-deck__toolbar cv-chrome", children: [
391
- /* @__PURE__ */ jsx("button", { onClick: addTextEl, title: "Add text box", children: "+ Text" }),
392
- /* @__PURE__ */ jsx("button", { onClick: () => imgRef.current?.click(), title: "Add image", children: "+ Image" }),
393
- /* @__PURE__ */ jsx("input", { ref: imgRef, type: "file", accept: "image/*", hidden: true, onChange: (e) => addImageEl(e.target.files?.[0]) }),
394
- /* @__PURE__ */ jsx("input", { ref: bgRef, type: "file", accept: "image/*", hidden: true, onChange: (e) => setSlideBgImage(e.target.files?.[0]) }),
395
- /* @__PURE__ */ jsxs(
396
- "select",
397
- {
398
- className: "cv-deck__theme",
399
- value: "",
400
- title: "Add a shape",
401
- onChange: (e) => {
402
- if (e.target.value) addShapeEl(e.target.value);
403
- e.currentTarget.value = "";
404
- },
405
- children: [
406
- /* @__PURE__ */ jsx("option", { value: "", children: "+ Shape" }),
407
- SHAPES.map((s) => /* @__PURE__ */ jsx("option", { value: s.id, children: s.label }, s.id))
408
- ]
409
- }
410
- ),
411
- /* @__PURE__ */ jsxs(
412
- "select",
413
- {
414
- className: "cv-deck__theme",
415
- value: "",
416
- title: "Apply a layout",
417
- onChange: (e) => {
418
- if (e.target.value) applyLayout(e.target.value);
419
- e.currentTarget.value = "";
420
- },
421
- children: [
422
- /* @__PURE__ */ jsx("option", { value: "", children: "Layout\u2026" }),
423
- Object.entries(LAYOUTS).map(([k, v]) => /* @__PURE__ */ jsx("option", { value: k, children: v.label }, k))
424
- ]
425
- }
426
- ),
427
- /* @__PURE__ */ jsxs(
428
- "select",
429
- {
430
- className: "cv-deck__theme",
431
- value: "",
432
- title: "Theme",
433
- onChange: (e) => {
434
- const t = THEMES.find((x) => x.id === e.target.value);
435
- if (t) update({ background: t.bg, textColor: t.text });
436
- e.currentTarget.value = "";
437
- },
438
- children: [
439
- /* @__PURE__ */ jsx("option", { value: "", children: "Theme\u2026" }),
440
- THEMES.map((t) => /* @__PURE__ */ jsx("option", { value: t.id, children: t.label }, t.id))
441
- ]
442
- }
443
- ),
444
- /* @__PURE__ */ jsx("label", { className: "cv-deck__bg", title: "Background color", children: /* @__PURE__ */ jsx("input", { type: "color", value: /^#/.test(slide.background ?? "") ? slide.background : "#ffffff", onChange: (e) => update({ background: e.target.value }) }) }),
445
- /* @__PURE__ */ jsx("button", { onClick: () => bgRef.current?.click(), title: "Background image", children: "\u{1F5BC} BG" }),
446
- /* @__PURE__ */ jsxs("label", { className: "cv-deck__pad", title: "Content padding (% of slide)", children: [
447
- "Pad",
448
- /* @__PURE__ */ jsx("input", { type: "number", min: 0, max: 20, value: slide.padding ?? 0, onChange: (e) => update({ padding: Number(e.target.value) || void 0 }) })
449
- ] }),
450
- /* @__PURE__ */ jsx("span", { className: "cv-deck__spacer" }),
451
- /* @__PURE__ */ jsx("button", { className: "cv-deck__present", onClick: () => setPresenting(true), title: "Present (full screen)", children: "\u25B6 Present" }),
452
- /* @__PURE__ */ jsx("button", { onClick: () => moveSlide(-1), title: "Move up", disabled: at === 0, children: "\u25B2" }),
453
- /* @__PURE__ */ jsx("button", { onClick: () => moveSlide(1), title: "Move down", disabled: at === slides.length - 1, children: "\u25BC" }),
454
- /* @__PURE__ */ jsx("button", { onClick: duplicateSlide, title: "Duplicate slide", children: "\u29C9" }),
455
- /* @__PURE__ */ jsx("button", { onClick: deleteSlide, title: "Delete slide", disabled: slides.length === 1, children: "\u{1F5D1}" })
456
- ] }),
457
- /* @__PURE__ */ jsx("div", { className: "cv-slide cv-slide--blank", style: slideStyle, children: /* @__PURE__ */ jsx(FreeSlide, { elements: resolveElements(slide), onChange: (elements) => update({ elements }), padding: slide.padding }) }),
458
- /* @__PURE__ */ jsxs("div", { className: "cv-deck__nav cv-chrome", children: [
459
- /* @__PURE__ */ jsx("button", { disabled: at === 0, onClick: () => setIndex(at - 1), "aria-label": "Previous slide", children: "\u2039" }),
460
- /* @__PURE__ */ jsxs("span", { children: [
461
- at + 1,
462
- " / ",
463
- slides.length
464
- ] }),
465
- /* @__PURE__ */ jsx("button", { disabled: at === slides.length - 1, onClick: () => setIndex(at + 1), "aria-label": "Next slide", children: "\u203A" })
466
- ] }),
467
- /* @__PURE__ */ jsx(
468
- "textarea",
469
- {
470
- className: "cv-deck__notes cv-chrome",
471
- value: slide.notes ?? "",
472
- placeholder: "Speaker notes\u2026",
473
- onChange: (e) => update({ notes: e.target.value })
474
- }
475
- )
476
- ] }),
477
- presenting && /* @__PURE__ */ jsxs("div", { className: "cv-present", onClick: () => setIndex(Math.min(at + 1, slides.length - 1)), children: [
478
- /* @__PURE__ */ jsx("div", { className: "cv-present__slide cv-present__fade cv-slide cv-slide--blank", style: slideStyle, children: /* @__PURE__ */ jsx("div", { className: "cv-free", style: slide.padding ? { inset: `${slide.padding}%` } : void 0, children: resolveElements(slide).map(
479
- (el) => el.type === "text" ? /* @__PURE__ */ jsx("div", { className: "cv-free__el", style: { left: `${el.x}%`, top: `${el.y}%`, width: `${el.w}%`, height: `${el.h}%` }, children: /* @__PURE__ */ jsx("div", { className: "cv-free__text", style: { fontSize: el.fontSize ?? 24, fontWeight: el.bold ? 700 : 400, color: el.color, textAlign: el.align ?? "left", whiteSpace: "pre-wrap" }, children: el.text }) }, el.id) : el.type === "shape" ? /* @__PURE__ */ jsx("div", { className: "cv-free__el", style: { left: `${el.x}%`, top: `${el.y}%`, width: `${el.w}%`, height: `${el.h}%`, color: slide.textColor }, children: /* @__PURE__ */ jsx("div", { style: shapeStyle(el) }) }, el.id) : /* @__PURE__ */ jsx("div", { className: "cv-free__el", style: { left: `${el.x}%`, top: `${el.y}%`, width: `${el.w}%`, height: `${el.h}%` }, children: /* @__PURE__ */ jsx("img", { className: "cv-free__img", src: el.src, alt: "" }) }, el.id)
480
- ) }) }, at),
481
- slide.notes ? /* @__PURE__ */ jsx("div", { className: "cv-present__notes", onClick: (e) => e.stopPropagation(), children: slide.notes }) : null,
482
- /* @__PURE__ */ jsxs("div", { className: "cv-present__hint", children: [
483
- at + 1,
484
- " / ",
485
- slides.length,
486
- " \xB7 \u2190 \u2192 to navigate \xB7 Esc to exit"
487
- ] })
488
- ] })
489
- ] });
490
- }
491
-
492
- export { SlidesRenderer };