UNPKG

spectacle

Version:
1,575 lines (1,537 loc) 148 kB
"use strict"; var __create = Object.create; var __defProp = Object.defineProperty; var __defProps = Object.defineProperties; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropDescs = Object.getOwnPropertyDescriptors; var __getOwnPropNames = Object.getOwnPropertyNames; var __getOwnPropSymbols = Object.getOwnPropertySymbols; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __propIsEnum = Object.prototype.propertyIsEnumerable; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __spreadValues = (a, b) => { for (var prop in b || (b = {})) if (__hasOwnProp.call(b, prop)) __defNormalProp(a, prop, b[prop]); if (__getOwnPropSymbols) for (var prop of __getOwnPropSymbols(b)) { if (__propIsEnum.call(b, prop)) __defNormalProp(a, prop, b[prop]); } return a; }; var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b)); var __objRest = (source, exclude) => { var target = {}; for (var prop in source) if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0) target[prop] = source[prop]; if (source != null && __getOwnPropSymbols) for (var prop of __getOwnPropSymbols(source)) { if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop)) target[prop] = source[prop]; } return target; }; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/index.ts var src_exports = {}; __export(src_exports, { AnimatedProgress: () => animated_progress_default, Appear: () => Appear, Box: () => Box, CodePane: () => code_pane_default, CodeSpan: () => CodeSpan, CommandBar: () => command_bar_default, Deck: () => deck_default, DeckContext: () => DeckContext, DefaultTemplate: () => DefaultTemplate, FitText: () => FitText, FlexBox: () => FlexBox, FullScreen: () => fullscreen_default, FullSizeImage: () => FullSizeImage, Grid: () => Grid, Heading: () => Heading, Image: () => Image, Link: () => Link, ListItem: () => ListItem, Markdown: () => Markdown, MarkdownPreHelper: () => MarkdownPreHelper, MarkdownSlide: () => MarkdownSlide, MarkdownSlideSet: () => MarkdownSlideSet, Notes: () => notes_default, OrderedList: () => OrderedList, Progress: () => progress_default, Quote: () => Quote, Slide: () => slide_default, SlideContext: () => SlideContext, SlideLayout: () => slide_layout_default, SpectacleLogo: () => SpectacleLogo, Stepper: () => Stepper, Table: () => Table, TableBody: () => TableBody, TableCell: () => TableCell, TableHeader: () => TableHeader, TableRow: () => TableRow, Text: () => Text, UnorderedList: () => UnorderedList, codePaneThemes: () => codePaneThemes, defaultTheme: () => default_theme_default, defaultTransition: () => defaultTransition, fadeTransition: () => fadeTransition, indentNormalizer: () => indent_normalizer_default, isolateNotes: () => isolateNotes, mdxComponentMap: () => mdx_component_mapper_default, removeNotes: () => removeNotes, slideTransition: () => slideTransition, useMousetrap: () => useMousetrap, useSteps: () => useSteps }); module.exports = __toCommonJS(src_exports); // src/components/deck/index.tsx var import_react17 = require("react"); // src/components/deck/default-deck.tsx var import_react9 = require("react"); // src/components/deck/deck.tsx var import_react7 = require("react"); var import_styled_components2 = __toESM(require("styled-components")); // src/hooks/use-slides.tsx var import_react = require("react"); var import_jsx_runtime = require("react/jsx-runtime"); var PLACEHOLDER_CLASS_NAME = "spectacle-v7-slide"; function useCollectSlides() { const [initialized, setInitialized] = (0, import_react.useState)(false); const [slideContainer, setSlideContainer] = (0, import_react.useState)(); const [slideIds, setSlideIds] = (0, import_react.useState)([]); const [slideIdsOfSlidesWithTemplates, setSlideIdsOfSlidesWithTemplates] = (0, import_react.useState)(/* @__PURE__ */ new Set()); (0, import_react.useEffect)(() => { if (!slideContainer) return; const slides = slideContainer.getElementsByClassName( PLACEHOLDER_CLASS_NAME ); const nextSlideIds = []; const nextSlideIdsOfSlidesWithTemplates = /* @__PURE__ */ new Set(); for (const placeholderNode of slides) { const { slideId, slideHasTemplate } = placeholderNode.dataset; if (slideId !== void 0) { nextSlideIds.push(slideId); if (slideHasTemplate === "true") { nextSlideIdsOfSlidesWithTemplates.add(slideId); } } } setSlideIds(nextSlideIds); setSlideIdsOfSlidesWithTemplates(nextSlideIdsOfSlidesWithTemplates); setInitialized(true); }, [slideContainer]); return [ setSlideContainer, slideIds, slideIdsOfSlidesWithTemplates, initialized ]; } function useSlide(doesSlideHaveTemplate, userProvidedId) { const id = (0, import_react.useId)(); const [slideId] = (0, import_react.useState)(userProvidedId || id); return { slideId, placeholder: /* @__PURE__ */ (0, import_jsx_runtime.jsx)( "div", { className: PLACEHOLDER_CLASS_NAME, "data-slide-id": slideId, "data-slide-has-template": doesSlideHaveTemplate } ) }; } // src/hooks/use-aspect-ratio-fitting.ts var import_react2 = require("react"); var import_use_resize_observer = __toESM(require("use-resize-observer")); function useAspectRatioFitting({ targetWidth = 1366, targetHeight = 768 }) { const containerRef = (0, import_react2.useRef)(null); const [scaleFactor, setScaleFactor] = (0, import_react2.useState)(1); const [transformOrigin, setTransformOrigin] = (0, import_react2.useState)({ x: 0, y: 0 }); const recalculate = (0, import_react2.useCallback)( ({ width, height }) => { const containerWidth = Number(width) || 0.01; const containerHeight = Number(height) || 0.01; const containerRatio = containerWidth / containerHeight; const targetRatio = targetWidth / targetHeight; const useVertical = containerRatio > targetRatio; const scaleFactor2 = useVertical ? containerHeight / targetHeight : containerWidth / targetWidth; const scaledWidth = targetWidth * scaleFactor2; const scaledHeight = targetHeight * scaleFactor2; let x0 = 0; if (useVertical) { x0 = 0.5 * (containerWidth - scaledWidth); x0 /= 1 - scaleFactor2; } let y0 = 0; if (!useVertical) { y0 = 0.5 * (containerHeight - scaledHeight); y0 /= 1 - scaleFactor2; } setScaleFactor(scaleFactor2); setTransformOrigin({ x: x0, y: y0 }); }, [targetWidth, targetHeight] ); (0, import_react2.useEffect)(() => { if (!containerRef || !containerRef.current) return; const rects = containerRef.current.getClientRects(); recalculate(rects[0]); }, [targetWidth, targetHeight, recalculate]); (0, import_use_resize_observer.default)({ ref: containerRef, onResize: recalculate }); const styles = { position: "relative", width: targetWidth, height: targetHeight, scaleFactor, transform: `scale(${scaleFactor})`, transformOrigin: `${transformOrigin.x}px ${transformOrigin.y}px` }; return [containerRef, styles]; } // src/hooks/use-deck-state.ts var import_react3 = require("react"); var import_merge_anything = require("merge-anything"); // src/utils/clamp.ts function toFiniteNumber(value) { if (!value || isNaN(value)) { return 0; } else if (value === Infinity || value === -Infinity) { const sign = value < 0 ? -1 : 1; return sign * Number.MAX_SAFE_INTEGER; } return value; } function clamp(number, lower, upper) { if (isNaN(number)) { return NaN; } let finiteNumber = toFiniteNumber(number); if (finiteNumber === finiteNumber) { if (upper !== void 0) { finiteNumber = finiteNumber <= upper ? finiteNumber : upper; } if (lower !== void 0) { finiteNumber = finiteNumber >= lower ? finiteNumber : lower; } } return finiteNumber; } // src/hooks/use-deck-state.ts var GOTO_FINAL_STEP = null; var initialDeckState = { initialized: false, navigationDirection: 0, pendingView: { slideIndex: 0, stepIndex: 0 }, activeView: { slideIndex: 0, stepIndex: 0 } }; function deckReducer(state, { type, payload = {} }) { var _a; switch (type) { case "INITIALIZE_TO": return { navigationDirection: 0, activeView: (0, import_merge_anything.merge)(state.activeView, payload), pendingView: (0, import_merge_anything.merge)(state.pendingView, payload), initialized: true }; case "SKIP_TO": const navigationDirection = (() => { if ("slideIndex" in payload && payload.slideIndex) { return clamp(payload.slideIndex - state.activeView.slideIndex, -1, 1); } return null; })(); return __spreadProps(__spreadValues({}, state), { navigationDirection: navigationDirection || state.navigationDirection, pendingView: (0, import_merge_anything.merge)(state.pendingView, payload) }); case "STEP_FORWARD": return __spreadProps(__spreadValues({}, state), { navigationDirection: 1, pendingView: (0, import_merge_anything.merge)(state.pendingView, { stepIndex: state.pendingView.stepIndex + 1 }) }); case "STEP_BACKWARD": return __spreadProps(__spreadValues({}, state), { navigationDirection: -1, pendingView: (0, import_merge_anything.merge)(state.pendingView, { stepIndex: state.pendingView.stepIndex - 1 }) }); case "ADVANCE_SLIDE": return __spreadProps(__spreadValues({}, state), { navigationDirection: 1, pendingView: (0, import_merge_anything.merge)(state.pendingView, { stepIndex: 0, slideIndex: state.pendingView.slideIndex + 1 }) }); case "REGRESS_SLIDE": return __spreadProps(__spreadValues({}, state), { navigationDirection: -1, pendingView: (0, import_merge_anything.merge)(state.pendingView, { stepIndex: (_a = payload == null ? void 0 : payload.stepIndex) != null ? _a : GOTO_FINAL_STEP, slideIndex: state.pendingView.slideIndex - 1 }) }); case "COMMIT_TRANSITION": const pendingView = (0, import_merge_anything.merge)(state.pendingView, payload); return __spreadProps(__spreadValues({}, state), { pendingView, activeView: (0, import_merge_anything.merge)(state.activeView, pendingView) }); case "CANCEL_TRANSITION": return __spreadProps(__spreadValues({}, state), { pendingView: (0, import_merge_anything.merge)(state.pendingView, state.activeView) }); default: return state; } } function useDeckState(userProvidedInitialState) { const [ { initialized, navigationDirection, pendingView, activeView }, dispatch ] = (0, import_react3.useReducer)(deckReducer, { initialized: initialDeckState.initialized, navigationDirection: initialDeckState.navigationDirection, pendingView: __spreadValues(__spreadValues({}, initialDeckState.pendingView), userProvidedInitialState), activeView: __spreadValues(__spreadValues({}, initialDeckState.activeView), userProvidedInitialState) }); const actions = (0, import_react3.useMemo)( () => ({ initializeTo: (payload) => dispatch({ type: "INITIALIZE_TO", payload }), skipTo: (payload) => dispatch({ type: "SKIP_TO", payload }), stepForward: () => dispatch({ type: "STEP_FORWARD" }), stepBackward: () => dispatch({ type: "STEP_BACKWARD" }), advanceSlide: () => dispatch({ type: "ADVANCE_SLIDE" }), regressSlide: (payload) => dispatch({ type: "REGRESS_SLIDE", payload }), commitTransition: (payload) => dispatch({ type: "COMMIT_TRANSITION", payload }), cancelTransition: () => dispatch({ type: "CANCEL_TRANSITION" }) }), [dispatch] ); return __spreadValues({ initialized, navigationDirection, pendingView, activeView }, actions); } // src/hooks/use-mousetrap.ts var import_react4 = require("react"); var import_mousetrap = __toESM(require("mousetrap")); function useMousetrap(keybinds, deps) { (0, import_react4.useEffect)(() => { for (const combo in keybinds) { const callback = keybinds[combo]; if (typeof callback !== "function") { throw new TypeError( `Expected type 'function' in useMousetrap for combo '${combo}', but got ${typeof callback}` ); } import_mousetrap.default.bind(combo, callback); } return () => { for (const combo in keybinds) { import_mousetrap.default.unbind(combo); } }; }, [keybinds, ...deps]); } // src/hooks/use-location-sync.ts var import_react5 = require("react"); var import_history = require("history"); var import_query_string = __toESM(require("query-string")); var import_react_fast_compare = __toESM(require("react-fast-compare")); var import_merge_anything2 = require("merge-anything"); function defaultMergeLocation(object, ...sources) { return (0, import_merge_anything2.mergeAndCompare)( (left, right, key) => { switch (key) { case "search": if (!left) return right; return "?" + import_query_string.default.stringify(__spreadValues(__spreadValues({}, import_query_string.default.parse(left)), import_query_string.default.parse(right))); default: return (0, import_merge_anything2.merge)(left, right); } }, object, ...sources ); } function useLocationSync({ setState, mapStateToLocation: mapStateToLocation2, mapLocationToState: mapLocationToState2, disableInteractivity = false, mergeLocation = defaultMergeLocation, historyFactory = import_history.createBrowserHistory }) { const [history] = (0, import_react5.useState)(() => { return typeof document !== "undefined" ? historyFactory() : null; }); const [initialized, setInitialized] = (0, import_react5.useState)(false); (0, import_react5.useEffect)(() => { if (!initialized && disableInteractivity) return; return history == null ? void 0 : history.listen(({ location }) => { setState(mapLocationToState2(location)); }); }, [ disableInteractivity, initialized, history, setState, mapLocationToState2 ]); const syncLocation = (0, import_react5.useCallback)( (defaultState) => { if (disableInteractivity || !history) { return defaultState; } const { location } = history; const initialState = (0, import_merge_anything2.merge)( defaultState, mapLocationToState2(location) ); const nextLocation = mergeLocation( {}, location, mapStateToLocation2(initialState) ); history.replace(nextLocation); setInitialized(true); return initialState; }, [ history, mapLocationToState2, mapStateToLocation2, disableInteractivity, mergeLocation ] ); const setLocation = (0, import_react5.useCallback)( (state) => { if (!initialized || !history) return; const { location } = history; const nextLocation = mergeLocation( {}, location, mapStateToLocation2(state) ); if (!(0, import_react_fast_compare.default)(location, nextLocation)) { history.push(nextLocation); } }, [history, initialized, mergeLocation, mapStateToLocation2] ); return [syncLocation, setLocation]; } // src/theme/default-theme.ts var defaultTheme = { size: { width: 1366, height: 768, maxCodePaneHeight: 200 }, colors: { primary: "#ebe5da", secondary: "#fc6986", tertiary: "#1e2852", quaternary: "#ffc951", quinary: "#8bddfd" }, fonts: { header: '"Helvetica Neue", Helvetica, Arial, sans-serif', text: '"Helvetica Neue", Helvetica, Arial, sans-serif', monospace: '"Consolas", "Menlo", monospace' }, fontSizes: { h1: "72px", h2: "64px", h3: "56px", text: "44px", monospace: "20px" }, space: [16, 24, 32] }; var default_theme_default = defaultTheme; // src/theme/print-theme.ts var printTheme = { colors: { primary: "#777", secondary: "#000", tertiary: "#fff", quaternary: "#000000", quinary: "#000000" } }; var print_theme_default = printTheme; // src/theme/index.ts var mergeKeys = (base, override) => Object.keys(override || {}).reduce( (merged, key) => { merged[key] = __spreadValues(__spreadValues({}, merged[key]), override[key]); return merged; }, __spreadValues({}, base) ); function mergeTheme({ theme, printMode }) { const merged = mergeKeys(default_theme_default, theme); return printMode ? mergeKeys(merged, print_theme_default) : merged; } // src/location-map-fns/query-string.ts var query_string_exports = {}; __export(query_string_exports, { mapLocationToState: () => mapLocationToState, mapStateToLocation: () => mapStateToLocation }); var import_query_string2 = require("query-string"); function mapLocationToState(location) { const { search: queryString } = location; const { slideIndex: rawSlideIndex, stepIndex: rawStepIndex } = (0, import_query_string2.parse)(queryString); const nextState = {}; if (rawSlideIndex === void 0) { return nextState; } nextState.slideIndex = Number(rawSlideIndex); if (isNaN(nextState.slideIndex)) { throw new Error( `Invalid slide index in URL query string: '${queryString}'` ); } if (rawStepIndex === "final") { nextState.stepIndex = GOTO_FINAL_STEP; } else if (rawStepIndex !== void 0) { nextState.stepIndex = Number(rawStepIndex); if (isNaN(nextState.stepIndex)) { throw new Error( `Invalid step index in URL query string: '${queryString}'` ); } } return nextState; } function mapStateToLocation(state) { const { slideIndex, stepIndex } = state; const query = {}; if (typeof slideIndex !== "number") { return query; } query.slideIndex = String(slideIndex); if (typeof stepIndex === "number") { query.stepIndex = String(stepIndex); } else if (stepIndex === GOTO_FINAL_STEP) { query.stepIndex = "final"; } return { search: "?" + (0, import_query_string2.stringify)(query) }; } // src/components/deck/deck-styles.ts function overviewFrameStyle({ overviewScale, nativeSlideWidth, nativeSlideHeight }) { return { margin: "1rem", width: `${overviewScale * nativeSlideWidth}px`, height: `${overviewScale / (nativeSlideWidth / nativeSlideHeight) * nativeSlideWidth}px`, display: "block", transform: "none", position: "relative" }; } function overviewWrapperStyle({ overviewScale }) { return { width: `${100 / overviewScale}%`, height: `${100 / overviewScale}%`, transform: `scale(${overviewScale})`, transformOrigin: "0px 0px", position: "absolute" }; } function printFrameStyle({ nativeSlideWidth, nativeSlideHeight, printScale }) { return { margin: "0", width: `${printScale * nativeSlideWidth}px`, height: `${printScale / (nativeSlideWidth / nativeSlideHeight) * nativeSlideWidth}px`, display: "block", transform: "none", position: "relative", breakAfter: "page" }; } function printWrapperStyle({ printScale }) { return { width: `${100 / printScale}%`, height: `${100 / printScale}%`, transform: `scale(${printScale})`, transformOrigin: "0px 0px", position: "absolute" }; } // src/utils/use-auto-play.ts var import_react6 = require("react"); var useAutoPlay = ({ enabled = false, loop = false, stepForward, interval = 1e3 }) => { const stepFn = (0, import_react6.useRef)(stepForward); stepFn.current = stepForward; (0, import_react6.useEffect)(() => { if (enabled) { const id = setInterval(() => { stepFn.current(); }, interval); return () => clearInterval(id); } }, [enabled, interval, loop]); }; // src/components/transitions/index.ts var STAGE_RIGHT = "translateX(-100%)"; var CENTER_STAGE = "translateX(0%)"; var STAGE_LEFT = "translateX(100%)"; var fadeTransition = { from: { opacity: 0 }, enter: { opacity: 1 }, leave: { opacity: 0 } }; var slideTransition = { from: { transform: STAGE_LEFT }, enter: { transform: CENTER_STAGE }, leave: { transform: STAGE_RIGHT } }; var defaultTransition = slideTransition; // src/components/template-wrapper.tsx var import_styled_components = __toESM(require("styled-components")); var TemplateWrapper = import_styled_components.default.div` position: absolute; top: 0; left: 0; right: 0; bottom: 0; pointer-events: none; `; var template_wrapper_default = TemplateWrapper; // src/components/deck/deck.tsx var import_kbar = require("kbar"); // src/utils/constants.ts var SYSTEM_FONT = '-apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Helvetica Neue", Helvetica, sans-serif'; var KEYBOARD_SHORTCUTS = { DEFAULT_MODE: "mod+shift+d", PRESENTER_MODE: "mod+shift+p", OVERVIEW_MODE: "mod+shift+o", PRINT_MODE: "mod+shift+r", EXPORT_MODE: "mod+shift+e", TAB_FORWARD_OVERVIEW_MODE: "tab", TAB_BACKWARD_OVERVIEW_MODE: "shift+tab", SELECT_SLIDE_OVERVIEW_MODE: "enter", NEXT_SLIDE: "right", PREVIOUS_SLIDE: "left" }; var KEYBOARD_SHORTCUTS_IDS = { DEFAULT_MODE: "DEFAULT_MODE", PRESENTER_MODE: "PRESENTER_MODE", OVERVIEW_MODE: "OVERVIEW_MODE", PRINT_MODE: "PRINT_MODE", EXPORT_MODE: "EXPORT_MODE", TAB_FORWARD_OVERVIEW_MODE: "TAB_FORWARD_OVERVIEW_MODE", TAB_BACKWARD_OVERVIEW_MODE: "TAB_BACKWARD_OVERVIEW_MODE", SELECT_SLIDE_OVERVIEW_MODE: "SELECT_SLIDE_OVERVIEW_MODE", NEXT_SLIDE: "NEXT_SLIDE", PREVIOUS_SLIDE: "PREVIOUS_SLIDE" }; var SPECTACLE_MODES = { DEFAULT_MODE: "DEFAULT_MODE", PRESENTER_MODE: "PRESENTER_MODE", OVERVIEW_MODE: "OVERVIEW_MODE", PRINT_MODE: "PRINT_MODE", EXPORT_MODE: "EXPORT_MODE" }; // src/components/deck/deck.tsx var import_jsx_runtime2 = require("react/jsx-runtime"); var DeckContext = (0, import_react7.createContext)(null); DeckContext.displayName = "DeckContext"; var noop = () => { }; var DEFAULT_PRINT_SCALE = 1; var DEFAULT_OVERVIEW_SCALE = 0.25; var Portal = import_styled_components2.default.div( ({ fitAspectRatioStyle, overviewMode, printMode }) => [ !printMode && { overflow: "hidden" }, !printMode && fitAspectRatioStyle, overviewMode && { display: "flex", flexWrap: "wrap", justifyContent: "flex-start", alignItems: "flex-start", alignContent: "flex-start", transform: "scale(1)", overflowY: "scroll", width: "100%", height: "100%" }, printMode && { display: "block" } ] ); var DeckInternal = (0, import_react7.forwardRef)( (_a, ref) => { var _b = _a, { id: userProvidedId, className = "", backdropStyle: userProvidedBackdropStyle, overviewMode = false, printMode = false, exportMode = false, overviewScale = DEFAULT_OVERVIEW_SCALE, printScale = DEFAULT_PRINT_SCALE, template, theme: _c = {} } = _b, _d = _c, { Backdrop: UserProvidedBackdropComponent, backdropStyle: themeProvidedBackdropStyle = { position: "fixed", top: 0, left: 0, width: "100vw", height: "100vh" }, suppressBackdropFallback: themeSuppressBackdropFallback } = _d, restTheme = __objRest(_d, [ "Backdrop", "backdropStyle", "suppressBackdropFallback" ]), { onSlideClick = noop, onMobileSlide = noop, disableInteractivity = false, notePortalNode, useAnimations = true, children, onActiveStateChange: onActiveStateChangeExternal = noop, initialState: initialDeckState2 = { slideIndex: 0, stepIndex: 0 }, suppressBackdropFallback = false, autoPlay = false, autoPlayLoop = false, autoPlayInterval = 1e3, transition = defaultTransition, backgroundImage } = _b; const id = (0, import_react7.useId)(); const [deckId] = (0, import_react7.useState)(userProvidedId || id); const { width: nativeSlideWidth = default_theme_default.size.width, height: nativeSlideHeight = default_theme_default.size.height } = restTheme.size || {}; const { initialized, pendingView, activeView, navigationDirection, initializeTo, skipTo, stepForward, stepBackward, advanceSlide, regressSlide, commitTransition, cancelTransition } = useDeckState(initialDeckState2); const [ setPlaceholderContainer, slideIds, slideIdsWithTemplates, slideIdsInitialized ] = useCollectSlides(); (0, import_react7.useImperativeHandle)( ref, () => ({ initialized, activeView, initializeTo, skipTo, stepForward, stepBackward, advanceSlide, regressSlide, numberOfSlides: slideIds.length }), [ initialized, activeView, initializeTo, skipTo, stepForward, stepBackward, advanceSlide, regressSlide, slideIds ] ); (0, import_kbar.useRegisterActions)( !disableInteractivity ? [ { id: KEYBOARD_SHORTCUTS_IDS.NEXT_SLIDE, name: "Next Slide", keywords: "next", perform: () => stepForward(), section: "Slide" }, { id: KEYBOARD_SHORTCUTS_IDS.PREVIOUS_SLIDE, name: "Previous Slide", keywords: "previous", perform: () => stepBackward(), section: "Slide" }, { id: "Restart Presentation", name: "Restart Presentation", keywords: "restart", perform: () => skipTo({ slideIndex: 0, stepIndex: 0 }), section: "Slide" } ] : [] ); useMousetrap( disableInteractivity ? {} : { left: () => stepBackward(), right: () => stepForward() }, [] ); const [syncLocation, onActiveStateChange] = useLocationSync(__spreadValues({ disableInteractivity, setState: skipTo }, query_string_exports)); (0, import_react7.useEffect)(() => { if (!initialized) return; onActiveStateChange(activeView); onActiveStateChangeExternal(activeView); }, [ initialized, activeView, onActiveStateChange, onActiveStateChangeExternal ]); (0, import_react7.useEffect)(() => { const initialView = syncLocation({ slideIndex: 0, stepIndex: 0 }); initializeTo(initialView); }, [initializeTo, syncLocation]); useAutoPlay({ enabled: autoPlay, loop: autoPlayLoop, interval: autoPlayInterval, stepForward }); const handleSlideClick = (0, import_react7.useCallback)( (e, slideId) => { const slideIndex = slideIds.indexOf(slideId); onSlideClick(e, slideIndex); }, [onSlideClick, slideIds] ); const activeSlideId = slideIds[activeView.slideIndex]; const pendingSlideId = slideIds[pendingView.slideIndex]; const fullyInitialized = initialized && slideIdsInitialized; const [slidePortalNode, setSlidePortalNode] = (0, import_react7.useState)(); const [backdropRef, fitAspectRatioStyle] = useAspectRatioFitting({ targetWidth: nativeSlideWidth, targetHeight: nativeSlideHeight }); const frameStyle = (0, import_react7.useMemo)(() => { const options = { printScale, overviewScale, nativeSlideWidth, nativeSlideHeight }; if (overviewMode) { return overviewFrameStyle(options); } else if (printMode) { return printFrameStyle(options); } return {}; }, [ nativeSlideHeight, nativeSlideWidth, overviewMode, overviewScale, printMode, printScale ]); const wrapperStyle = (0, import_react7.useMemo)(() => { if (overviewMode) { return overviewWrapperStyle({ overviewScale }); } else if (printMode) { return printWrapperStyle({ printScale }); } return {}; }, [overviewMode, overviewScale, printMode, printScale]); const backdropStyle = __spreadValues(__spreadValues({}, themeProvidedBackdropStyle), userProvidedBackdropStyle); const BackdropComponent = UserProvidedBackdropComponent || "div"; if (!backdropStyle["background"] && !backdropStyle["backgroundColor"] && !UserProvidedBackdropComponent && !suppressBackdropFallback && !themeSuppressBackdropFallback) { backdropStyle["backgroundColor"] = "black"; } const doesCurrentSlideHaveItsOwnTemplate = slideIdsWithTemplates.has(activeSlideId); const templateElement = typeof template === "function" ? template({ slideNumber: activeView.slideIndex + 1, numberOfSlides: slideIds.length }) : template; return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( import_styled_components2.ThemeProvider, { theme: mergeTheme({ theme: restTheme, printMode: printMode && !exportMode }), children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( BackdropComponent, { ref: backdropRef, className, style: __spreadProps(__spreadValues({}, backdropStyle), { overflow: "hidden" }), children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)( DeckContext.Provider, { value: { deckId, slideCount: slideIds.length, slideIds, useAnimations, slidePortalNode, onSlideClick: handleSlideClick, onMobileSlide, theme: restTheme, autoPlayLoop, navigationDirection, frameOverrideStyle: frameStyle, wrapperOverrideStyle: wrapperStyle, backdropNode: backdropRef.current, notePortalNode, initialized: fullyInitialized, activeView: __spreadProps(__spreadValues({}, activeView), { slideId: activeSlideId }), pendingView: __spreadProps(__spreadValues({}, pendingView), { slideId: pendingSlideId }), skipTo, stepForward, stepBackward, advanceSlide, regressSlide, commitTransition, cancelTransition, transition, template, backgroundImage, inOverviewMode: overviewMode, inPrintMode: printMode }, children: [ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( Portal, { ref: setSlidePortalNode, overviewMode, printMode, fitAspectRatioStyle, children: !doesCurrentSlideHaveItsOwnTemplate && !overviewMode && !printMode && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( template_wrapper_default, { style: __spreadProps(__spreadValues({}, wrapperStyle), { // Slides are appended to the parent as they are portaled in and end up later in // the source order. Adding zIndex to the template to overlay the sibling slides // once they have been portaled in. zIndex: 1 }), children: templateElement } ) } ), /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { ref: setPlaceholderContainer, style: { display: "none" }, children }) ] } ) } ) } ); } ); DeckInternal.displayName = "Deck"; var Deck = DeckInternal; Deck.displayName = "Deck"; // src/hooks/use-broadcast-channel.ts var import_react8 = require("react"); var import_broadcast_channel = require("broadcast-channel"); var noop2 = () => { }; var safeWindow = {}; if (typeof window !== "undefined") { safeWindow = window; } var BroadcastChannel = safeWindow.BroadcastChannel || import_broadcast_channel.BroadcastChannel; function useBroadcastChannel(channelName, onMessage = noop2, deps = []) { const broadcasterId = (0, import_react8.useId)(); const channel = (0, import_react8.useRef)(); (0, import_react8.useEffect)(() => { channel.current = new BroadcastChannel(channelName); return () => { var _a; (_a = channel.current) == null ? void 0 : _a.close(); }; }, [channelName]); const postMessage = (0, import_react8.useCallback)( (type, payload = {}) => { var _a; const message = { type, payload, meta: { sender: broadcasterId } }; const rawMessage = JSON.stringify(message); (_a = channel.current) == null ? void 0 : _a.postMessage(rawMessage); }, [broadcasterId] ); const userMessageHandlerRef = (0, import_react8.useRef)(onMessage); (0, import_react8.useEffect)(() => { userMessageHandlerRef.current = onMessage; }, [...deps, postMessage]); (0, import_react8.useEffect)(() => { var _a; if (!channel.current) return; const messageHandler = (event) => { const rawMessage = event.data; const message = JSON.parse(rawMessage); userMessageHandlerRef.current(message); }; (_a = channel.current) == null ? void 0 : _a.addEventListener("message", messageHandler); return () => { var _a2; (_a2 = channel.current) == null ? void 0 : _a2.removeEventListener("message", messageHandler); }; }, [postMessage]); return [postMessage, broadcasterId]; } // src/components/deck/default-deck.tsx var import_jsx_runtime3 = require("react/jsx-runtime"); var DefaultDeck = (props) => { const _a = props, { overviewMode = false, printMode = false, exportMode = false, toggleMode, children } = _a, rest = __objRest(_a, [ "overviewMode", "printMode", "exportMode", "toggleMode", "children" ]); const deck = (0, import_react9.useRef)(null); const [postMessage] = useBroadcastChannel( "spectacle_presenter_bus", (message) => { if (message.type !== "SYNC") return; const nextView = message.payload; if (deck.current.initialized) { deck.current.skipTo(nextView); } else { deck.current.initializeTo(nextView); } } ); (0, import_react9.useEffect)(() => { postMessage("SYNC_REQUEST"); }, [postMessage]); useMousetrap( overviewMode ? { [KEYBOARD_SHORTCUTS.TAB_FORWARD_OVERVIEW_MODE]: () => deck.current.advanceSlide(), [KEYBOARD_SHORTCUTS.TAB_BACKWARD_OVERVIEW_MODE]: () => deck.current.regressSlide({ stepIndex: 0 }), [KEYBOARD_SHORTCUTS.SELECT_SLIDE_OVERVIEW_MODE]: () => toggleMode({ newMode: SPECTACLE_MODES.DEFAULT_MODE }) } : {}, [] ); const onSlideClick = (0, import_react9.useCallback)( (e, slideIndex) => { if (overviewMode) { toggleMode({ e, newMode: SPECTACLE_MODES.DEFAULT_MODE, senderSlideIndex: +slideIndex }); } }, [overviewMode, toggleMode] ); const onMobileSlide = (e) => { if (navigator.maxTouchPoints < 1 || !deck.current) return; switch (e.dir) { case "Left": deck.current.stepForward(); break; case "Right": deck.current.regressSlide(); break; } }; return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)( DeckInternal, __spreadProps(__spreadValues({ overviewMode, onSlideClick, onMobileSlide, printMode, exportMode, ref: deck }, rest), { children }) ); }; var default_deck_default = DefaultDeck; // src/components/presenter-mode/index.tsx var import_react13 = require("react"); var import_styled_components7 = __toESM(require("styled-components")); // src/components/presenter-mode/components.tsx var import_styled_components3 = __toESM(require("styled-components")); var PresenterDeckContainer = import_styled_components3.default.div` position: absolute; top: 0; left: 0; right: 0; bottom: 0; display: flex; flex-direction: row; background-color: #181818; overflow: hidden; color: white; `; var NotesColumn = import_styled_components3.default.div` padding: 0; display: flex; flex-direction: column; width: 50%; border-right: 1px solid black; `; var PreviewColumn = import_styled_components3.default.div` background-color: black; display: flex; flex-direction: column; height: 100%; width: 50%; > :first-child { margin-bottom: 0.5em; } `; var SlideContainer = import_styled_components3.default.div` display: flex; flex-direction: column; height: calc(50% - 1em); width: 100%; overflow: hidden; `; var SlideWrapper = import_styled_components3.default.div` flex: 1; width: 100%; position: relative; .spectacle-fullscreen-button { display: none; } ${({ small }) => small && `flex: 0.8;`} `; var SlideCountLabel = import_styled_components3.default.span` background: hsla(0, 0%, 100%, 0.1); border-radius: 4px; font-size: 0.7em; padding: 1px 4px; `; var NotesContainer = import_styled_components3.default.div` border-top: 1px solid black; overflow-y: scroll; flex: 1; ::-webkit-scrollbar { width: 10px; } /* Track */ ::-webkit-scrollbar-track { background-color: #111; } /* Handle */ ::-webkit-scrollbar-thumb { background: #333; border-radius: 10px; } `; var deckBackdropStyles = { currentSlide: { width: "50vw", height: "50vh", left: "50vw", top: "7vh" }, nextSlide: { width: "50vw", height: "33vh", top: "60vh", left: "50vw" } }; // src/components/layout-primitives.ts var import_styled_components4 = __toESM(require("styled-components")); var import_styled_system = require("styled-system"); var containerPrintStyle = ` @media print { height: inherit; } `; var Box = import_styled_components4.default.div( (0, import_styled_system.compose)(import_styled_system.layout, import_styled_system.space, import_styled_system.position, import_styled_system.color, import_styled_system.border), containerPrintStyle ); var FlexBox = import_styled_components4.default.div.attrs((props) => __spreadValues({ alignItems: "center", justifyContent: "center", display: "flex" }, props))( (0, import_styled_system.compose)(import_styled_system.layout, import_styled_system.space, import_styled_system.position, import_styled_system.color, import_styled_system.border, import_styled_system.flexbox), containerPrintStyle ); var Grid = import_styled_components4.default.div.attrs((props) => __spreadValues({ display: "grid" }, props))((0, import_styled_system.compose)(import_styled_system.layout, import_styled_system.grid, import_styled_system.position), containerPrintStyle); // src/components/presenter-mode/timer.tsx var import_react12 = require("react"); // src/components/typography.tsx var import_styled_components5 = __toESM(require("styled-components")); var import_styled_system2 = require("styled-system"); var import_react10 = require("react"); var import_use_resize_observer2 = __toESM(require("use-resize-observer")); var import_jsx_runtime4 = require("react/jsx-runtime"); var decoration = (0, import_styled_system2.system)({ textDecoration: true }); var Text = import_styled_components5.default.div.attrs((props) => __spreadValues({ color: "primary", fontFamily: "text", fontSize: "text", textAlign: "left", padding: 0, margin: 0 }, props))((0, import_styled_system2.compose)(import_styled_system2.color, import_styled_system2.typography, import_styled_system2.space)); var CodeSpan = import_styled_components5.default.code.attrs((props) => __spreadValues({ fontFamily: "monospace", fontSize: "text" }, props))((0, import_styled_system2.compose)(import_styled_system2.color, import_styled_system2.typography, import_styled_system2.space)); var Link = import_styled_components5.default.a.attrs( (props) => __spreadValues({ fontFamily: "text", fontSize: "text", textDecoration: "underline", color: "quaternary" }, props) )( (0, import_styled_system2.compose)(import_styled_system2.color, import_styled_system2.typography, import_styled_system2.space, decoration) ); var Heading = import_styled_components5.default.div.attrs((props) => __spreadValues({ color: "secondary", fontFamily: "header", fontSize: "h1", fontWeight: "bold", textAlign: "center", margin: 1, padding: 0 }, props))((0, import_styled_system2.compose)(import_styled_system2.color, import_styled_system2.typography, import_styled_system2.space)); var Quote = (0, import_styled_components5.default)( Text ).attrs((props) => __spreadValues({ color: "primary", fontFamily: "text", fontSize: "text", textAlign: "left", fontStyle: "italic", padding: "16px 0 16px 8px", margin: 0 }, props))` border-left: 1px solid ${({ theme, borderColor }) => borderColor || theme.colors.secondary}; div { margin: 0; } `; var listStyle = (0, import_styled_system2.system)({ listStyleType: true }); var OrderedList = import_styled_components5.default.ol.attrs( (props) => __spreadValues({ color: "primary", fontFamily: "text", fontSize: "text", textAlign: "left", margin: 0 }, props) )( (0, import_styled_system2.compose)(import_styled_system2.color, import_styled_system2.typography, import_styled_system2.space, listStyle) ); var UnorderedList = import_styled_components5.default.ul.attrs( (props) => __spreadValues({ color: "primary", fontFamily: "text", fontSize: "text", textAlign: "left", margin: 0 }, props) )( (0, import_styled_system2.compose)(import_styled_system2.color, import_styled_system2.typography, import_styled_system2.space, listStyle) ); var ListItem = import_styled_components5.default.li.attrs((props) => __spreadValues({ margin: 0 }, props))((0, import_styled_system2.compose)(import_styled_system2.color, import_styled_system2.typography, import_styled_system2.space)); var FitContainer = import_styled_components5.default.div` width: 100%; display: flex; align-items: center; justify-content: center; `; var ScalableText = (0, import_styled_components5.default)( Text ).attrs((props) => __spreadValues({ textAlign: "center" }, props))` transform-origin: center; transform: scale(${(props) => props.scale || 1}); white-space: nowrap; `; var FitText = (props) => { const containerRef = (0, import_react10.useRef)(null); const textRef = (0, import_react10.useRef)(null); const [scale, setScale] = (0, import_react10.useState)(1); (0, import_use_resize_observer2.default)({ ref: containerRef, onResize: () => { if (!containerRef.current || !textRef.current) return; const containerWidth = containerRef.current.offsetWidth; const textWidth = textRef.current.offsetWidth; if (textWidth === 0) return; const newScale = Math.min(containerWidth / textWidth); setScale(newScale); } }); return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(FitContainer, { ref: containerRef, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(ScalableText, __spreadProps(__spreadValues({}, props), { ref: textRef, scale })) }); }; // src/components/internal-button.ts var import_styled_components6 = __toESM(require("styled-components")); var InternalButton = (0, import_styled_components6.default)("button")` background: #333; border: 1px solid hsla(0, 0%, 0%, 0.4); border-radius: 2px; color: #fff; box-shadow: inset 1px 1px 0 hsla(0, 0%, 100%, 0.1), 1px 1px 0 hsla(0, 0%, 0%, 0.1); padding: 3px 20px; font-size: 14px; font-weight: bold; font-family: ${SYSTEM_FONT}; &:active { box-shadow: inset 1px 1px 0 hsla(0, 0%, 0%, 0.25), 1px 1px 0 hsla(0, 0%, 0%, 0.1); } `; var internal_button_default = InternalButton; // src/utils/use-timer.ts var import_react11 = require("react"); var useTimer = (handler, period, isActive) => { const [timeDelay, setTimeDelay] = (0, import_react11.useState)(1); const initialTime = (0, import_react11.useRef)(); const callBack = (0, import_react11.useRef)(); (0, import_react11.useEffect)(() => { callBack.current = handler; }, [handler]); (0, import_react11.useEffect)(() => { if (isActive) { initialTime.current = (/* @__PURE__ */ new Date()).getTime(); const timer = setInterval(() => { const currentTime = (/* @__PURE__ */ new Date()).getTime(); const delay = currentTime - initialTime.current; initialTime.current = currentTime; setTimeDelay(delay / 1e3); callBack.current(timeDelay); }, period); return () => { clearInterval(timer); }; } }, [period, isActive, timeDelay]); }; // src/components/presenter-mode/timer.tsx var import_kbar2 = require("kbar"); var import_jsx_runtime5 = require("react/jsx-runtime"); var Timer = () => { const [timer, setTimer] = (0, import_react12.useState)(0); const [timerStarted, setTimerStarted] = (0, import_react12.useState)(false); const addToTimer = (0, import_react12.useCallback)((v) => setTimer((s) => s + v), []); const toggleTimer = (0, import_react12.useCallback)(() => setTimerStarted((s) => !s), []); const resetTimer = (0, import_react12.useCallback)(() => setTimer(0), []); useTimer(addToTimer, 1e3, timerStarted); const minutes = Math.floor(Math.round(timer) / 60); (0, import_kbar2.useRegisterActions)([ { id: "Start/Pause Timer", name: "Start/Pause Timer", keywords: "start pause", perform: toggleTimer, section: "Timer" }, { id: "Restart Timer", name: "Restart Timer", keywords: "restart", perform: resetTimer, section: "Timer" } ]); return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(FlexBox, { children: [ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(FlexBox, { justifyContent: "flex-start", flex: 1, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)( Text, { fontFamily: SYSTEM_FONT, fontWeight: "bold", fontSize: "2vw", textAlign: "left", children: `${String(minutes).padStart(2, "0")}:${String( Math.round(timer) - minutes * 60 ).padStart(2, "0")}` } ) }), /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(internal_button_default, { onClick: toggleTimer, children: timerStarted ? "Stop Timer" : "Start Timer" }), /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Box, { width: 8 }), /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(internal_button_default, { onClick: resetTimer, children: "Reset" }) ] }); }; // src/components/presenter-mode/index.tsx var import_jsx_runtime6 = require("react/jsx-runtime"); var endOfNextSlide = ({ slideIndex }) => ({ slideIndex: slideIndex + 1, stepIndex: GOTO_FINAL_STEP }); var PreviewSlideWrapper = import_styled_components7.default.div( ({ visible }) => ({ visibility: visible ? "visible" : "hidden" }) ); var PresenterMode = (props) => { const { children, theme, backgroundImage, template } = props; const deck = (0, import_react13.useRef)(null); const previewDeck = (0, import_react13.useRef)(null); const [notePortalNode, setNotePortalNode] = (0, import_react13.useState)(); const [showFinalSlide, setShowFinalSlide] = (0, import_react13.useState)(true); const [postMessage] = useBroadcastChannel( "spectacle_presenter_bus", (message) => { if (message.type === "SYNC_REQUEST") { postMessage("SYNC", deck.current.activeView); } } ); const [syncLocation, setLocation] = useLocationSync(__spreadValues({ setState: (state) => deck.current.skipTo(state) }, query_string_exports)); const onActiveStateChange = (0, import_react13.useCallback)( (activeView) => { var _a, _b; setLocation(active