Skip to content

Commit 64772e9

Browse files
committed
Add launcher from bottom toolbar and keyboard shortcut
1 parent faa3ce5 commit 64772e9

File tree

5 files changed

+67
-9
lines changed

5 files changed

+67
-9
lines changed

frontend/src/components/editor/actions/useNotebookActions.tsx

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import {
2929
KeyboardIcon,
3030
LayoutTemplateIcon,
3131
LinkIcon,
32+
MapIcon,
3233
MessagesSquareIcon,
3334
PanelLeftIcon,
3435
PowerSquareIcon,
@@ -95,8 +96,8 @@ const NOOP_HANDLER = (event?: Event) => {
9596
export function useNotebookActions() {
9697
const filename = useFilename();
9798
const { openModal, closeModal } = useImperativeModal();
98-
const { toggleApplication } = useChromeActions();
99-
const { selectedPanel } = useChromeState();
99+
const { toggleApplication, toggleMinimap } = useChromeActions();
100+
const { selectedPanel, isMinimapOpen } = useChromeState();
100101
const [viewState] = useAtom(viewStateAtom);
101102
const kioskMode = useAtomValue(kioskModeAtom);
102103
const hideAllMarkdownCode = useHideAllMarkdownCode();
@@ -286,6 +287,11 @@ export function useNotebookActions() {
286287
};
287288
}),
288289
},
290+
{
291+
icon: <MapIcon size={14} strokeWidth={1.5} />,
292+
label: isMinimapOpen ? "Hide minimap" : "Show minimap",
293+
handle: () => toggleMinimap(),
294+
},
289295

290296
{
291297
icon: <PresentationIcon size={14} strokeWidth={1.5} />,

frontend/src/components/editor/chrome/state.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ export interface ChromeState {
1010
selectedPanel: PanelType | undefined;
1111
isSidebarOpen: boolean;
1212
isTerminalOpen: boolean;
13+
isMinimapOpen: boolean;
1314
}
1415

1516
const KEY = "marimo:sidebar";
@@ -21,6 +22,7 @@ const storage = new ZodLocalStorage<ChromeState>(
2122
.transform((v) => v as PanelType),
2223
isSidebarOpen: z.boolean(),
2324
isTerminalOpen: z.boolean(),
25+
isMinimapOpen: z.boolean(),
2426
}),
2527
initialState,
2628
);
@@ -30,6 +32,7 @@ function initialState(): ChromeState {
3032
selectedPanel: "variables", // initial panel
3133
isSidebarOpen: false,
3234
isTerminalOpen: false,
35+
isMinimapOpen: false,
3336
};
3437
}
3538

@@ -71,6 +74,14 @@ const {
7174
...state,
7275
isTerminalOpen: isOpen,
7376
}),
77+
toggleMinimap: (state) => ({
78+
...state,
79+
isMinimapOpen: !state.isMinimapOpen,
80+
}),
81+
setIsMinimapOpen: (state, isOpen: boolean) => ({
82+
...state,
83+
isMinimapOpen: isOpen,
84+
}),
7485
},
7586
[(_prevState, newState) => storage.set(KEY, newState)],
7687
);
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/* Copyright 2024 Marimo. All rights reserved. */
2+
3+
import { MapIcon } from "lucide-react";
4+
import { useChromeActions, useChromeState } from "../../state";
5+
import { FooterItem } from "../footer-item";
6+
7+
export const MinimapStatusIcon: React.FC = () => {
8+
const { isMinimapOpen } = useChromeState();
9+
const { toggleMinimap } = useChromeActions();
10+
11+
return (
12+
<FooterItem
13+
tooltip="Toggle Minimap"
14+
selected={isMinimapOpen}
15+
onClick={() => toggleMinimap()}
16+
data-testid="footer-minimap"
17+
>
18+
<MapIcon className="h-4 w-4" />
19+
</FooterItem>
20+
);
21+
};

frontend/src/components/editor/chrome/wrapper/footer.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import { AIStatusIcon } from "./footer-items/ai-status";
1717
import { BackendConnection } from "./footer-items/backend-status";
1818
import { CopilotStatusIcon } from "./footer-items/copilot-status";
1919
import { MachineStats } from "./footer-items/machine-stats";
20+
import { MinimapStatusIcon } from "./footer-items/minimap-status";
2021
import { RTCStatus } from "./footer-items/rtc-status";
2122
import { RuntimeSettings } from "./footer-items/runtime-settings";
2223

@@ -78,6 +79,7 @@ export const Footer: React.FC = () => {
7879

7980
<div className="flex items-center flex-shrink-0 min-w-0">
8081
<MachineStats />
82+
<MinimapStatusIcon />
8183
<AIStatusIcon />
8284
<CopilotStatusIcon />
8385
<RTCStatus />

frontend/src/components/editor/chrome/wrapper/minimap.tsx

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
11
/* Copyright 2024 Marimo. All rights reserved. */
22

33
import { useAtomValue } from "jotai";
4-
import React from "react";
4+
import { XIcon } from "lucide-react";
5+
import * as React from "react";
6+
import { Button } from "@/components/ui/button";
57
import { useCellActions, useNotebook } from "@/core/cells/cells";
68
import { cellFocusAtom, useCellFocusActions } from "@/core/cells/focus";
79
import type { CellId } from "@/core/cells/ids";
810
import { useVariables } from "@/core/variables/state";
911
import type { VariableName } from "@/core/variables/types";
1012
import { cn } from "@/utils/cn";
13+
import { useChromeActions, useChromeState } from "../state";
1114
import {
1215
type CellGraph,
1316
cellGraphsAtom,
@@ -122,13 +125,14 @@ const MinimapCell: React.FC<MinimapCellProps> = (props) => {
122125
);
123126
};
124127

125-
export const Minimap: React.FC<{ className?: string }> = ({ className }) => {
128+
const MinimapInternal: React.FC<{
129+
open: boolean;
130+
setOpen: (update: boolean) => void;
131+
}> = ({ open, setOpen }) => {
126132
const notebook = useNotebook();
127-
128133
const cellPositions: Record<CellId, number> = Object.fromEntries(
129134
notebook.cellIds.inOrderIds.map((id, idx) => [id, idx]),
130135
);
131-
132136
const columnBoundaries: number[] = [];
133137
let cellCount = 0;
134138
for (const [idx, column] of notebook.cellIds.getColumns().entries()) {
@@ -137,16 +141,24 @@ export const Minimap: React.FC<{ className?: string }> = ({ className }) => {
137141
}
138142
cellCount += column.inOrderIds.length;
139143
}
140-
141144
return (
142145
<div
143146
className={cn(
144-
"fixed top-14 right-4 z-50 bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60 rounded-lg border shadow-lg w-64 flex flex-col max-h-[75vh]",
145-
className,
147+
"fixed top-14 right-5 z-50 bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60 rounded-lg border shadow-lg w-64 flex flex-col max-h-[58vh]",
148+
"motion-safe:transition-transform motion-safe:duration-200 motion-safe:ease-in-out",
149+
open ? "translate-x-0" : "translate-x-[calc(100%+20px)]",
146150
)}
147151
>
148152
<div className="flex items-center justify-between p-4 border-b">
149153
<span className="text-sm font-semibold">Minimap</span>
154+
<Button
155+
variant="ghost"
156+
size="icon"
157+
className="h-6 w-6"
158+
onClick={() => setOpen(false)}
159+
>
160+
<XIcon className="h-4 w-4" />
161+
</Button>
150162
</div>
151163
<div className="overflow-y-auto overflow-x-hidden flex-1 scrollbar-none">
152164
<div className="py-3 pl-3 pr-4 relative min-h-full">
@@ -173,6 +185,12 @@ export const Minimap: React.FC<{ className?: string }> = ({ className }) => {
173185
);
174186
};
175187

188+
export const Minimap: React.FC = () => {
189+
const { setIsMinimapOpen } = useChromeActions();
190+
const { isMinimapOpen } = useChromeState();
191+
return <MinimapInternal open={isMinimapOpen} setOpen={setIsMinimapOpen} />;
192+
};
193+
176194
function codePreview(code: string): string | undefined {
177195
return code.split("\n")[0].trim() || undefined;
178196
}

0 commit comments

Comments
 (0)