```
- Note that publishing multiple pacakges via `changeset` to npm with an OTP code can often fail with `429 Too Many Requests` rate limiting error. Take a 5+ minute coffee break, then come back and try again.
+ Note that publishing multiple packages via `changeset` to npm with an OTP code can often fail with `429 Too Many Requests` rate limiting error. Take a 5+ minute coffee break, then come back and try again.
Then issue the following to also push git tags:
diff --git a/README.md b/README.md
index fd79d14ab..1bd92e96f 100644
--- a/README.md
+++ b/README.md
@@ -1,8 +1,5 @@
-
-
-
-
-
+[](https://commerce.nearform.com/open-source/spectacle/)
+
✨ A ReactJS based Presentation Library ✨
@@ -36,4 +33,4 @@ Please see our [contributing guide](CONTRIBUTING.md).
**Active:** Formidable is actively working on this project, and we expect to continue for work for the foreseeable future. Bug reports, feature requests and pull requests are welcome.
-[docs site]: https://www.formidable.com/open-source/spectacle/docs/
+[docs site]: https://commerce.nearform.com/open-source/spectacle/docs/
diff --git a/docs/api-reference.md b/docs/api-reference.md
index 56b0b8278..6fe4b8d62 100644
--- a/docs/api-reference.md
+++ b/docs/api-reference.md
@@ -56,6 +56,7 @@ These tags are for displaying textual content.
| Tag Name | Theme Props | Additional Props | Default Props |
|---------------------|-------------------------------------------------------------------------------------------------------------|----------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| **`Text`** | [**Space**](./props#space)
[**Color**](./props#color)
[**Typography**](./props#typography) | — | **color**: primary
**fontFamily**: text
**fontSize**: text
**textAlign**: left
**margin**: textMargin |
+| **`FitText`** | [**Space**](./props#space)
[**Color**](./props#color)
[**Typography**](./props#typography) | — | **color**: primary
**fontFamily**: text
**fontSize**: text
**textAlign**: center
**margin**: textMargin |
| **`Heading`** | [**Space**](./props#space)
[**Color**](./props#color)
[**Typography**](./props#typography) | — | **color**: secondary
**fontFamily**: header
**fontSize**: h1
**fontWeight**: bold
**textAlign**: center
**margin**: headerMargin |
| **`Link`** | [**Space**](./props#space)
[**Color**](./props#color)
[**Typography**](./props#typography)
| **href**: PropTypes.string | **color**: quaternary
**fontFamily**: text
**fontSize**: text
**textDecoration**: underline
**textAlign**: left
**margin**: textMargin |
| **`Quote`** | [**Space**](./props#space)
[**Color**](./props#color)
[**Typography**](./props#typography)
| — | **color**: primary
**fontFamily**: text
**fontSize**: text
**textAlign**: left
**borderLeft**: 1px solid secondary |
@@ -65,7 +66,7 @@ These tags are for displaying textual content.
| **`CodeSpan`** | [**Space**](./props#space)
[**Color**](./props#color)
[**Typography**](./props#typography) | — | **fontFamily**: monospace
**fontSize**: text |
-## Templates 🆕
+## Templates
Templates are overlays that are present on every slide. Typically, they contain controls and deck progress. Spectacle contains a default template, shown below, ready to use that contains the full screen control and the animated progress dots. `` also supports customizing the color using the prop `color` and CSS color values such as `purple` or `#fff`.
diff --git a/docs/extensions.md b/docs/extensions.md
index 41927981e..59cf90d8e 100644
--- a/docs/extensions.md
+++ b/docs/extensions.md
@@ -1,7 +1,7 @@
---
title: Extensions
-order: 8
-sidebar_position: 8
+order: 9
+sidebar_position: 9
---
# Third Party Extensions
@@ -29,4 +29,4 @@ For complete documentation of the CLI, please check out [the repository](https:/
A webpack MDX loader for Spectacle presentations.
-See [the repository](https://www.github.com/FormidableLabs/spectacle-mdx-loader) for usage, integration, and more information.
+Check [the repository](https://github.com/FormidableLabs/spectacle/tree/main/packages/spectacle-mdx-loader) for usage, integration, and more information.
diff --git a/docs/faq.md b/docs/faq.md
index f2d5c33a4..07fb39081 100644
--- a/docs/faq.md
+++ b/docs/faq.md
@@ -1,7 +1,7 @@
---
title: FAQ
-order: 9
-sidebar_position: 9
+order: 10
+sidebar_position: 10
---
# Frequently Asked Questions
@@ -12,8 +12,8 @@ Yes - you can export your slides in PDF format. Appending your presentation URL
If you want a black & white version of your slides printed to PDF, append your URL with `?exportMode=true&printMode=true` to get a printer-friendly, flattened, black & white print out of your slideshow.
-For more info about the query parameters Spectacle supports, please check out our section about it in the [advanced concepts documentation](./advanced-concepts.md#query-parameters).
+For more info about the query parameters Spectacle supports, please check out our section about it in the [presenting controls documentation](./presenting-controls#query-parameters).
## Can I write my presentation in TypeScript?
-Yes - Spectacle types are shipped with the package, so you can safely use Spectacle in any `.ts` or `.js` presentation without a separate type definition import.
+Yes—Spectacle types are shipped with the package, so you can safely use Spectacle in any `.ts` or `.js` presentation without a separate type definition import. TypeScript is now the default language for Spectacle and the `create-spectacle` CLI, so you can use it without any additional configuration.
diff --git a/docs/index.md b/docs/index.md
deleted file mode 100644
index d8cf24ce8..000000000
--- a/docs/index.md
+++ /dev/null
@@ -1,149 +0,0 @@
----
-sidebar_label: Basic Concepts
-order: 1
-sidebar_position: 1
-slug: /
----
-
-# Basic Concepts
-
-## Installation
-
-Installing Spectacle is as quick as you'd expect. Install it using your package manager of choice.
-
-```bash
-$ yarn add spectacle
-# or
-$ npm install --save spectacle
-```
-
-## Getting Started with Development
-
-The `src` directory contains all the source for the Spectacle library. All components designed to be part of the Spectacle API must be exported in `src/index.tsx`.
-
-#### JavaScript-based Decks
-
-To start the development server at port `3000` against a JavaScript-based deck (found in `examples/js`) use `yarn start:js` or `npm run start:js`.
-
-#### Markdown-based Decks
-
-To start the development server at port `3100` against a Markdown-based deck (found in `examples/md`) use `yarn start:md` or `npm run start:md`.
-
-## Writing your Presentation
-
-After installing Spectacle, all of your presentation and style logic will live in a main file, while your content exists either inline (with JSX) or in a separate markdown file (using MDX).
-
-### MDX/Markdown
-
-This approach involves statically generating your slides from a `.mdx` or .`md` file, which is accomplished with [`spectacle-cli`](https://www.github.com/FormidableLabs/spectacle-cli). With this package, you can either generate a new presentation (with the `spectacle-boilerplate` tool) or you can serve up an existing MDX/Markdown file as a presentation (with `spectacle -s`). It can be installed globally, locally, or used via `npx`.
-
-```bash
-# globally install `spectacle` and `spectacle-boilerplate` tools
-$ npm install --global spectacle-cli
-$ yarn global add spectacle-cli
-
-# serving a presentation using npx
-$ npx spectacle-cli
-
-# generating a new presentation using npx
-$ npx -p spectacle-cli spectacle-boilerplate
-```
-
-To serve a local Markdown or MDX file up as a presentation with the CLI tool:
-
-```bash
-# navigate to the directory containing your slides
-$ cd my-cool-presentation
-
-# run the CLI (given there is a slides.md or slides.mdx in the CWD)
-$ spectacle -s
-```
-
-To generate a new MDX or MD presentation using the boilerplate tool:
-
-```bash
-$ spectacle-boilerplate -m mdx
-$ spectacle-boilerplate -m md
-```
-
-To see a more complete examples of a presentation generated with MDX or Markdown, please check out our three samples available for use with the CLI as well as manual builds:
-
-- [`.md` Example](https://github.com/FormidableLabs/spectacle/tree/main/examples/md) (`spectacle`)
-- [`.mdx` Example](https://github.com/FormidableLabs/spectacle-mdx-loader/tree/main/examples/mdx) (`spectacle-mdx-loader`)
-- [`.mdx` + Babel Example](https://github.com/FormidableLabs/spectacle-cli/tree/main/examples/cli-mdx-babel) (`spectacle-cli`)
-
-For a more thorough understanding of the features and flags provided by the CLI, please see its [complete documentation](./extensions.md#spectacle-cli).
-
-**Note:** If you want to manually create the build infrastructure for MDX support in a Spectacle deck, you can add the [`spectacle-mdx-loader`](https://github.com/FormidableLabs/spectacle-mdx-loader) plugin to your webpack configuration.
-
-### JSX
-
-This approach is where you use the library's tags to compose your presentation. While you can mix in your own JSX syntax here, building your presentation with the supplied tags will allow for out-of-box themeing and layouts to work properly.
-
-The bare minimum you'll want to use to build your presentation are the `Deck` element and a `Slide` element. Each `Slide` represents a slide within your presentation `Deck` (the entire slideshow).
-
-To see a complete example of a presentation written in JSX, please check out our [sample JSX presentation](https://github.com/FormidableLabs/spectacle/blob/main/examples/js/index.js).
-
-You can also bootstrap a fresh JSX project with `spectacle-boilerplate`:
-
-```bash
-$ spectacle-boilerplate
-```
-
-### One HTML Page
-
-To create a Spectacle presentation that lives in a single HTML page, you will only need to add a few scripts to your setup:
-
-```html
-
-
-
-
-
-```
-
-... and then wrap your HTML in a declarative `module` script, like so:
-
-```html
-
-```
-
-To see a complete example of a presentation written as a single HTML page, please check out our [sample one page presentation](https://github.com/FormidableLabs/spectacle/blob/main/examples/one-page/index.html).
-
-## Presenting
-
-Spectacle comes with a built-in presenter mode. It shows you a slide lookahead, your current slide, current time (or time elapsed), and any notes you've appended to your slide:
-
-
-
-To present:
-
-1. Run `yarn start`, which will open up a presentation at [localhost:3000/](http://localhost:3000/) by default.
-2. Open a second browser window on a different screen.
-3. Append [`?presenterMode=true`](http://localhost:3000/?presenterMode=true) immediately after the `/`
-4. Give an amazingly in-sync and stylish presentation.
-
-**Note:** Any windows/tabs in the same browser running Spectacle will sync to one another, even if you aren't in presentation mode.
-
-
diff --git a/docs/index.mdx b/docs/index.mdx
new file mode 100644
index 000000000..c7e2908bc
--- /dev/null
+++ b/docs/index.mdx
@@ -0,0 +1,89 @@
+---
+title: Introduction
+order: 1
+sidebar_position: 1
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+import defaultDeckImg from '../website/static/img/default-deck.png'
+import presenterModeImg from '../website/static/img/presenter-mode.png'
+import animatedPresentationImg from '../website/static/img/presentation-mode.gif'
+
+Spectacle is a React-based library for creating sleek presentations using React or Markdown that gives you the ability to live demo your code and use React libraries in your slides.
+
+
+
+## How to get started with Spectacle
+
+
+
+
+ The fastest and easiest way to get started with Spectacle is to use the Create Spectacle CLI. This will create a new Spectacle project with a default deck and all the necessary dependencies.
+
+ There are four different kinds of templates you can use to create your Spectacle project:
+
+ 1. ⚡️ **One Page** - A single HTML file using [htm](https://github.com/developit/htm) that self contains everything you need. Since it does not require a server to run your presentation, this is the recommended option for quickly spinning up a deck.
+ 2. 📄 **Markdown** - An app that uses Markdown for your presentation’s content with support for [Markdown Slide Layouts](md-slide-layouts). This is a React app that uses webpack and imports the Markdown content using Spectacle’s [`MarkdownSlideSet`](api-reference#markdown-components) component.
+ 3. 🏗️ **React using Vite** - An app that lets you build your presentation in React and uses Vite for building and running. This is the recommended option if you plan on using a number of additional npm libraries in your presentation.
+ 4. 🏗️ **React using webpack** - An app that lets you build your presentation in React and uses webpack for building and running.
+
+ **To get started use `npx` or `pnpm dlx` to run the `create-spectacle` cli:**
+
+ ```bash
+
+ $ npx create-spectacle
+
+ ```
+
+ This will create a new Spectacle presentation in a directory of your deck’s name or one page file in the current directory.
+
+
+
+
+
+ 1. Install Spectacle by running `npm add spectacle`.
+
+ 2. In your main entry file, return the following Spectacle starter:
+
+ ```tsx
+ import { Deck, Slide, Heading, DefaultTemplate } from 'spectacle';
+
+ function App() {
+ return (
+ }>
+
+ Welcome to Spectacle
+
+
+ );
+ }
+
+ export default App;
+ ```
+
+ \:\:\:info
+
+ If you are using NextJS with App Router, Spectacle needs to be rendered inside a client component. You can read more about this [here](https://nextjs.org/docs/app/building-your-application/rendering/client-components).
+
+ \:\:\:
+
+
+
+
+
+## Presenter Mode
+
+Spectacle also has a presenter mode that allows you to view your slides and notes on one screen while your audience views your slides on another. To use presenter mode, open a second browser window and visit your deck’s local server and enable it by using the key command. You can find more information about presentation controls [here](presenting-controls).
+
+
+
+
+
+## Documentation, Contributing, and Source
+
+For more information about Spectacle and its components, check out [the docs](https://commerce.nearform.com/open-source/spectacle).
+
+Interested in helping out or seeing what's happening under the hood? Spectacle is maintained [on Github](https://github.com/FormidableLabs/spectacle) and you can [start contributing here](https://github.com/FormidableLabs/spectacle/blob/main/CONTRIBUTING.md).
+
+For any questions, feel free to [open a new question on Github](https://github.com/FormidableLabs/spectacle/issues/new?template=question.md).
diff --git a/docs/md-slide-layouts.md b/docs/md-slide-layouts.md
new file mode 100644
index 000000000..e9a246a48
--- /dev/null
+++ b/docs/md-slide-layouts.md
@@ -0,0 +1,89 @@
+---
+title: Markdown Slide Layouts
+order: 7
+sidebar_position: 7
+---
+
+# Markdown Slide Layouts
+
+Spectacle supports a number of layout containers for use in your Markdown slides. These containers are designed to enable more complex layouts previously not doable in Markdown without the usage of importing JSX elements into MDX.
+
+Spectacle uses `---` (three dashes) to delimit each slide in a Markdown file. Spectacle adds supports for a JSON-based configuration object with the slide delimiter in your Markdown file. This configuration object is used to define the layout of the slide. Currently, two types of layouts are support are `center` and `columns`.
+
+:::info
+
+Markdown-annotated slide layouts is available only with `.md` files. This feature is not available with `.mdx` files where you can use JSX-based layout primitives.
+
+:::
+
+## Columns Layout
+
+The columns layout is used to create a row-based column layout. The columns layout is defined by the following JSON object:
+
+```json
+{ "layout" : "columns" }
+```
+
+Each column section is defined by a `::section` delimiter. The number of columns is determined by the number of `::section` annotations.
+
+
+
+```md
+
+--- { "layout" : "columns" }
+
+::section
+
+
+
+::section
+
+
+
+---
+
+# Ghost-type Pokémon
+
+The Ghost-type (ゴーストタイプ Gosuto taipu in Japanese) is one of the eighteen Pokémon elemental types.
+
+```
+
+This layout has the underlying JSX structure and divides each section into an array:
+
+```jsx
+
+ {sectionsArray}
+
+```
+
+## Center Layout
+
+The center layout is used to create a single column layout with the content centered. The center layout is defined by the following JSON object:
+
+```json
+{ "layout" : "center" }
+```
+
+
+
+```md
+
+--- { "layout" : "center" }
+
+
+
+---
+
+# Gengar
+
+Gengar is a dark purple, bipedal Pokémon with a roundish body. It has red eyes and a wide mouth that is usually curled into a sinister grin. Multiple spikes cover its back, and it has large pointed ears. Its arms and legs are short with three digits on both its hands and feet. It also has a stubby tail.
+
+```
+
+This layout has the underlying JSX structure and passes all the slide content as chidren:
+
+```jsx
+
+ {content}
+
+```
\ No newline at end of file
diff --git a/docs/advanced-concepts.md b/docs/presenting-controls.mdx
similarity index 50%
rename from docs/advanced-concepts.md
rename to docs/presenting-controls.mdx
index a40724973..75dc93ac2 100644
--- a/docs/advanced-concepts.md
+++ b/docs/presenting-controls.mdx
@@ -1,28 +1,31 @@
---
-title: Advanced Concepts
+title: Presenting Controls
order: 2
sidebar_position: 2
---
-# Advanced Concepts
+import commandBarImage from "../website/static/img/command-bar.png"
-## Build & Deployment
+# Presenting Controls
-There are a variety of ways to host your Spectacle presentation.
+Spectacle comes with a few keyboard shortcuts to help you navigate your presentation. These shortcuts are available in both the browser and the Presenter Mode. You can also use the command bar to see all available shortcuts.
-1. If you are integrating this lib yourself, take your build and follow the linked instructions from any of (but not limited to) the following providers: [Heroku](https://devcenter.heroku.com/articles/git#deploying-code), [Zeit](https://zeit.co/docs/v2/platform/deployments), or [Surge](https://surge.sh/help/deploying-continuously-using-git-hooks).
+## Command Bar
+
+Pressing ⌘/Ctrl + k will open the command bar. This will show you all available keyboard shortcuts and common navigation options.
+
+
-2. If using `spectacle-cli` (either `spectacle --action build` or `spectacle-boilerplate` with `yarn clean && yarn build`) your output is `dist/` and upload that directory to any of the above static hosting providers.
## Keyboard Controls
| Key Combination | Function |
| --------------- | ---------------------- |
-| Right Arrow | Next Slide |
-| Left Arrow | Previous Slide |
-| Alt/Option + O | Toggle Overview Mode |
-| Alt/Option + P | Toggle Presenter Mode |
-| Alt/Option + F | Toggle Fullscreen Mode |
+| → | Next Slide |
+| ← | Previous Slide |
+| ⌥/Alt + ⇧ + O | Toggle Overview Mode |
+| ⌥/Alt + ⇧ + P | Toggle Presenter Mode |
+| ⌥/Alt + ⇧ + F | Toggle Fullscreen Mode |
## Query Parameters
@@ -34,4 +37,4 @@ To combine parameters, use multiple `&` to separate the parameters, e.g.: `&expo
| --------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `exportMode` | For exporting your presentation as a PDF. Add it to your project URL and "Save to PDF" directly from the browser |
| `printMode` | Turns your slideshow into a printer-friendly, black & white version. Meant for use concurrently with `?exportMode` e.g. `?exportMode=true&printMode=true` |
-| `presenterMode` | Displays a Presenter Mode for ease of presentation. For more info on this mode, please see [Presenting](./index.md#presenting) in our Basic Concepts doc |
+| `presenterMode` | Displays a Presenter Mode with slide notes, a timer, and the upcoming slide. |
diff --git a/docs/props.md b/docs/props.md
index 2f014cdab..052f063db 100644
--- a/docs/props.md
+++ b/docs/props.md
@@ -44,7 +44,7 @@ const transition = {
## Color
-**Color** props are used by [`CodeSpan`](./api-reference.md#code-span), [`Text`](./api-reference.md#text), [`Link`](./api-reference.md#link), [`Heading`](./api-reference.md#heading), [`Quote`](./api-reference.md#quote), [`Table`](./api-reference.md#table), [`TableHeader`](./api-reference.md#table-header), [`TableBody`](./api-reference.md#table-body), [`TableRow`](./api-reference.md#table-row), [`TableCell`](./api-reference.md#table-cell), [`UnorderedList`](./api-reference.md#unordered-list), [`OrderedList`](./api-reference.md#ordered-list), and [`ListItem`](./api-reference.md#list-item).
+**Color** props are used by [`CodeSpan`](./api-reference.md#typography-tags), [`Text`](./api-reference.md#typography-tags), [`Link`](./api-reference.md#typography-tags), [`Heading`](./api-reference.md#typography-tags), [`Quote`](./api-reference.md#typography-tags), [`Table`](./api-reference.md#table-tags), [`TableHeader`](./api-reference.md#table-tags), [`TableBody`](./api-reference.md#table-tags), [`TableRow`](./api-reference.md#table-tags), [`TableCell`](./api-reference.md#table-tags), [`UnorderedList`](./api-reference.md#typography-tags), [`OrderedList`](./api-reference.md#typography-tags), and [`ListItem`](./api-reference.md#typography-tags).
| Name | PropType | Description | Example |
| ------------------------- | ---------------- | ------------------------------------------------------- | ------------------------ |
@@ -53,7 +53,7 @@ const transition = {
## Space
-**Space** props used by [`Box`](./api-reference.md#box), [`FlexBox`](./api-reference.md#flex-box), [`Grid`](./api-reference.md#grid), [`CodeSpan`](./api-reference.md#code-span), [`Text`](./api-reference.md#text), [`Link`](./api-reference.md#link), [`Heading`](./api-reference.md#heading), [`Quote`](./api-reference.md#quote), [`Table`](./api-reference.md#table), [`TableHeader`](./api-reference.md#table-header), [`TableBody`](./api-reference.md#table-body), [`TableRow`](./api-reference.md#table-row), [`TableCell`](./api-reference.md#table-cell), [`UnorderedList`](./api-reference.md#unordered-list), [`OrderedList`](./api-reference.md#ordered-list), and [`ListItem`](./api-reference.md#list-item).
+**Space** props used by [`Box`](./api-reference.md#layout-tags), [`FlexBox`](./api-reference.md#layout-tags), [`Grid`](./api-reference.md#layout-tags), [`CodeSpan`](./api-reference.md#typography-tags), [`Text`](./api-reference.md#typography-tags), [`Link`](./api-reference.md#typography-tags), [`Heading`](./api-reference.md#typography-tags), [`Quote`](./api-reference.md#typography-tags), [`Table`](./api-reference.md#table-tags), [`TableHeader`](./api-reference.md#table-tags), [`TableBody`](./api-reference.md#table-tags), [`TableRow`](./api-reference.md#table-tags), [`TableCell`](./api-reference.md#table-tags), [`UnorderedList`](./api-reference.md#typography-tags), [`OrderedList`](./api-reference.md#typography-tags), and [`ListItem`](./api-reference.md#typography-tags).
| Name | PropType | Description | Example |
| ----------------------- | ---------------- | ----------------------------------------------------------------------- | ----------------------------------- |
@@ -74,13 +74,13 @@ const transition = {
## Typography
-**Typography** props are used by [`CodeSpan`](./api-reference.md#code-span), [`Text`](./api-reference.md#text), [`Link`](./api-reference.md#link), [`Heading`](./api-reference.md#heading), [`Quote`](./api-reference.md#quote), [`Table`](./api-reference.md#table), [`TableHeader`](./api-reference.md#table-header), [`TableBody`](./api-reference.md#table-body), [`TableRow`](./api-reference.md#table-row), [`TableCell`](./api-reference.md#table-cell), [`UnorderedList`](./api-reference.md#unordered-list), [`OrderedList`](./api-reference.md#ordered-list), and [`ListItem`](./api-reference.md#list-item).
+**Typography** props are used by [`CodeSpan`](./api-reference.md#typography-tags), [`Text`](./api-reference.md#typography-tags), [`Link`](./api-reference.md#typography-tags), [`Heading`](./api-reference.md#typography-tags), [`Quote`](./api-reference.md#typography-tags), [`Table`](./api-reference.md#table-tags), [`TableHeader`](./api-reference.md#table-tags), [`TableBody`](./api-reference.md#table-tags), [`TableRow`](./api-reference.md#table-tags), [`TableCell`](./api-reference.md#table-tags), [`UnorderedList`](./api-reference.md#typography-tags), [`OrderedList`](./api-reference.md#typography-tags), and [`ListItem`](./api-reference.md#typography-tags).
| Name | PropType | Description | Example |
| --------------- | ---------------- | -------------------------------------------------------------- | ------------------------------------------------------ |
| `fontFamily` | PropTypes.string | Set CSS `font-family` value or `fonts` theme value | `Helvetica` or `primary` |
| `fontSize` | PropTypes.string | Set CSS `font-size` value or `fontSizes` theme value | `16px` or `bodyCopy` |
-| `fontWeight` | PropTypes.string | Set CSS `font-weight` value or `fontWeights` theme value | `400`, `bold`, or [`Heading`](./api-reference.md#heading) |
+| `fontWeight` | PropTypes.string | Set CSS `font-weight` value or `fontWeights` theme value | `400`, `bold`, or [`Heading`](./api-reference.md#typography-tags) |
| `lineHeight` | PropTypes.string | Set CSS `line-height` value or `fontWeights` theme value | `1.5em` or `paragraph` |
| `letterSpacing` | PropTypes.string | Set CSS `letter-spacing` value or `letterSpacings` theme value | `1px` or `spreadOutText` |
| `textAlign` | PropTypes.string | Set CSS `text-align` value | `left` |
@@ -88,7 +88,7 @@ const transition = {
## Layout
-**Layout** props are used by [`Box`](./api-reference.md#box), [`FlexBox`](./api-reference.md#flex-box), [`Grid`](./api-reference.md#grid), [`Table`](./api-reference.md#table), [`TableHeader`](./api-reference.md#table-header), [`TableBody`](./api-reference.md#table-body), [`TableRow`](./api-reference.md#table-row), [`TableCell`](./api-reference.md#table-cell), [`CodePane`](./api-reference.md#code-pane), and [`Markdown`](./api-reference.md#markdown-components).
+**Layout** props are used by [`Box`](./api-reference.md#layout-tags), [`FlexBox`](./api-reference.md#layout-tags), [`Grid`](./api-reference.md#layout-tags), [`Table`](./api-reference.md#table-tags), [`TableHeader`](./api-reference.md#table-tags), [`TableBody`](./api-reference.md#table-tags), [`TableRow`](./api-reference.md#table-tags), [`TableCell`](./api-reference.md#table-tags), [`CodePane`](./api-reference.md#code-pane), and [`Markdown`](./api-reference.md#markdown-components).
| Name | PropType | Description | Example |
| ----------- | ------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------ |
@@ -106,7 +106,7 @@ const transition = {
## Flex
-**Flex** props are used by [`FlexBox`](./api-reference.md#flex-box).
+**Flex** props are used by [`FlexBox`](./api-reference.md#layout-tags).
| Name | PropType | Description | Example |
| ---------------- | ------------------------------------ | ------------------------------- | --------------- |
@@ -123,7 +123,7 @@ const transition = {
## Grid
-**Grid** props are used by [`Grid`](./api-reference.md#grid).
+**Grid** props are used by [`Grid`](./api-reference.md#layout-tags).
| Name | PropType | Description | Example |
| --------------------- | ------------------------------------ | ------------------------------------- | --------------------------------------------- |
@@ -142,7 +142,7 @@ const transition = {
## Position
-**Position** props are used by [`Box`](./api-reference.md#box), [`FlexBox`](./api-reference.md#flex-box), [`Grid`](./api-reference.md#grid), [`CodePane`](./api-reference.md#code-pane), [`FullScreen`](./api-reference.md#fullscreen), [`Progress`](./api-reference.md#progress), [`AnimatedProgress`](./api-reference.md#animatedprogress), and [`Markdown`](./api-reference.md#markdown-components).
+**Position** props are used by [`Box`](./api-reference.md#layout-tags), [`FlexBox`](./api-reference.md#layout-tags), [`Grid`](./api-reference.md#layout-tags), [`CodePane`](./api-reference.md#typography-tags), [`FullScreen`](./api-reference.md#fullscreen), [`Progress`](./api-reference.md#progress), [`AnimatedProgress`](./api-reference.md#animatedprogress), and [`Markdown`](./api-reference.md#markdown-components).
| Name | PropType | Description | Example |
| ---------- | ---------------- | ------------------------ | ---------- |
@@ -155,7 +155,7 @@ const transition = {
## Border
-**Border** props are used by [`Box`](./api-reference.md#box), [`FlexBox`](./api-reference.md#flex-box), [`Grid`](./api-reference.md#grid), [`Table`](./api-reference.md#table), [`TableHeader`](./api-reference.md#table-header), [`TableBody`](./api-reference.md#table-body), [`TableRow`](./api-reference.md#table-row), and [`TableCell`](./api-reference.md#table-cell).
+**Border** props are used by [`Box`](./api-reference.md#layout-tags), [`FlexBox`](./api-reference.md#layout-tags), [`Grid`](./api-reference.md#layout-tags), [`Table`](./api-reference.md#table-tags), [`TableHeader`](./api-reference.md#table-tags), [`TableBody`](./api-reference.md#table-tags), [`TableRow`](./api-reference.md#table-tags), and [`TableCell`](./api-reference.md#table-tags).
| Name | PropType | Description | Example |
| ------------------------- | ------------------------------------ | ---------------------------------------------- | -------------------- |
diff --git a/docs/slide-layouts.md b/docs/react-slide-layouts.md
similarity index 98%
rename from docs/slide-layouts.md
rename to docs/react-slide-layouts.md
index f3ad90820..11cfca5fd 100644
--- a/docs/slide-layouts.md
+++ b/docs/react-slide-layouts.md
@@ -1,5 +1,5 @@
---
-title: Slide Layouts
+title: React Slide Layouts
order: 6
sidebar_position: 6
---
@@ -64,7 +64,7 @@ A layout with a section title
| Props | Type | Required | Example |
|-----------------|-----------------------------------------------|----------|----------------------|
| `...slideProps` | [Slide Props](./api-reference#slide) | ❌ | |
-| `sectionProps` | [Text Props](./api-reference#typography-tags) | ❌ | { fontSize: "48px" } |
+| `sectionProps` | [Text Props](./api-reference#typography-tags) | ❌ | `{ fontSize: "48px" }` |

@@ -75,7 +75,7 @@ A layout with a centered statement
| Props | Type | Required | Example |
|------------------|-----------------------------------------------|----------|----------------------|
| `...slideProps` | [Slide Props](./api-reference#slide) | ❌ | |
-| `statementProps` | [Text Props](./api-reference#typography-tags) | ❌ | { fontSize: "48px" } |
+| `statementProps` | [Text Props](./api-reference#typography-tags) | ❌ | `{ fontSize: "48px" }` |

@@ -88,8 +88,8 @@ A layout to present a fact in large font
| `children` | `ReactNode` | ✅ | `100%` | |
| `...slideProps` | [Slide Props](./api-reference#slide) | ❌ | | |
| `factInformation` | `ReactNode` | ❌ | `Fact information` | |
-| `factProps` | [Text Props](./api-reference#typography-tags) | ❌ | { fontSize: "100px" } | |
-| `factInformationProps` | [Text Props](./api-reference#typography-tags) | ❌ | { fontSize: "48px" } | |
+| `factProps` | [Text Props](./api-reference#typography-tags) | ❌ | `{ fontSize: "100px" }` | |
+| `factInformationProps` | [Text Props](./api-reference#typography-tags) | ❌ | `{ fontSize: "48px" }` | |
| `factFontSize` | `string` | ❌ | `150px` | `250px` |

@@ -103,8 +103,8 @@ A quote and attribution layout
| `...slideProps` | [Slide Props](./api-reference#slide) | ❌ | |
| `children` | `ReactNode` | ✅ | `To be, or not to be` |
| `attribution` | `ReactNode` | ✅ | `William Shakespeare` |
-| `quoteProps` | [Text Props](./api-reference#typography-tags) | ❌ | { fontSize: "100px" } |
-| `attributionProps` | [Text Props](./api-reference#typography-tags) | ❌ | { fontSize: "48px" } |
+| `quoteProps` | [Text Props](./api-reference#typography-tags) | ❌ | `{ fontSize: "100px" }` |
+| `attributionProps` | [Text Props](./api-reference#typography-tags) | ❌ | `{ fontSize: "48px" }` |


diff --git a/docs/themes.md b/docs/themes.md
index 6bb770f1b..dc43be624 100644
--- a/docs/themes.md
+++ b/docs/themes.md
@@ -1,7 +1,7 @@
---
title: Themes
-order: 7
-sidebar_position: 7
+order: 8
+sidebar_position: 8
---
# Theme System
diff --git a/docs/tutorial.md b/docs/tutorial.md
deleted file mode 100644
index 0eaad78a5..000000000
--- a/docs/tutorial.md
+++ /dev/null
@@ -1,162 +0,0 @@
----
-title: Getting Started
-order: 3
-sidebar_position: 3
----
-
-# Getting Started with Spectacle: A Tutorial
-
-In this guide, we'll show you a couple of different ways to get started with Spectacle and walk you through the creation and customization of a presentation deck.
-
-## Option One: Using Vite to bootstrap a React-based Spectacle app
-
-1. Spin up a new React project using [`vite`](https://vitejs.dev/guide/#scaffolding-your-first-vite-project):
-
- ```bash
- npm create vite@latest my-spectacle-deck -- --template react-ts
- ```
-
-2. Install Spectacle by running `npm add spectacle`.
-
-3. In `App.tsx`, replace the boilerplate content with this Spectacle starter:
-
- ```tsx
- import { Deck, Slide, Heading, DefaultTemplate } from 'spectacle';
-
- function App() {
- return (
- }>
-
- Welcome to Spectacle
-
-
- );
- }
-
- export default App;
- ```
-
-## Option Two: Using Next.js App Router to bootstrap a React-based Spectacle app
-
-1. Spin up a new React project using [`create-next-app`](https://nextjs.org/docs/pages/api-reference/create-next-app):
-
- ```bash
- npx create-next-app@latest
- ```
-
-2. Install Spectacle by running `npm add spectacle`.
-
-3. Create a `deck.tsx` file inside the `app` directory and add the following Spectacle starter:
-
- ```tsx
- 'use client';
-
- import { Deck, Slide, Heading, DefaultTemplate } from 'spectacle';
-
- export const SpectacleDeck = () => {
- return (
- }>
-
- Welcome to Spectacle
-
-
- );
- };
- ```
-
-4. In `app/page.tsx`, import the `` component:
-
- ```tsx
- import { SpectacleDeck } from './deck';
-
- export default function Home() {
- return ;
- }
- ```
-
-## Option Three: Using Markdown and the Spectacle CLI
-
-1. Create a new markdown file. You can use `.md` or `.mdx` (MDX lets you mix JSX components inside markdown).
-
- You can use this as a starter:
-
- ```md
- # Welcome to Spectacle
-
- - This is a list item
- - This is another list item
-
- ---
-
- # Second Slide
-
- Text can be **bold** or _italic_!
-
- Notes: These are presenter notes, only visible in presenter mode to the speaker.
- ```
-
- **Note:** The triple dash (`---`) is used as a slide delimiter. The `Notes:` keyword is used to embed presenter notes only visible to the speaker in presenter mode.
-
-2. To view your slides, supply your markdown to the Spectacle CLI to start a local web server.
-
- ```bash
- $ npm install --global spectacle-cli
- $ spectacle -s my-slides.mdx
- ```
-
-3. And you're good to go! The web server you started supports live refreshing and will update your deck as you make changes to the markdown file.
-
-## Option Four: Using One Page
-
-One Page is a single self-contained `HTML` file that lets you build a deck using no build steps, using [htm](https://github.com/developit/htm) over JSX to reduce the dependencies and load time.
-
-As a self-contained entity, it already has references to the dependencies you need to author and launch a deck in a web browser. Since there is no tooling required, One Page is also optimal on tablets. The One Page `HTML` file can be downloaded from the `examples` directory [in this repository](https://github.com/FormidableLabs/spectacle/blob/main/examples/one-page/index.html).
-
-## Next Steps
-
-### Styling your Spectacle Deck
-
-The easiest way to apply consistent styles to your Spectacle deck is using [themes](./themes.md).
-
-1. Create a theme JS file containing a single object export. Supplied properties will be merged with the default base theme (found in Spectacle at `src/theme/default-theme.js`).
-
- Here's a sample object:
-
- ```js
- export default {
- colors: {
- primary: 'red',
- secondary: 'green'
- },
- fonts: {
- header: '"Helvetica Neue", Helvetica, Arial, sans-serif'
- },
- fontSizes: {
- h1: '72px',
- h2: '64px'
- }
- };
- ```
-
-2. Consume the theme using the approach of your choice:
-
- a. To use a custom theme with a JSX- (Option One) or HTM- (Option Three) Deck, supply the object to the `theme` prop in the `Deck` tag. ``.
-
- b. To use a custom theme with the Markdown CLI (Option Two), supply the file using the `-t` argument.
-
- ```bash
- $ npm install --global spectacle-cli
- $ spectacle -s my-slides.mdx -t custom-theme.js
- ```
-
-### Sharing your Spectacle Deck
-
-For more information on [presenting](./index.md#presenting), [exporting](./advanced-concepts.md#exporting), [building](./advanced-concepts.md#build--deployment), or [deploying](./advanced-concepts.md#build--deployment) your Spectacle deck, please check out [the documentation on advanced concepts](./advanced-concepts.md).
-
-## Documentation, Contributing, and Source
-
-For more information about Spectacle and its components, check out [the docs](https://formidable.com/open-source/spectacle).
-
-Interested in helping out or seeing what's happening under the hood? Spectacle is maintained [on Github](https://github.com/FormidableLabs/spectacle) and you can [start contributing here](https://github.com/FormidableLabs/spectacle/blob/main/CONTRIBUTING.md).
-
-For any questions, feel free to [open a new question on Github](https://github.com/FormidableLabs/spectacle/issues/new?template=question.md).
diff --git a/examples/js/index.js b/examples/js/index.js
index a04923005..cdf0b1f41 100644
--- a/examples/js/index.js
+++ b/examples/js/index.js
@@ -1,4 +1,5 @@
import React from 'react';
+import { createRoot } from 'react-dom/client';
import {
FlexBox,
Heading,
@@ -11,6 +12,7 @@ import {
Slide,
Deck,
Text,
+ FitText,
Grid,
Box,
Image,
@@ -21,7 +23,6 @@ import {
DefaultTemplate,
SlideLayout
} from 'spectacle';
-import ReactDOM from 'react-dom';
const formidableLogo =
'https://avatars2.githubusercontent.com/u/5078602?s=280&v=4';
@@ -37,10 +38,10 @@ const theme = {
const SlideFragments = () => (
<>
-
+
This is a slide fragment.
-
+
This is also a slide fragment.
This item shows up!
@@ -138,6 +139,19 @@ const Presentation = () => (
+
+ This is a Heading
+
+ This is a FitText component
+
+
+ Shorter fit text
+
+ This is a Text. (Resize this window!)
+
These
@@ -253,5 +267,5 @@ const Presentation = () => (
);
-const root = ReactDOM.createRoot(document.getElementById('root'));
+const root = createRoot(document.getElementById('root'));
root.render();
diff --git a/examples/md/slides.md b/examples/md/slides.md
index a5245d078..c8f3106a7 100644
--- a/examples/md/slides.md
+++ b/examples/md/slides.md
@@ -1,21 +1,28 @@
-# Spectacle Presentation (MD) 👋
+---
+# Spectacle Presentation (MD) 👋
These slides are bare Markdown with nothing special.
- `one`
- "two"
- 'three'
----
+--- { "layout" : "columns" }
+
+::section
# Write your Spectacle Presentations in Markdown
+And use layout primitives to define columns!
+
+::section
+
## And seamlessly use React Components
**How sweet is that**
**(super sweet)**
----
+--- { "layout" : "center" }

diff --git a/examples/one-page/index.html b/examples/one-page/index.html
index 531459461..37d6341bc 100644
--- a/examples/one-page/index.html
+++ b/examples/one-page/index.html
@@ -9,15 +9,47 @@
-
-
-
-
-
-
-
+
diff --git a/examples/one-page/package.json b/examples/one-page/package.json
index e58a1f59d..6daa9fa51 100644
--- a/examples/one-page/package.json
+++ b/examples/one-page/package.json
@@ -5,6 +5,7 @@
"pretty": "^2.0.0"
},
"scripts": {
+ "start": "pnpm exec serve --listen 5000 ../..",
"build": "wireit",
"lint": "wireit",
"lint:fix": "wireit",
diff --git a/examples/one-page/scripts/one-page.js b/examples/one-page/scripts/one-page.js
index 2b689c022..051c301d7 100644
--- a/examples/one-page/scripts/one-page.js
+++ b/examples/one-page/scripts/one-page.js
@@ -2,6 +2,16 @@
/**
* Generate the JS `index.html` from `examples/js/index.js`
+ *
+ * To rebuild:
+ * ```sh
+ * pnpm run build:one-page
+ * ```
+ *
+ * To do dev builds and testing:
+ * ```sh
+ * USE_LOCAL=true IS_DEV=true pnpm run build:one-page && pnpm run start:one-page
+ * ```
*/
const fs = require('fs').promises;
const path = require('path');
@@ -9,12 +19,92 @@ const path = require('path');
const { transformFileAsync } = require('@babel/core');
const pretty = require('pretty');
+// Paths
const EXAMPLES = path.resolve(__dirname, '../..');
+const SPECTACLE_PATH = path.resolve(__dirname, '../../../packages/spectacle');
const SRC_FILE = path.join(EXAMPLES, 'js/index.js');
const DEST_FILE = path.join(EXAMPLES, 'one-page/index.html');
+// Dependencies.
+const ESM_SH_VERSION = ''; // 'v121/, stable, etc.
+const {
+ dependencies,
+ peerDependencies
+} = require(`${SPECTACLE_PATH}/package.json`);
+const reactPkgPath = require.resolve('react/package.json', {
+ paths: [SPECTACLE_PATH]
+});
+const { version: reactVersion } = require(reactPkgPath);
+const EXTERNAL = `external=react,react-dom`;
+
+// Toggle dev resources. (Use if debugging load / dependency errors).
+const IS_DEV = process.env.IS_DEV === 'true';
+const DEV = IS_DEV ? '&dev' : '';
+
+// Use local built spectacle? Toggle to `true` for dev-only.
+// Note: Due to CORS, you'll need to run `pnpm run --filter ./examples/one-page start` and
+// open http://localhost:5000/examples/one-page to work.
+const USE_LOCAL = process.env.USE_LOCAL === 'true';
+
+// ================================================================================================
+// Import Map
+// ================================================================================================
+const importUrl = (k, v, extra = '', params = '') => {
+ let external = EXTERNAL;
+
+ // Pin react and react-dom.
+ if (k === 'react' || k === 'react-dom') {
+ v = reactVersion;
+ external = '';
+ }
+
+ return `https://esm.sh/${ESM_SH_VERSION}${k}@${v}${extra}?${external}${params}${DEV}`;
+};
+
+const getImportMap = () => {
+ // Start with extra imports for one-page alone.
+ const importMap = {
+ htm: importUrl('htm', '^3'),
+ spectacle: USE_LOCAL
+ ? '../../packages/spectacle/lib/index.mjs'
+ : importUrl('spectacle', '10', '', '&bundle')
+ };
+
+ Object.entries(Object.assign({}, dependencies, peerDependencies))
+ .sort((a, b) => a[0].localeCompare(b[0]))
+ .forEach(([k, v]) => {
+ // General
+ importMap[k] = importUrl(k, v);
+
+ // Special case internal deps
+ if (k === 'react') {
+ importMap[`${k}/jsx-runtime`] = importUrl(k, v, '/jsx-runtime');
+ }
+ if (k === 'react-dom') {
+ importMap[`${k}/client`] = importUrl(k, v, '/client');
+ }
+ if (k === 'react-syntax-highlighter') {
+ importMap[`${k}/dist/cjs/styles/prism/vs-dark.js`] = importUrl(
+ k,
+ v,
+ '/dist/esm/styles/prism/vs-dark.js'
+ );
+ importMap[`${k}/dist/cjs/styles/prism/index.js`] = importUrl(
+ k,
+ v,
+ '/dist/esm/styles/prism/index.js'
+ );
+ }
+ });
+
+ return importMap;
+};
+
+// ================================================================================================
+// Rewriting
+// ================================================================================================
const htmImport = `
-import htm from 'https://unpkg.com/htm@^3?module';
+import htm from 'htm';
const html = htm.bind(React.createElement);
`
.replace(/ /gm, '')
@@ -27,7 +117,7 @@ const spectacleImportReplacer = (match, imports) => {
.map((i) => ` ${i.trim()}`)
.join(`,\n`);
- return `const {\n${imports}\n} = Spectacle;\n\n${htmImport}`;
+ return `import {\n${imports}\n} from 'spectacle';\n\n${htmImport}`;
};
const getSrcContent = async (src) => {
@@ -40,7 +130,6 @@ const getSrcContent = async (src) => {
// Mutate exports and comments.
code = code
// Mutate exports to our global imports.
- .replace(/import React(|DOM) from 'react(|-dom)';[\n]*/gm, '')
.replace(/import {[ ]*(.*)} from 'spectacle';/, spectacleImportReplacer)
// Hackily fix / undo babel's poor control comment placment.
.replace(/\/\/ SPECTACLE_CLI/gm, '\n// SPECTACLE_CLI')
@@ -84,17 +173,27 @@ const getSrcContent = async (src) => {
return code;
};
+// ================================================================================================
+// Output
+// ================================================================================================
const writeDestContent = async (destFile, code) => {
// Format for indentation in index.html.
const indent = ' ';
- code = `${indent}${code}`;
- code = code.split('\n').join(`\n${indent}`);
+ code = `${indent}${code.split('\n').join(`\n${indent}`)}`;
+
+ // Import map
+ let importMap = JSON.stringify({ imports: getImportMap() }, null, 2);
+ importMap = `${indent}${importMap.split('\n').join(`\n${indent}`)}`;
// Get destination content.
let destContent = (await fs.readFile(destFile)).toString();
// Mutate in our updated code.
destContent = destContent
+ .replace(
+ /(`
- );
-
- // README instructions for changing dist directory
- expect(await peakFile('README.md')).toContain(
- `\`build.outDir\` in \`vite.config.js`
- );
-
- // Custom lang/port
- expect(await peakFile('index.html')).toContain(`lang="en"`);
- expect(pak.scripts.start).toContain('--port 3000');
- });
-
it('generates a onepage file', async () => {
await runCliWithArgs({ type: 'onepage' });
@@ -214,16 +185,8 @@ describe('create-spectacle', () => {
.readFile(HTML_PATH, 'utf8')
.then((buffer) => buffer.toString());
- // Should have deps
- const deps = [
- 'https://unpkg.com/react@18.1.0/umd/react.production.min.js',
- 'https://unpkg.com/react-dom@18.1.0/umd/react-dom.production.min.js',
- 'https://unpkg.com/react-is@18.1.0/umd/react-is.production.min.js',
- 'https://unpkg.com/prop-types@15.7.2/prop-types.min.js',
- 'https://unpkg.com/spectacle@^9/dist/spectacle.min.js'
- ];
- deps.forEach((dep) => {
- expect(contents).toContain(``);
+ Array.from(generateImportMap().entries()).forEach(([pkg, url]) => {
+ expect(contents).toContain(`"${pkg}": "${url}"`);
});
});
});
@@ -231,7 +194,7 @@ describe('create-spectacle', () => {
/**
* Run the cli with certain args
*/
-type Type = 'tsx' | 'jsx' | 'tsx-vite' | 'jsx-vite' | 'onepage';
+type Type = 'tsx' | 'md' | 'tsx-vite' | 'onepage';
const runCliWithArgs = ({
type,
lang = 'en',
diff --git a/packages/create-spectacle/src/cli.ts b/packages/create-spectacle/src/cli.ts
index 9dd41e9c8..bccb6e544 100644
--- a/packages/create-spectacle/src/cli.ts
+++ b/packages/create-spectacle/src/cli.ts
@@ -14,9 +14,8 @@ import {
writeOnePageHTMLFile,
writeViteProjectFiles
} from './templates/file-writers';
-// @ts-ignore
-import { devDependencies } from '../package.json';
+const spectaclePackage = require(`${__dirname}/../spectacle-package.json`);
const argv = yargs(hideBin(process.argv)).argv;
const cwd = process.cwd();
@@ -29,14 +28,13 @@ enum ArgName {
}
const DeckTypeOptions = [
- { title: 'tsx (webpack)', value: 'tsx' },
- { title: 'jsx (webpack)', value: 'jsx' },
- { title: 'tsx (vite)', value: 'tsx-vite' },
- { title: 'jsx (vite)', value: 'jsx-vite' },
- { title: 'One Page', value: 'onepage' }
+ { title: 'One Page', value: 'onepage' },
+ { title: 'Markdown', value: 'md' },
+ { title: 'React using Vite', value: 'tsx-vite' },
+ { title: 'React using webpack', value: 'tsx' }
];
-let progressInterval: NodeJS.Timer;
+let progressInterval: NodeJS.Timeout;
const log = console.log;
const printConsoleError = (message: string) =>
chalk.whiteBright.bgRed.bold(' ! ') + chalk.red.bold(' ' + message + '\n');
@@ -176,17 +174,20 @@ const main = async () => {
name,
lang,
port,
- enableTypeScriptSupport: /^tsx/.test(type),
isVite: /vite$/.test(type),
- spectacleVersion: devDependencies.spectacle
+ spectacleVersion: spectaclePackage.version
};
switch (type) {
- case 'jsx':
+ case 'md':
+ await writeWebpackProjectFiles({
+ ...fileOptions,
+ useMarkdownSlides: true
+ });
+ break;
case 'tsx':
await writeWebpackProjectFiles(fileOptions);
break;
- case 'jsx-vite':
case 'tsx-vite':
await writeViteProjectFiles(fileOptions);
break;
diff --git a/packages/create-spectacle/src/generators/one-page.ts b/packages/create-spectacle/src/generators/one-page.ts
new file mode 100644
index 000000000..a821afcbc
--- /dev/null
+++ b/packages/create-spectacle/src/generators/one-page.ts
@@ -0,0 +1,61 @@
+import { onePageTemplate } from '../templates/one-page';
+
+const spectaclePackage = require(`${__dirname}/../../spectacle-package.json`);
+const REACT_VERSION = spectaclePackage.devDependencies.react.replace('^', '');
+const ESM_SH_VERSION = 'v121';
+const DEVELOPMENT_BUILDS = false; // Enable this to use react.development.mjs etc...
+
+export const generateImportMap = () => {
+ const importMap = new Map();
+
+ importMap.set('spectacle', importUrl('spectacle', '10', '?bundle'));
+ importMap.set('react', importUrl('react', REACT_VERSION));
+ importMap.set(
+ 'react/jsx-runtime',
+ importUrl('react', REACT_VERSION, '/jsx-runtime')
+ );
+ importMap.set(
+ 'react-dom/client',
+ importUrl('react-dom', REACT_VERSION, '/client')
+ );
+ importMap.set('htm', importUrl('htm', '^3'));
+
+ const dependencies = spectaclePackage.dependencies as Record;
+ for (const [pkg, version] of Object.entries(dependencies)) {
+ if (importMap.has(pkg)) continue;
+ importMap.set(pkg, importUrl(pkg, version));
+ handlePackageExceptions(pkg, version, importMap);
+ }
+
+ return importMap;
+};
+
+export const createOnePage = (name: string, lang: string) => {
+ const importMap = generateImportMap();
+ return onePageTemplate({ importMap, name, lang });
+};
+
+const importUrl = (pkg: string, version: string, path = '') => {
+ let params = `?deps=react@${REACT_VERSION},react-dom@${REACT_VERSION}`;
+ if (DEVELOPMENT_BUILDS) params += '&dev';
+ if (path.includes('?')) params = params.replace('?', '&');
+
+ return `https://esm.sh/${ESM_SH_VERSION}/${pkg}@${version}${path}${params}`;
+};
+
+const handlePackageExceptions = (
+ pkg: string,
+ version: string,
+ importMap: Map
+) => {
+ if (pkg === 'react-syntax-highlighter') {
+ importMap.set(
+ `${pkg}/dist/cjs/styles/prism/vs-dark.js`,
+ importUrl(pkg, version, '/dist/esm/styles/prism/vs-dark.js')
+ );
+ importMap.set(
+ `${pkg}/dist/cjs/styles/prism/index.js`,
+ importUrl(pkg, version, '/dist/esm/styles/prism/index.js')
+ );
+ }
+};
diff --git a/packages/create-spectacle/src/templates/babel.ts b/packages/create-spectacle/src/templates/babel.ts
index 3ef7a2921..5955c33ec 100644
--- a/packages/create-spectacle/src/templates/babel.ts
+++ b/packages/create-spectacle/src/templates/babel.ts
@@ -1,11 +1,7 @@
-type BabelTemplateOptions = {
- enableTypeScriptSupport: boolean;
-};
-
-export const babelTemplate = (options: BabelTemplateOptions) =>
+export const babelTemplate = () =>
`{
"presets": [
- ${options.enableTypeScriptSupport ? '"@babel/preset-typescript",' : ''}
+ "@babel/preset-typescript",
["@babel/preset-env", { "modules": false }],
["@babel/preset-react", { "runtime": "automatic" }]
],
diff --git a/packages/create-spectacle/src/templates/file-writers.ts b/packages/create-spectacle/src/templates/file-writers.ts
index 1114d84c3..fef38a32b 100644
--- a/packages/create-spectacle/src/templates/file-writers.ts
+++ b/packages/create-spectacle/src/templates/file-writers.ts
@@ -1,7 +1,6 @@
import path from 'path';
import { mkdir, writeFile, rm } from 'fs/promises';
import { htmlTemplate } from './html';
-import { onePageTemplate } from './one-page';
import { webpackTemplate } from './webpack';
import { babelTemplate } from './babel';
import { packageTemplate, vitePackageTemplate } from './package';
@@ -10,20 +9,21 @@ import { tsconfigTemplate } from './tsconfig';
import { gitignoreTemplate } from './gitignore';
import { readmeTemplate } from './readme';
import { viteConfigTemplate } from './viteConfig';
+import { createOnePage } from '../generators/one-page';
+import { markdownCustomTypesTemplate, markdownTemplate } from './markdown';
export type FileOptions = {
snakeCaseName: string;
name: string;
lang: string;
port: number;
- enableTypeScriptSupport: boolean;
+ useMarkdownSlides?: boolean;
spectacleVersion: string;
isVite: boolean;
};
const prepForProjectWrite = async (fileOptions: FileOptions) => {
- const { name, lang, snakeCaseName, enableTypeScriptSupport, isVite } =
- fileOptions;
+ const { name, lang, snakeCaseName, isVite } = fileOptions;
const outPath = path.resolve(process.cwd(), snakeCaseName);
const pathFor = (file: string) => path.join(outPath, file);
@@ -38,25 +38,24 @@ const prepForProjectWrite = async (fileOptions: FileOptions) => {
htmlTemplate({
name,
lang,
- entryFile: isVite
- ? `/index.${enableTypeScriptSupport ? 'tsx' : 'jsx'}`
- : undefined
+ entryFile: isVite ? '/index.tsx' : undefined
})
);
await writeFile(
- pathFor(`index.${enableTypeScriptSupport ? 'tsx' : 'jsx'}`),
+ pathFor('index.tsx'),
indexTemplate({
- usesTypeScript: enableTypeScriptSupport,
- name
+ name,
+ usesMarkdown: fileOptions.useMarkdownSlides
})
);
await writeFile(pathFor('.gitignore'), gitignoreTemplate());
- await writeFile(
- pathFor('README.md'),
- readmeTemplate({ name, enableTypeScriptSupport, isVite })
- );
- enableTypeScriptSupport &&
- (await writeFile(pathFor('tsconfig.json'), tsconfigTemplate()));
+ await writeFile(pathFor('README.md'), readmeTemplate({ name, isVite }));
+ await writeFile(pathFor('tsconfig.json'), tsconfigTemplate());
+ fileOptions.useMarkdownSlides &&
+ (await Promise.all([
+ writeFile(pathFor('custom.d.ts'), markdownCustomTypesTemplate()),
+ writeFile(pathFor('slides.md'), markdownTemplate({ name }))
+ ]));
return { outPath, pathFor };
};
@@ -65,22 +64,14 @@ const prepForProjectWrite = async (fileOptions: FileOptions) => {
* Generate a webpack-based project
*/
export const writeWebpackProjectFiles = async (options: FileOptions) => {
- const { port, enableTypeScriptSupport, snakeCaseName, spectacleVersion } =
- options;
+ const { port, snakeCaseName, spectacleVersion } = options;
const { pathFor } = await prepForProjectWrite(options);
- await writeFile(
- pathFor('webpack.config.js'),
- webpackTemplate({ port, usesTypeScript: enableTypeScriptSupport })
- );
- await writeFile(
- pathFor('.babelrc'),
- babelTemplate({ enableTypeScriptSupport })
- );
+ await writeFile(pathFor('webpack.config.js'), webpackTemplate({ port }));
+ await writeFile(pathFor('.babelrc'), babelTemplate());
await writeFile(
pathFor('package.json'),
packageTemplate({
- usesTypeScript: enableTypeScriptSupport,
name: snakeCaseName,
spectacleVersion
})
@@ -91,24 +82,19 @@ export const writeWebpackProjectFiles = async (options: FileOptions) => {
* Generate a vite-based project
*/
export const writeViteProjectFiles = async (options: FileOptions) => {
- const { enableTypeScriptSupport, snakeCaseName, spectacleVersion, port } =
- options;
+ const { snakeCaseName, spectacleVersion, port } = options;
const { pathFor } = await prepForProjectWrite(options);
await writeFile(
pathFor('package.json'),
vitePackageTemplate({
- usesTypeScript: enableTypeScriptSupport,
name: snakeCaseName,
spectacleVersion,
port
})
);
- await writeFile(
- pathFor(`vite.config.${enableTypeScriptSupport ? 'ts' : 'js'}`),
- viteConfigTemplate()
- );
+ await writeFile(pathFor(`vite.config.ts`), viteConfigTemplate());
};
/**
@@ -121,6 +107,6 @@ export const writeOnePageHTMLFile = async ({
}: FileOptions) => {
await writeFile(
path.resolve(process.cwd(), `${snakeCaseName}.html`),
- onePageTemplate({ name, lang })
+ createOnePage(name, lang)
);
};
diff --git a/packages/create-spectacle/src/templates/index.ts b/packages/create-spectacle/src/templates/index.ts
index edd6284e5..54016333e 100644
--- a/packages/create-spectacle/src/templates/index.ts
+++ b/packages/create-spectacle/src/templates/index.ts
@@ -1,34 +1,31 @@
type IndexTemplateOptions = {
name: string;
- usesTypeScript: boolean;
+ usesMarkdown: boolean;
};
-export const indexTemplate = (options: IndexTemplateOptions) =>
- `import React from 'react';
+const content = {
+ reactImports() {
+ return `
+import React from 'react';
import { createRoot } from 'react-dom/client';
-import { Slide, Deck, FlexBox, Heading, SpectacleLogo, Box, FullScreen, AnimatedProgress } from 'spectacle';
+import { Deck, DefaultTemplate, Slide, FlexBox, Heading, SpectacleLogo } from 'spectacle'
+ `;
+ },
-const template = () => (
-
-
-
-
-
-
-
-
-);
+ mdImports() {
+ return `
+import React from 'react';
+import { createRoot } from 'react-dom/client';
+import { Deck, DefaultTemplate, MarkdownSlideSet } from 'spectacle';
+import mdContent from './slides.md';
+ `;
+ },
-const Presentation = () => (
-
+ reactBody(name: string) {
+ return `
- ${options.name}
+ ${name}
@@ -37,10 +34,39 @@ const Presentation = () => (
+ `
+ .substring(1)
+ .trim();
+ },
+
+ mdBody() {
+ return `
+ {mdContent}
+ `
+ .substring(1)
+ .trim();
+ }
+};
+
+export const indexTemplate = (options: IndexTemplateOptions) =>
+ `
+${(() => {
+ if (options.usesMarkdown) {
+ return content.mdImports();
+ }
+ return content.reactImports();
+})().trim()}
+
+const Presentation = () => (
+ }>
+ ${(() => {
+ if (options.usesMarkdown) {
+ return content.mdBody();
+ }
+ return content.reactBody(options.name);
+ })()}
);
-createRoot(document.getElementById('app')${
- options.usesTypeScript ? '!' : ''
- }).render();
-`;
+createRoot(document.getElementById('app')!).render();
+`.trim();
diff --git a/packages/create-spectacle/src/templates/markdown.ts b/packages/create-spectacle/src/templates/markdown.ts
new file mode 100644
index 000000000..6b0912900
--- /dev/null
+++ b/packages/create-spectacle/src/templates/markdown.ts
@@ -0,0 +1,20 @@
+type TemplateOptions = {
+ name: string;
+};
+
+export const markdownTemplate = (options: TemplateOptions) =>
+ `
+--- { "layout" : "center" }
+# ${options.name}
+
+---
+- Made with Spectacle
+`.trim();
+
+export const markdownCustomTypesTemplate = () =>
+ `
+declare module '*.md' {
+ const content: string;
+ export default content;
+}
+`.trim();
diff --git a/packages/create-spectacle/src/templates/one-page.ts b/packages/create-spectacle/src/templates/one-page.ts
index 2f04aeb83..98341670b 100644
--- a/packages/create-spectacle/src/templates/one-page.ts
+++ b/packages/create-spectacle/src/templates/one-page.ts
@@ -1,9 +1,14 @@
type OnePageTemplateOptions = {
name: string;
lang: string;
+ importMap: Map;
};
-export const onePageTemplate = ({ name, lang }: OnePageTemplateOptions) => `
+export const onePageTemplate = ({
+ name,
+ lang,
+ importMap
+}: OnePageTemplateOptions) => `
@@ -15,26 +20,34 @@ export const onePageTemplate = ({ name, lang }: OnePageTemplateOptions) => `
-
-
-
-
-
+