@marimo-team/islands 0.23.10-dev3 → 0.23.10-dev31
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{ConnectedDataExplorerComponent-CyV83R2m.js → ConnectedDataExplorerComponent-DmBropAy.js} +31 -31
- package/dist/{ErrorBoundary-rULOrC_p.js → ErrorBoundary-DpbaKVv7.js} +1 -1
- package/dist/{any-language-editor-DfdpyDv_.js → any-language-editor-DNmoSiWL.js} +20 -20
- package/dist/assets/__vite-browser-external-eshhtsgZ.js +1 -0
- package/dist/assets/worker-CC0Oul9k.js +73 -0
- package/dist/{chat-ui-C1tL1pML.js → chat-ui-D6oraHT2.js} +76 -76
- package/dist/{check-DTbrK0zt.js → check-BCaJeT-J.js} +1 -1
- package/dist/{code-visibility-DfnO0DcH.js → code-visibility-B9qGgair.js} +2166 -1292
- package/dist/{copy-BuQpJEzp.js → copy-UqRYxiOg.js} +33 -33
- package/dist/dist-7QfXoMdB.js +5 -0
- package/dist/{dist-DgnE8F-r.js → dist-A2846XWO.js} +1 -1
- package/dist/dist-BEXXyZig.js +5 -0
- package/dist/{dist-B3pZ0Ab6.js → dist-BR_gyG9L.js} +3 -3
- package/dist/{dist-CcXxepx6.js → dist-BSAt6RhH.js} +27 -27
- package/dist/{dist-Bde4a2kU.js → dist-BY018Paw.js} +8 -8
- package/dist/dist-BYj57OV4.js +5 -0
- package/dist/{dist-CUCNs1ja.js → dist-BaoDKvdy.js} +2 -2
- package/dist/{dist-Cy1WxgBD.js → dist-Bf7SHuNp.js} +5 -5
- package/dist/{dist-Bz_sYWbr.js → dist-Bk75fBZA.js} +2 -2
- package/dist/dist-BlSvQzNr.js +5 -0
- package/dist/{dist-C5VC_yzu.js → dist-BzEzfugY.js} +1 -1
- package/dist/dist-CCBlxAgS.js +8 -0
- package/dist/dist-CIDTVIUf.js +5 -0
- package/dist/{dist-CLUtPrdy.js → dist-CIYBwstr.js} +1 -1
- package/dist/{dist-BotSqB48.js → dist-C_Y3oV3C.js} +12 -12
- package/dist/{dist-BTfv03uy.js → dist-CcWX6tmx.js} +2 -2
- package/dist/{dist-BhM8gdSO.js → dist-CoXAujgg.js} +4 -4
- package/dist/{dist-4j4c7bjm.js → dist-CpxNdDkw.js} +3 -3
- package/dist/dist-CqQyhAM8.js +8 -0
- package/dist/dist-CwRu2Xzh.js +5 -0
- package/dist/{dist-BcuoonNH.js → dist-CxJDU6Bh.js} +9 -9
- package/dist/{dist-DxvORzUR.js → dist-D-W5ny5a.js} +8 -8
- package/dist/dist-D8CDTVgf.js +6 -0
- package/dist/dist-D8DNB0nO.js +8 -0
- package/dist/dist-DL6N_q-A.js +5 -0
- package/dist/{dist-BbbIBDiQ.js → dist-DMjWuVs8.js} +1 -1
- package/dist/dist-DOFbNV_b.js +8 -0
- package/dist/dist-DPrYzMY0.js +6 -0
- package/dist/{dist-h2c8sZvT.js → dist-DZORgqKY.js} +1 -1
- package/dist/{dist-B3P2fFpz.js → dist-DZo4nSS0.js} +14 -14
- package/dist/{dist-D4CewLk6.js → dist-Dax--nl9.js} +1 -1
- package/dist/{dist-DRfcqpxJ.js → dist-DgGbNavJ.js} +2 -2
- package/dist/{dist-C1BYNeCR.js → dist-Dk6PV_d3.js} +10 -10
- package/dist/{dist-fQ0ViXGs.js → dist-Dv_Y15yk.js} +107 -107
- package/dist/{dist-Bfwsv11D.js → dist-DyyjKEYf.js} +2 -2
- package/dist/{dist-p2qyWijU.js → dist-GZXUmt0b.js} +2 -2
- package/dist/{dist-CLJWPTX2.js → dist-LTU8Hdvn.js} +3 -3
- package/dist/{dist-DqAWR3CS.js → dist-M9Vag9Y0.js} +20 -20
- package/dist/{dist-DNdhYsgW.js → dist-U4F-tbMs.js} +79 -62
- package/dist/{dist-RqXTaiir.js → dist-abid3KgM.js} +11 -11
- package/dist/dist-cdmMjgsn.js +5 -0
- package/dist/dist-hT4QzYX-.js +1247 -0
- package/dist/{dist-luvabDEB.js → dist-t9Kf7xqC.js} +2 -2
- package/dist/{error-banner-5bz0L9hS.js → error-banner-Cc0I3C9e.js} +1 -1
- package/dist/esm-BaH2eg5-.js +1171 -0
- package/dist/{esm-Duie8iU-.js → esm-ga2Bf3O2.js} +43 -43
- package/dist/{extends-BgdxCfYu.js → extends-D_hDsj6R.js} +4 -4
- package/dist/{formats-DHxc-FdY.js → formats-C4wO47tk.js} +1 -1
- package/dist/{glide-data-editor-BOmK9ETQ.js → glide-data-editor-Qhu8oCX-.js} +12 -12
- package/dist/{html-to-image-CNa5ok96.js → html-to-image-UEH5lFDZ.js} +2318 -2275
- package/dist/{input-_2sjvfne.js → input-CMYy4hzj.js} +187 -185
- package/dist/{label-LWtdw5i8.js → label-CC4ytI1X.js} +1 -1
- package/dist/main.js +6941 -6913
- package/dist/{mermaid-lXOw5Py9.js → mermaid-zuLgJ8J8.js} +4 -4
- package/dist/{process-output-DKr4f1di.js → process-output-CyMLTogj.js} +3 -3
- package/dist/{reveal-component-UdMnCK5U.js → reveal-component-Co2AuBAx.js} +697 -619
- package/dist/{spec-B96zNUEA.js → spec-X7FwLJni.js} +4 -4
- package/dist/{strings-Bu3vlb6W.js → strings-J57tzLr3.js} +47 -46
- package/dist/style.css +1 -1
- package/dist/{toDate-x-WRDCH7.js → toDate-d8RCRrRd.js} +2 -2
- package/dist/{tooltip-C5FYOpQc.js → tooltip-DpcyNkQ2.js} +2 -2
- package/dist/{types-CVvp1fKr.js → types-ChtMFmZ2.js} +1 -1
- package/dist/{useAsyncData-iRgKDT5s.js → useAsyncData-PonK__yh.js} +1 -1
- package/dist/{useDateFormatter-BA4FCquG.js → useDateFormatter-QB-3MpYr.js} +2 -2
- package/dist/{useDeepCompareMemoize-CkQ57VS2.js → useDeepCompareMemoize-D3NGWke6.js} +1 -1
- package/dist/{useLifecycle-BBO9PIph.js → useLifecycle-00mO3OSS.js} +2 -2
- package/dist/{useTheme-DHIrRQOe.js → useTheme-DEhDzATN.js} +1 -1
- package/dist/{vega-component-Dq-SH463.js → vega-component-9h1ACS78.js} +8 -8
- package/dist/{zod-CoBiJ5v4.js → zod-aLSua2NL.js} +24 -23
- package/package.json +3 -3
- package/src/components/data-table/TableBottomBar.tsx +1 -15
- package/src/components/data-table/TableTopBar.tsx +8 -13
- package/src/components/data-table/__tests__/TableBottomBar.test.tsx +6 -12
- package/src/components/data-table/__tests__/column-visibility-dropdown.test.tsx +227 -0
- package/src/components/data-table/__tests__/data-table.test.tsx +154 -12
- package/src/components/data-table/column-visibility-dropdown.tsx +204 -0
- package/src/components/data-table/data-table.tsx +1 -1
- package/src/components/data-table/filter-by-values-picker.tsx +39 -17
- package/src/components/data-table/filter-pills.tsx +1 -1
- package/src/components/data-table/hover-tooltip/__tests__/content.test.ts +60 -0
- package/src/components/data-table/hover-tooltip/content.ts +44 -0
- package/src/components/data-table/hover-tooltip/hover-tooltip.tsx +55 -0
- package/src/components/data-table/hover-tooltip/use-table-hover-tooltip.ts +159 -0
- package/src/components/data-table/renderers.tsx +27 -43
- package/src/components/datasources/__tests__/filter-empty.test.ts +183 -0
- package/src/components/datasources/datasources.tsx +92 -3
- package/src/components/editor/cell/cell-context-menu.tsx +15 -2
- package/src/components/editor/cell/code/language-toggle.tsx +7 -1
- package/src/components/editor/chrome/wrapper/app-chrome.tsx +97 -52
- package/src/components/editor/chrome/wrapper/lazy-panels.ts +91 -0
- package/src/components/editor/chrome/wrapper/sidebar.tsx +2 -0
- package/src/components/editor/documentation.css +35 -0
- package/src/components/editor/file-tree/file-explorer.tsx +8 -18
- package/src/components/editor/file-tree/tree-actions.tsx +46 -1
- package/src/components/editor/renderers/slides-layout/__tests__/plugin.test.ts +20 -0
- package/src/components/editor/renderers/slides-layout/types.ts +1 -0
- package/src/components/slides/__tests__/minimap-actions.test.tsx +166 -0
- package/src/components/slides/__tests__/reveal-component.test.ts +425 -0
- package/src/components/slides/minimap.tsx +127 -10
- package/src/components/slides/reveal-component.tsx +287 -61
- package/src/components/slides/slide-cell-view.tsx +26 -2
- package/src/components/slides/slide-form.tsx +26 -4
- package/src/components/storage/__tests__/storage-inspector.test.ts +53 -0
- package/src/components/storage/storage-inspector.tsx +68 -48
- package/src/components/ui/__tests__/use-toast.test.ts +75 -0
- package/src/components/ui/combobox.tsx +51 -32
- package/src/components/ui/reorderable-list.tsx +13 -0
- package/src/components/ui/select-core/__tests__/use-select-list.test.ts +294 -0
- package/src/components/ui/select-core/__tests__/utils.test.ts +222 -0
- package/src/components/ui/select-core/index.ts +16 -0
- package/src/components/ui/select-core/option-row.tsx +33 -0
- package/src/components/ui/select-core/render-slot.ts +20 -0
- package/src/components/ui/select-core/select-list.tsx +248 -0
- package/src/components/ui/select-core/types.ts +44 -0
- package/src/components/ui/select-core/use-select-list.ts +347 -0
- package/src/components/ui/select-core/utils.ts +121 -0
- package/src/components/ui/use-toast.ts +33 -13
- package/src/core/cells/__tests__/__snapshots__/cells.test.ts.snap +0 -28
- package/src/core/cells/__tests__/cell.test.ts +29 -2
- package/src/core/cells/cell.ts +5 -1
- package/src/core/codemirror/go-to-definition/commands.ts +4 -3
- package/src/core/codemirror/language/languages/python.ts +2 -0
- package/src/core/codemirror/language/languages/sql/utils.ts +3 -1
- package/src/core/codemirror/lsp/__tests__/markdown-renderer.test.ts +41 -0
- package/src/core/codemirror/lsp/markdown-renderer.ts +59 -0
- package/src/core/datasets/data-source-connections.ts +2 -0
- package/src/core/network/__tests__/requests-static.test.ts +30 -0
- package/src/core/network/requests-static.ts +14 -10
- package/src/core/wasm/worker/bootstrap.ts +12 -4
- package/src/plugins/impl/MultiselectPlugin.tsx +19 -142
- package/src/plugins/impl/SearchableSelect.tsx +16 -97
- package/src/plugins/impl/__tests__/DropdownPlugin.test.tsx +5 -2
- package/src/plugins/impl/__tests__/MultiSelectPlugin.test.ts +1 -1
- package/src/plugins/layout/DownloadPlugin.tsx +1 -1
- package/src/utils/lazy.ts +6 -1
- package/dist/assets/__vite-browser-external-Ci2ZQfXU.js +0 -1
- package/dist/assets/worker-ip3AI_sN.js +0 -73
- package/dist/dist-0Fif7jnk.js +0 -5
- package/dist/dist-B5h_9sHB.js +0 -6
- package/dist/dist-B9M6R5ye.js +0 -5
- package/dist/dist-BCt3tnck.js +0 -8
- package/dist/dist-BUIJwMwn.js +0 -8
- package/dist/dist-BpquMd3k.js +0 -5
- package/dist/dist-BzJsqYfz.js +0 -5
- package/dist/dist-CA5ELXAf.js +0 -6
- package/dist/dist-CLBRs6Uv.js +0 -5
- package/dist/dist-CStVCMbq.js +0 -5
- package/dist/dist-CZRIEY3Y.js +0 -8
- package/dist/dist-CuUHbFD0.js +0 -5
- package/dist/dist-DV7Iabxb.js +0 -8
- package/dist/dist-DhHh0jLg.js +0 -1247
- package/dist/dist-DuEeHMvL.js +0 -5
- package/dist/esm-BfhQmZjp.js +0 -1171
- package/src/plugins/impl/multiselectFilterFn.tsx +0 -22
- /package/src/components/{data-table → ui}/value-chips.tsx +0 -0
|
@@ -14,6 +14,7 @@ import { Deck, Fragment, Slide, Stack } from "@revealjs/react";
|
|
|
14
14
|
import { Panel, PanelGroup, PanelResizeHandle } from "react-resizable-panels";
|
|
15
15
|
import { Slide as CellOutputSlide } from "@/components/slides/slide";
|
|
16
16
|
import { Button } from "@/components/ui/button";
|
|
17
|
+
import { useFullScreenElement } from "@/components/ui/fullscreen";
|
|
17
18
|
import { Tooltip } from "@/components/ui/tooltip";
|
|
18
19
|
import type { CellId } from "@/core/cells/ids";
|
|
19
20
|
import type { RuntimeCell } from "@/core/cells/types";
|
|
@@ -26,6 +27,7 @@ import "./reveal-slides.css";
|
|
|
26
27
|
import type {
|
|
27
28
|
SlideConfig,
|
|
28
29
|
SlidesLayout,
|
|
30
|
+
SlideType,
|
|
29
31
|
} from "../editor/renderers/slides-layout/types";
|
|
30
32
|
import {
|
|
31
33
|
buildSlideIndices,
|
|
@@ -155,14 +157,148 @@ const NotesAside = ({ text }: { text: string }) => {
|
|
|
155
157
|
);
|
|
156
158
|
};
|
|
157
159
|
|
|
160
|
+
/**
|
|
161
|
+
* Resolve whether a slide cell shows its source instead of its output.
|
|
162
|
+
*
|
|
163
|
+
* Code is shown when either the cell's persisted `showCode` config is set or
|
|
164
|
+
* the keyboard toggle `C` override is active for it (logical OR).
|
|
165
|
+
*/
|
|
166
|
+
export function shouldShowCode(options: {
|
|
167
|
+
cells: ReadonlyMap<CellId, SlideConfig>;
|
|
168
|
+
cellId: CellId | undefined;
|
|
169
|
+
showCodeOverrides: ReadonlySet<CellId>;
|
|
170
|
+
codeToggleEnabled: boolean;
|
|
171
|
+
}): boolean {
|
|
172
|
+
const { cells, cellId, showCodeOverrides, codeToggleEnabled } = options;
|
|
173
|
+
if (cellId == null || !codeToggleEnabled) {
|
|
174
|
+
return false;
|
|
175
|
+
}
|
|
176
|
+
const configured = cells.get(cellId)?.showCode ?? false;
|
|
177
|
+
return configured || showCodeOverrides.has(cellId);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* The slide type a cell takes *in the composed deck*. Cells without output and
|
|
182
|
+
* the cell currently held in the parked edit overlay are dropped (`"skip"`) so
|
|
183
|
+
* they aren't mounted a second time in the deck — the overlay renders them
|
|
184
|
+
* instead. Everything else uses its configured type, defaulting to a slide.
|
|
185
|
+
*/
|
|
186
|
+
export function deckSlideType(options: {
|
|
187
|
+
cell: RuntimeCell;
|
|
188
|
+
noOutputIds: ReadonlySet<CellId>;
|
|
189
|
+
heldEditCellId: CellId | null;
|
|
190
|
+
slideConfigs: ReadonlyMap<CellId, SlideConfig>;
|
|
191
|
+
}): SlideType {
|
|
192
|
+
const { cell, noOutputIds, heldEditCellId, slideConfigs } = options;
|
|
193
|
+
if (noOutputIds.has(cell.id) || cell.id === heldEditCellId) {
|
|
194
|
+
return "skip";
|
|
195
|
+
}
|
|
196
|
+
return slideConfigs.get(cell.id)?.type ?? DEFAULT_SLIDE_TYPE;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Tracks the cell pinned in the parked overlay (rendered over the deck for
|
|
201
|
+
* skipped / output-less cells, and during in-progress edits).
|
|
202
|
+
*
|
|
203
|
+
* A brand-new (or output-less) cell is edited in the parked overlay, which
|
|
204
|
+
* lives outside reveal's slide DOM. The moment it first produces output it
|
|
205
|
+
* would normally jump to its composed slide — a different React subtree that
|
|
206
|
+
* reveal also re-syncs/transitions — tearing down the editor and dropping
|
|
207
|
+
* focus mid-edit (e.g. typing in a new markdown cell). To avoid that we *hold*
|
|
208
|
+
* the cell in the overlay even after it gains output, and only let it settle
|
|
209
|
+
* into the deck once the user navigates to a different cell.
|
|
210
|
+
*
|
|
211
|
+
* The hold is keyed off the active cell rather than DOM focus on purpose: the
|
|
212
|
+
* slide editor doesn't participate in the global cell-focus state, and the
|
|
213
|
+
* active cell only changes when the user moves in the minimap — exactly when
|
|
214
|
+
* we want to release the hold.
|
|
215
|
+
*
|
|
216
|
+
* Returns:
|
|
217
|
+
* - `parkedPreviewCell`: the cell to render in the overlay
|
|
218
|
+
* - `isHeldEdit`: whether the cell is held in the overlay
|
|
219
|
+
* - `isNoOutputPreview`: whether the cell is output-less
|
|
220
|
+
* - `heldEditCellId`: the id of the cell that is held in the overlay
|
|
221
|
+
*/
|
|
222
|
+
export function useParkedPreview(options: {
|
|
223
|
+
activeCell: RuntimeCell | undefined;
|
|
224
|
+
slideConfigs: ReadonlyMap<CellId, SlideConfig>;
|
|
225
|
+
noOutputIds: ReadonlySet<CellId>;
|
|
226
|
+
}): {
|
|
227
|
+
parkedPreviewCell: RuntimeCell | null;
|
|
228
|
+
isHeldEdit: boolean;
|
|
229
|
+
isNoOutputPreview: boolean;
|
|
230
|
+
heldEditCellId: CellId | null;
|
|
231
|
+
heldShowsCode: boolean;
|
|
232
|
+
toggleHeldShowsCode: () => void;
|
|
233
|
+
} {
|
|
234
|
+
const { activeCell, slideConfigs, noOutputIds } = options;
|
|
235
|
+
const activeCellId = activeCell?.id ?? null;
|
|
236
|
+
const isNoOutputPreview =
|
|
237
|
+
activeCell != null && noOutputIds.has(activeCell.id);
|
|
238
|
+
const isSkippedPreview =
|
|
239
|
+
activeCell != null && slideConfigs.get(activeCell.id)?.type === "skip";
|
|
240
|
+
// Genuinely parked: skipped in the deck, or no output to compose yet.
|
|
241
|
+
const baseParked = isSkippedPreview || isNoOutputPreview;
|
|
242
|
+
|
|
243
|
+
// The cell pinned in the overlay, tracked alongside the active cell it was
|
|
244
|
+
// armed against so we can release it exactly when the active cell changes.
|
|
245
|
+
const [held, setHeld] = useState<{
|
|
246
|
+
activeCellId: CellId | null;
|
|
247
|
+
cellId: CellId | null;
|
|
248
|
+
}>({ activeCellId, cellId: null });
|
|
249
|
+
|
|
250
|
+
let heldCellId = held.cellId;
|
|
251
|
+
if (held.activeCellId !== activeCellId) {
|
|
252
|
+
// Active cell changed: drop any prior hold, arming a fresh one only while
|
|
253
|
+
// the new cell has no output yet (skipped cells park via `baseParked`).
|
|
254
|
+
heldCellId = isNoOutputPreview ? activeCellId : null;
|
|
255
|
+
setHeld({ activeCellId, cellId: heldCellId });
|
|
256
|
+
} else if (isNoOutputPreview && heldCellId !== activeCellId) {
|
|
257
|
+
// Same active cell, still output-less: (re)arm the hold.
|
|
258
|
+
heldCellId = activeCellId;
|
|
259
|
+
setHeld({ activeCellId, cellId: heldCellId });
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
const isHeldEdit =
|
|
263
|
+
!baseParked && activeCellId != null && heldCellId === activeCellId;
|
|
264
|
+
// Keep the held cell out of the composed deck so its editor isn't mounted a
|
|
265
|
+
// second time (the overlay already renders it); it rejoins once released.
|
|
266
|
+
const heldEditCellId = isHeldEdit ? heldCellId : null;
|
|
267
|
+
|
|
268
|
+
// Code visibility for the held overlay. Defaults to showing the editor so it
|
|
269
|
+
// survives the no-output -> output transition mid-edit; the `C` toggle can
|
|
270
|
+
// hide it on demand.
|
|
271
|
+
const [heldShow, setHeldShow] = useState<{
|
|
272
|
+
cellId: CellId | null;
|
|
273
|
+
show: boolean;
|
|
274
|
+
}>({ cellId: heldEditCellId, show: true });
|
|
275
|
+
let heldShowsCode = heldShow.show;
|
|
276
|
+
if (heldShow.cellId !== heldEditCellId) {
|
|
277
|
+
heldShowsCode = true;
|
|
278
|
+
setHeldShow({ cellId: heldEditCellId, show: true });
|
|
279
|
+
}
|
|
280
|
+
const toggleHeldShowsCode = useEvent(() =>
|
|
281
|
+
setHeldShow((prev) => ({ ...prev, show: !prev.show })),
|
|
282
|
+
);
|
|
283
|
+
|
|
284
|
+
return {
|
|
285
|
+
parkedPreviewCell: baseParked || isHeldEdit ? (activeCell ?? null) : null,
|
|
286
|
+
isHeldEdit,
|
|
287
|
+
isNoOutputPreview,
|
|
288
|
+
heldEditCellId,
|
|
289
|
+
heldShowsCode,
|
|
290
|
+
toggleHeldShowsCode,
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
|
|
158
294
|
const SubslideView = ({
|
|
159
295
|
subslide,
|
|
160
|
-
|
|
296
|
+
resolveShowCode,
|
|
161
297
|
isEditable,
|
|
162
298
|
slideConfigs,
|
|
163
299
|
}: {
|
|
164
300
|
subslide: ComposedSubslide<RuntimeCell>;
|
|
165
|
-
|
|
301
|
+
resolveShowCode: (cellId: CellId) => boolean;
|
|
166
302
|
isEditable: boolean;
|
|
167
303
|
slideConfigs: ReadonlyMap<CellId, SlideConfig>;
|
|
168
304
|
}) => {
|
|
@@ -171,12 +307,16 @@ const SubslideView = ({
|
|
|
171
307
|
slideConfigs,
|
|
172
308
|
);
|
|
173
309
|
|
|
310
|
+
const anyCodeShown = subslide.blocks.some((block) =>
|
|
311
|
+
block.cells.some((cell) => resolveShowCode(cell.id)),
|
|
312
|
+
);
|
|
313
|
+
|
|
174
314
|
return (
|
|
175
315
|
<Slide>
|
|
176
316
|
<div className="h-full w-full overflow-auto flex">
|
|
177
317
|
<div
|
|
178
318
|
className={
|
|
179
|
-
|
|
319
|
+
anyCodeShown
|
|
180
320
|
? "mo-slide-content flex flex-col gap-3"
|
|
181
321
|
: "mo-slide-content"
|
|
182
322
|
}
|
|
@@ -186,7 +326,7 @@ const SubslideView = ({
|
|
|
186
326
|
>
|
|
187
327
|
{subslide.blocks.map((block, i) => {
|
|
188
328
|
const rendered = block.cells.map((cell) => {
|
|
189
|
-
if (!
|
|
329
|
+
if (!resolveShowCode(cell.id)) {
|
|
190
330
|
return (
|
|
191
331
|
<CellOutputSlide
|
|
192
332
|
key={cell.id}
|
|
@@ -221,22 +361,36 @@ const SubslideView = ({
|
|
|
221
361
|
);
|
|
222
362
|
};
|
|
223
363
|
|
|
364
|
+
/**
|
|
365
|
+
* Whether the parked overlay renders the cell's *source* instead of its output.
|
|
366
|
+
*/
|
|
367
|
+
export function parkedRendersSource(options: {
|
|
368
|
+
isNoOutputPreview: boolean;
|
|
369
|
+
isEditable: boolean;
|
|
370
|
+
showCode: boolean;
|
|
371
|
+
}): boolean {
|
|
372
|
+
const { isNoOutputPreview, isEditable, showCode } = options;
|
|
373
|
+
return isNoOutputPreview ? isEditable || showCode : showCode;
|
|
374
|
+
}
|
|
375
|
+
|
|
224
376
|
const ParkedPreviewContent = ({
|
|
225
377
|
cell,
|
|
226
378
|
isNoOutputPreview,
|
|
227
379
|
isEditable,
|
|
228
|
-
|
|
380
|
+
showCode,
|
|
229
381
|
}: {
|
|
230
382
|
cell: RuntimeCell;
|
|
231
383
|
isNoOutputPreview: boolean;
|
|
232
384
|
isEditable: boolean;
|
|
233
|
-
|
|
385
|
+
showCode: boolean;
|
|
234
386
|
}) => {
|
|
235
|
-
if (isNoOutputPreview
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
387
|
+
if (parkedRendersSource({ isNoOutputPreview, isEditable, showCode })) {
|
|
388
|
+
// Editable cells get the live editor; otherwise a read-only source view.
|
|
389
|
+
return isEditable ? (
|
|
390
|
+
<SlideCellView cell={cell} />
|
|
391
|
+
) : (
|
|
392
|
+
<SlideCellReadOnlyView cell={cell} />
|
|
393
|
+
);
|
|
240
394
|
}
|
|
241
395
|
return (
|
|
242
396
|
<CellOutputSlide
|
|
@@ -274,6 +428,8 @@ const RevealSlidesComponent = ({
|
|
|
274
428
|
const containerRef = useRef<HTMLDivElement>(null);
|
|
275
429
|
const deckRef = useRef<RevealApi | null>(null);
|
|
276
430
|
const { width, height } = useSlideDimensions(containerRef);
|
|
431
|
+
const isFullscreen = useFullScreenElement() != null;
|
|
432
|
+
|
|
277
433
|
// Skip the Notes plugin inside reveal's own speaker-view iframes so pressing
|
|
278
434
|
// `S` there doesn't try to spawn another popup.
|
|
279
435
|
const kioskMode = useAtomValue(kioskModeAtom);
|
|
@@ -282,39 +438,68 @@ const RevealSlidesComponent = ({
|
|
|
282
438
|
[kioskMode],
|
|
283
439
|
);
|
|
284
440
|
|
|
285
|
-
|
|
441
|
+
// Store the state of the code toggle for each cell
|
|
442
|
+
// This acts like a 'peek' at the code.
|
|
443
|
+
const [showCodeOverrides, setShowCodeOverrides] = useState<
|
|
444
|
+
ReadonlySet<CellId>
|
|
445
|
+
>(() => new Set());
|
|
286
446
|
const codeAvailable = useNotebookCodeAvailable(slideCells);
|
|
287
447
|
const codeToggleEnabled = !isIslands() && codeAvailable;
|
|
288
|
-
const codeShown = codeToggleEnabled && showCode;
|
|
289
448
|
|
|
290
449
|
const activeCell = activeIndex != null ? slideCells[activeIndex] : undefined;
|
|
291
450
|
// Fall back to the first cell while the deck settles on an initial slide.
|
|
292
451
|
// Still `undefined` when the deck is empty (handled below).
|
|
293
452
|
const activeConfigCell = activeCell ?? slideCells.at(0);
|
|
294
453
|
|
|
454
|
+
const {
|
|
455
|
+
parkedPreviewCell,
|
|
456
|
+
isHeldEdit,
|
|
457
|
+
isNoOutputPreview,
|
|
458
|
+
heldEditCellId,
|
|
459
|
+
heldShowsCode,
|
|
460
|
+
toggleHeldShowsCode,
|
|
461
|
+
} = useParkedPreview({
|
|
462
|
+
activeCell,
|
|
463
|
+
slideConfigs: layout.cells,
|
|
464
|
+
noOutputIds,
|
|
465
|
+
});
|
|
466
|
+
|
|
467
|
+
const resolveShowCode = (cellId: CellId | undefined): boolean =>
|
|
468
|
+
shouldShowCode({
|
|
469
|
+
cells: layout.cells,
|
|
470
|
+
cellId,
|
|
471
|
+
showCodeOverrides,
|
|
472
|
+
codeToggleEnabled,
|
|
473
|
+
});
|
|
474
|
+
|
|
475
|
+
// `C` and the toolbar button target the active slide's cell (the revealed
|
|
476
|
+
// fragment when stepping through a stack, otherwise the lead cell).
|
|
477
|
+
const cellIdToShowCode = activeCell?.id ?? activeConfigCell?.id;
|
|
478
|
+
const cellShowsCode = isHeldEdit
|
|
479
|
+
? heldShowsCode
|
|
480
|
+
: resolveShowCode(cellIdToShowCode);
|
|
481
|
+
|
|
482
|
+
// A slide persisted with `showCode: true` always renders code
|
|
483
|
+
const codeAlwaysShown =
|
|
484
|
+
codeToggleEnabled &&
|
|
485
|
+
cellIdToShowCode != null &&
|
|
486
|
+
(layout.cells.get(cellIdToShowCode)?.showCode ?? false);
|
|
487
|
+
|
|
295
488
|
const composition = useMemo(
|
|
296
489
|
() =>
|
|
297
490
|
composeSlides({
|
|
298
491
|
cells: slideCells,
|
|
299
492
|
getType: (cell) =>
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
493
|
+
deckSlideType({
|
|
494
|
+
cell,
|
|
495
|
+
noOutputIds,
|
|
496
|
+
heldEditCellId,
|
|
497
|
+
slideConfigs: layout.cells,
|
|
498
|
+
}),
|
|
303
499
|
}),
|
|
304
|
-
[slideCells, noOutputIds, layout.cells],
|
|
500
|
+
[slideCells, noOutputIds, layout.cells, heldEditCellId],
|
|
305
501
|
);
|
|
306
502
|
|
|
307
|
-
// Skipped and output-less cells aren't part of the composed deck. When one is
|
|
308
|
-
// selected in the minimap we render a preview over the deck and park reveal on
|
|
309
|
-
// a neighboring real slide; keyboard nav while parked is handled below.
|
|
310
|
-
const activeCellSlideType = activeCell
|
|
311
|
-
? layout.cells.get(activeCell.id)?.type
|
|
312
|
-
: undefined;
|
|
313
|
-
const isNoOutputPreview =
|
|
314
|
-
activeCell != null && noOutputIds.has(activeCell.id);
|
|
315
|
-
const isParkedPreview = activeCellSlideType === "skip" || isNoOutputPreview;
|
|
316
|
-
const parkedPreviewCell = isParkedPreview ? activeCell : null;
|
|
317
|
-
|
|
318
503
|
const { cellToTarget, targetToCellIndex } = useMemo(
|
|
319
504
|
() =>
|
|
320
505
|
buildSlideIndices({
|
|
@@ -336,6 +521,7 @@ const RevealSlidesComponent = ({
|
|
|
336
521
|
url.searchParams.set("show-chrome", "false");
|
|
337
522
|
return url.toString();
|
|
338
523
|
}, []);
|
|
524
|
+
|
|
339
525
|
const revealConfig: RevealConfig = useMemo(
|
|
340
526
|
() => ({
|
|
341
527
|
embedded: true,
|
|
@@ -378,14 +564,35 @@ const RevealSlidesComponent = ({
|
|
|
378
564
|
// the state update so the button/keypress paints first and the heavier mount
|
|
379
565
|
// can be interrupted by higher-priority work.
|
|
380
566
|
const toggleShowCode = useEvent(() => {
|
|
381
|
-
|
|
567
|
+
if (cellIdToShowCode == null || codeAlwaysShown) {
|
|
568
|
+
return;
|
|
569
|
+
}
|
|
570
|
+
if (isHeldEdit) {
|
|
571
|
+
toggleHeldShowsCode();
|
|
572
|
+
return;
|
|
573
|
+
}
|
|
574
|
+
startTransition(() =>
|
|
575
|
+
setShowCodeOverrides((prev) => {
|
|
576
|
+
const next = new Set(prev);
|
|
577
|
+
if (next.has(cellIdToShowCode)) {
|
|
578
|
+
next.delete(cellIdToShowCode);
|
|
579
|
+
} else {
|
|
580
|
+
next.add(cellIdToShowCode);
|
|
581
|
+
}
|
|
582
|
+
return next;
|
|
583
|
+
}),
|
|
584
|
+
);
|
|
382
585
|
});
|
|
383
586
|
|
|
384
587
|
const handleDeckReady = useEvent((deck: RevealApi) => {
|
|
385
588
|
navigateDeckToActiveCell(deck);
|
|
386
589
|
if (codeToggleEnabled) {
|
|
387
590
|
deck.addKeyBinding(
|
|
388
|
-
{
|
|
591
|
+
{
|
|
592
|
+
keyCode: 67,
|
|
593
|
+
key: "C",
|
|
594
|
+
description: "Toggle code editor",
|
|
595
|
+
},
|
|
389
596
|
toggleShowCode,
|
|
390
597
|
);
|
|
391
598
|
}
|
|
@@ -401,17 +608,6 @@ const RevealSlidesComponent = ({
|
|
|
401
608
|
}
|
|
402
609
|
});
|
|
403
610
|
|
|
404
|
-
const activeSubslide = useMemo(() => {
|
|
405
|
-
if (!activeCell) {
|
|
406
|
-
return null;
|
|
407
|
-
}
|
|
408
|
-
const target = cellToTarget.get(activeCell.id);
|
|
409
|
-
if (!target) {
|
|
410
|
-
return null;
|
|
411
|
-
}
|
|
412
|
-
return { h: target.h, v: target.v };
|
|
413
|
-
}, [activeCell, cellToTarget]);
|
|
414
|
-
|
|
415
611
|
// Forward the deck's current cell to the parent, except while a parked
|
|
416
612
|
// preview is parked: every reveal.js event during that window is an echo
|
|
417
613
|
// of the programmatic park (possibly with transient indices), so ignoring
|
|
@@ -463,9 +659,22 @@ const RevealSlidesComponent = ({
|
|
|
463
659
|
|
|
464
660
|
useEventListener(document, "keydown", handleParkedNavKey, { capture: true });
|
|
465
661
|
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
662
|
+
// `isHeldEdit` means the cell already produces output and is only kept in the
|
|
663
|
+
// overlay so the editor survives the edit, so the parked banners don't apply.
|
|
664
|
+
const parkedPreviewLabel = isHeldEdit
|
|
665
|
+
? null
|
|
666
|
+
: isNoOutputPreview
|
|
667
|
+
? "Hidden as there is no output"
|
|
668
|
+
: "Skipped in presentation";
|
|
669
|
+
|
|
670
|
+
const parkedShowCode = isHeldEdit
|
|
671
|
+
? heldShowsCode
|
|
672
|
+
: resolveShowCode(parkedPreviewCell?.id);
|
|
673
|
+
const parkedShowsSource = parkedRendersSource({
|
|
674
|
+
isNoOutputPreview,
|
|
675
|
+
isEditable,
|
|
676
|
+
showCode: parkedShowCode,
|
|
677
|
+
});
|
|
469
678
|
|
|
470
679
|
const slideArea = (
|
|
471
680
|
<div
|
|
@@ -485,13 +694,11 @@ const RevealSlidesComponent = ({
|
|
|
485
694
|
>
|
|
486
695
|
{composition.stacks.map((stack, h) => {
|
|
487
696
|
if (stack.subslides.length === 1) {
|
|
488
|
-
const isActive =
|
|
489
|
-
activeSubslide?.h === h && activeSubslide?.v === 0;
|
|
490
697
|
return (
|
|
491
698
|
<SubslideView
|
|
492
699
|
key={h}
|
|
493
700
|
subslide={stack.subslides[0]}
|
|
494
|
-
|
|
701
|
+
resolveShowCode={resolveShowCode}
|
|
495
702
|
isEditable={isEditable}
|
|
496
703
|
slideConfigs={layout.cells}
|
|
497
704
|
/>
|
|
@@ -500,13 +707,11 @@ const RevealSlidesComponent = ({
|
|
|
500
707
|
return (
|
|
501
708
|
<Stack key={h}>
|
|
502
709
|
{stack.subslides.map((sub, v) => {
|
|
503
|
-
const isActive =
|
|
504
|
-
activeSubslide?.h === h && activeSubslide?.v === v;
|
|
505
710
|
return (
|
|
506
711
|
<SubslideView
|
|
507
712
|
key={v}
|
|
508
713
|
subslide={sub}
|
|
509
|
-
|
|
714
|
+
resolveShowCode={resolveShowCode}
|
|
510
715
|
isEditable={isEditable}
|
|
511
716
|
slideConfigs={layout.cells}
|
|
512
717
|
/>
|
|
@@ -520,16 +725,18 @@ const RevealSlidesComponent = ({
|
|
|
520
725
|
<div
|
|
521
726
|
key={parkedPreviewCell.id}
|
|
522
727
|
className="absolute inset-0 z-10 border rounded bg-background flex flex-col overflow-hidden"
|
|
523
|
-
aria-label={parkedPreviewLabel}
|
|
728
|
+
aria-label={parkedPreviewLabel ?? undefined}
|
|
524
729
|
>
|
|
525
|
-
|
|
526
|
-
<
|
|
527
|
-
|
|
528
|
-
|
|
730
|
+
{parkedPreviewLabel && (
|
|
731
|
+
<div className="flex items-center gap-1.5 px-3 py-1.5 text-xs text-muted-foreground border-b bg-muted/40">
|
|
732
|
+
<EyeOffIcon className="h-3.5 w-3.5" />
|
|
733
|
+
<span>{parkedPreviewLabel}</span>
|
|
734
|
+
</div>
|
|
735
|
+
)}
|
|
529
736
|
<div className="flex-1 overflow-auto flex">
|
|
530
737
|
<div
|
|
531
738
|
className={
|
|
532
|
-
|
|
739
|
+
parkedShowsSource
|
|
533
740
|
? "mo-slide-content flex flex-col gap-3"
|
|
534
741
|
: "mo-slide-content"
|
|
535
742
|
}
|
|
@@ -539,7 +746,7 @@ const RevealSlidesComponent = ({
|
|
|
539
746
|
cell={parkedPreviewCell}
|
|
540
747
|
isNoOutputPreview={isNoOutputPreview}
|
|
541
748
|
isEditable={isEditable}
|
|
542
|
-
|
|
749
|
+
showCode={parkedShowCode}
|
|
543
750
|
/>
|
|
544
751
|
</div>
|
|
545
752
|
</div>
|
|
@@ -547,17 +754,35 @@ const RevealSlidesComponent = ({
|
|
|
547
754
|
)}
|
|
548
755
|
<div className="absolute top-2 right-2 z-20 opacity-0 group-hover:opacity-70 text-muted-foreground transition-opacity">
|
|
549
756
|
{codeToggleEnabled && (
|
|
550
|
-
<Tooltip
|
|
757
|
+
<Tooltip
|
|
758
|
+
content={
|
|
759
|
+
codeAlwaysShown
|
|
760
|
+
? "Code is always shown for this slide"
|
|
761
|
+
: cellShowsCode
|
|
762
|
+
? "Hide code (C)"
|
|
763
|
+
: "Show code (C)"
|
|
764
|
+
}
|
|
765
|
+
>
|
|
551
766
|
<Button
|
|
552
767
|
data-testid="marimo-plugin-slides-toggle-code"
|
|
553
768
|
variant="ghost"
|
|
554
769
|
size="icon"
|
|
770
|
+
// Stay hoverable (no `disabled` attr) so the tooltip can
|
|
771
|
+
// explain why the toggle is inert when code is pinned on.
|
|
555
772
|
className={cn(
|
|
556
773
|
"text-muted-foreground h-7 w-7",
|
|
557
|
-
|
|
774
|
+
cellShowsCode && "text-foreground bg-muted",
|
|
775
|
+
codeAlwaysShown && "opacity-50 cursor-not-allowed",
|
|
558
776
|
)}
|
|
559
|
-
aria-pressed={
|
|
560
|
-
aria-
|
|
777
|
+
aria-pressed={cellShowsCode}
|
|
778
|
+
aria-disabled={codeAlwaysShown}
|
|
779
|
+
aria-label={
|
|
780
|
+
codeAlwaysShown
|
|
781
|
+
? "Code always shown"
|
|
782
|
+
: cellShowsCode
|
|
783
|
+
? "Hide code"
|
|
784
|
+
: "Show code"
|
|
785
|
+
}
|
|
561
786
|
onClick={toggleShowCode}
|
|
562
787
|
>
|
|
563
788
|
<CodeIcon className="h-4 w-4" />
|
|
@@ -607,6 +832,7 @@ const RevealSlidesComponent = ({
|
|
|
607
832
|
<PanelResizeHandle
|
|
608
833
|
className="mo-slides-notes-resize"
|
|
609
834
|
hitAreaMargins={{ coarse: 12, fine: 4 }}
|
|
835
|
+
disabled={isFullscreen}
|
|
610
836
|
/>
|
|
611
837
|
<Panel
|
|
612
838
|
defaultSize={10}
|
|
@@ -3,14 +3,18 @@
|
|
|
3
3
|
import { useMemo, useRef, useState } from "react";
|
|
4
4
|
import type { EditorView } from "@codemirror/view";
|
|
5
5
|
import { useAtomValue } from "jotai";
|
|
6
|
+
import useEvent from "react-use-event-hook";
|
|
6
7
|
import { cellDomProps } from "@/components/editor/common";
|
|
7
8
|
import { CellEditor } from "@/components/editor/cell/code/cell-editor";
|
|
9
|
+
import { LanguageToggles } from "@/components/editor/cell/code/language-toggle";
|
|
8
10
|
import { CellStatusComponent } from "@/components/editor/cell/CellStatus";
|
|
9
11
|
import { RunButton } from "@/components/editor/cell/RunButton";
|
|
10
12
|
import { StopButton } from "@/components/editor/cell/StopButton";
|
|
11
13
|
import { useRunCell } from "@/components/editor/cell/useRunCells";
|
|
12
14
|
import { Slide as CellOutputSlide } from "@/components/slides/slide";
|
|
13
|
-
import {
|
|
15
|
+
import { maybeAddMarimoImport } from "@/core/cells/add-missing-import";
|
|
16
|
+
import { useCellActions } from "@/core/cells/cells";
|
|
17
|
+
import { autoInstantiateAtom, useUserConfig } from "@/core/config/config";
|
|
14
18
|
import {
|
|
15
19
|
cellNeedsRun,
|
|
16
20
|
cellStatusClasses,
|
|
@@ -37,12 +41,24 @@ export const SlideCellView = ({ cell }: { cell: RuntimeCell }) => {
|
|
|
37
41
|
const { theme } = useTheme();
|
|
38
42
|
const runCell = useRunCell(cell.id);
|
|
39
43
|
const connection = useAtomValue(connectionAtom);
|
|
44
|
+
const cellActions = useCellActions();
|
|
45
|
+
const autoInstantiate = useAtomValue(autoInstantiateAtom);
|
|
40
46
|
const editorViewRef = useRef<EditorView | null>(null);
|
|
41
47
|
const editorViewParentRef = useRef<HTMLDivElement | null>(null);
|
|
42
48
|
const [languageAdapter, setLanguageAdapter] = useState<
|
|
43
49
|
LanguageAdapterType | undefined
|
|
44
50
|
>();
|
|
45
51
|
|
|
52
|
+
const afterToggleLanguage = useEvent(() => {
|
|
53
|
+
maybeAddMarimoImport({
|
|
54
|
+
autoInstantiate,
|
|
55
|
+
createNewCell: cellActions.createNewCell,
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
// Must be a stable identity: it feeds the editor's `extensions` memo
|
|
60
|
+
const showHiddenCode = useEvent(() => undefined);
|
|
61
|
+
|
|
46
62
|
const cellOutputPosition = userConfig.display.cell_output;
|
|
47
63
|
const hasOutput = cell.output != null;
|
|
48
64
|
|
|
@@ -97,6 +113,13 @@ export const SlideCellView = ({ cell }: { cell: RuntimeCell }) => {
|
|
|
97
113
|
lastRunStartTimestamp={cell.lastRunStartTimestamp}
|
|
98
114
|
uninstantiated={uninstantiated}
|
|
99
115
|
/>
|
|
116
|
+
<LanguageToggles
|
|
117
|
+
code={cell.code}
|
|
118
|
+
editorView={editorViewRef.current}
|
|
119
|
+
currentLanguageAdapter={languageAdapter}
|
|
120
|
+
onAfterToggle={afterToggleLanguage}
|
|
121
|
+
className="flex items-center gap-1"
|
|
122
|
+
/>
|
|
100
123
|
<div className="flex items-center shadow-none gap-1">
|
|
101
124
|
<RunButton
|
|
102
125
|
edited={cell.edited}
|
|
@@ -113,6 +136,7 @@ export const SlideCellView = ({ cell }: { cell: RuntimeCell }) => {
|
|
|
113
136
|
|
|
114
137
|
const editor = (
|
|
115
138
|
<div
|
|
139
|
+
tabIndex={-1}
|
|
116
140
|
className={editorWrapperClassName}
|
|
117
141
|
{...cellDomProps(cell.id, cell.name)}
|
|
118
142
|
>
|
|
@@ -134,7 +158,7 @@ export const SlideCellView = ({ cell }: { cell: RuntimeCell }) => {
|
|
|
134
158
|
hasOutput={hasOutput}
|
|
135
159
|
// hide_code is intentionally overridden in the slide view; the editor
|
|
136
160
|
// is unmounted entirely when the user toggles code off.
|
|
137
|
-
showHiddenCode={
|
|
161
|
+
showHiddenCode={showHiddenCode}
|
|
138
162
|
languageAdapter={languageAdapter}
|
|
139
163
|
setLanguageAdapter={setLanguageAdapter}
|
|
140
164
|
showLanguageToggles={false}
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
PanelRightOpenIcon,
|
|
11
11
|
KeyboardIcon,
|
|
12
12
|
} from "lucide-react";
|
|
13
|
+
import { Switch } from "@/components/ui/switch";
|
|
13
14
|
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
|
|
14
15
|
import {
|
|
15
16
|
Select,
|
|
@@ -196,13 +197,22 @@ const SlideConfigForm = ({
|
|
|
196
197
|
setLayout: (layout: SlidesLayout) => void;
|
|
197
198
|
cellId: CellId;
|
|
198
199
|
}) => {
|
|
199
|
-
const
|
|
200
|
-
|
|
200
|
+
const currentConfig = layout.cells.get(cellId);
|
|
201
|
+
const currentSlideType: SlideType = currentConfig?.type ?? DEFAULT_SLIDE_TYPE;
|
|
202
|
+
const showCode = currentConfig?.showCode ?? false;
|
|
201
203
|
|
|
202
204
|
const handleSlideTypeChange = (value: SlideType) => {
|
|
203
|
-
const existingConfig = layout.cells.get(cellId);
|
|
204
205
|
const newCells = new Map(layout.cells);
|
|
205
|
-
newCells.set(cellId, { ...
|
|
206
|
+
newCells.set(cellId, { ...currentConfig, type: value });
|
|
207
|
+
setLayout({
|
|
208
|
+
...layout,
|
|
209
|
+
cells: newCells,
|
|
210
|
+
});
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
const handleShowCodeChange = (checked: boolean) => {
|
|
214
|
+
const newCells = new Map(layout.cells);
|
|
215
|
+
newCells.set(cellId, { ...currentConfig, showCode: checked });
|
|
206
216
|
setLayout({
|
|
207
217
|
...layout,
|
|
208
218
|
cells: newCells,
|
|
@@ -261,6 +271,18 @@ const SlideConfigForm = ({
|
|
|
261
271
|
);
|
|
262
272
|
})}
|
|
263
273
|
</RadioGroup>
|
|
274
|
+
<div className="flex items-center gap-2">
|
|
275
|
+
<label htmlFor="slide-show-code" className="text-sm">
|
|
276
|
+
Show code
|
|
277
|
+
</label>
|
|
278
|
+
<Switch
|
|
279
|
+
id="slide-show-code"
|
|
280
|
+
aria-label="Show code"
|
|
281
|
+
checked={showCode}
|
|
282
|
+
onCheckedChange={handleShowCodeChange}
|
|
283
|
+
size="sm"
|
|
284
|
+
/>
|
|
285
|
+
</div>
|
|
264
286
|
</div>
|
|
265
287
|
);
|
|
266
288
|
};
|