Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
117 changes: 25 additions & 92 deletions src/app/App.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,24 @@
// SPDX-License-Identifier: MIT
// Copyright (c) 2020-2023 The Pybricks Authors
// Copyright (c) 2020-2026 The Pybricks Authors

import 'react-splitter-layout/lib/index.css';
import './app.scss';
import { Classes, Spinner } from '@blueprintjs/core';
import { Button, Classes, Spinner } from '@blueprintjs/core';
import { Manual } from '@blueprintjs/icons';
import React, { useEffect, useState } from 'react';

type SideView = 'off' | 'docs';
import SplitterLayout from 'react-splitter-layout';
import { useLocalStorage, useTernaryDarkMode } from 'usehooks-ts';
import Activities from '../activities/Activities';
import DfuWindowsDriverInstallDialog from '../firmware/dfuWindowsDriverInstallDialog/DfuWindowsDriverInstallDialog';
import { InstallPybricksDialog } from '../firmware/installPybricksDialog/InstallPybricksDialog';
import RestoreOfficialDialog from '../firmware/restoreOfficialDialog/RestoreOfficialDialog';
import { useSettingIsShowDocsEnabled } from '../settings/hooks';
import SponsorDialog from '../sponsor/SponsorDialog';
import StatusBar from '../status-bar/StatusBar';
import Toolbar from '../toolbar/Toolbar';
import Tour from '../tour/Tour';
import { isMacOS } from '../utils/os';
import { useAppLastDocsPageSetting } from './hooks';
import { docsDefaultPage } from './constants';
import { useI18n } from './i18n';

const Editor = React.lazy(async () => {
Expand Down Expand Up @@ -47,105 +48,21 @@ const Terminal = React.lazy(async () => {
});

const Docs: React.FunctionComponent = () => {
const { setIsSettingShowDocsEnabled } = useSettingIsShowDocsEnabled();
const { initialDocsPage, setLastDocsPage } = useAppLastDocsPageSetting();

return (
<iframe
// REVISIT: some of this could be moved to the docs repo
// so that it runs earlier to prevent flashing in the UI.
// The load event doesn't run until after the page is fully
// loaded and there doesn't seem to be a reasonable way to
// hook into the iframe to know when it has a new document.
onLoad={(e) => {
// HACK: this mess restores the scroll position when
// the documentation iframe visibility is toggled.
// The iframe will be automatically scrolled to 0 when
// CSS `display: none` is set.

const target = e.target as HTMLIFrameElement;
const contentWindow = target.contentWindow;
if (!contentWindow) {
console.error('could not get iframe content window');
return;
}

// the last "good" scrollY value of the iframe
let iframeScroll = 0;

// This bit monitors the visibility.
// https://stackoverflow.com/a/44670818/1976323
const observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
// Restore the scroll position when the iframe is
// shown. Toggling the visibility prevents flashing
// the contents from the top of the page before the
// scroll is done.
if (entry.intersectionRatio > 0) {
contentWindow.scrollTo(0, iframeScroll);
contentWindow.document.documentElement.style.visibility =
'visible';
} else {
contentWindow.document.documentElement.style.visibility =
'hidden';
}
});
},
{
root: target.parentElement,
},
);

observer.observe(target);

// Have to remove he observer, otherwise we end up with
// conflicting values when a new page is loaded in the iframe.
contentWindow.addEventListener('unload', () => {
observer.unobserve(target);
});

// And this keeps track of the scroll position.
contentWindow.addEventListener('scroll', () => {
if (contentWindow.scrollY !== 0) {
// Record the current scroll position. If it is 0, it
// could be that the iframe has been hidden or the user
// scrolled there. So we have to ignore 0. But we don't
// want to be one pixel off if the user really did
// scroll there, so we assume that if the last scroll
// is 1, then the user probably went all the way to 0.
if (contentWindow.scrollY === 1) {
iframeScroll = 0;
} else {
iframeScroll = contentWindow.scrollY;
}
}
});

// Override browser default key bindings in iframe.
contentWindow.document.addEventListener('keydown', (e) => {
// use Ctrl-D/Cmd-D to toggle docs
if (
(isMacOS()
? e.metaKey && !e.ctrlKey
: e.ctrlKey && !e.metaKey) &&
!e.altKey &&
e.key === 'd'
) {
e.preventDefault();
// since the iframe is only visible when docs are shown
// the only action is to hide the docs
setIsSettingShowDocsEnabled(false);
}
});

if (document.body.classList.contains(Classes.DARK)) {
contentWindow.document.documentElement.classList.add(Classes.DARK);
}

setLastDocsPage(contentWindow.location.href);
}}
src={initialDocsPage}
src={docsDefaultPage}
allowFullScreen={true}
width="100%"
height="100%"
Expand All @@ -156,7 +73,7 @@ const Docs: React.FunctionComponent = () => {
const App: React.FunctionComponent = () => {
const i18n = useI18n();
const { isDarkMode } = useTernaryDarkMode();
const { isSettingShowDocsEnabled } = useSettingIsShowDocsEnabled();
const [sideView, setSideView] = useState<SideView>('off');
const [isDragging, setIsDragging] = useState(false);

const [docsSplit, setDocsSplit] = useLocalStorage('app-docs-split', 30);
Expand Down Expand Up @@ -201,7 +118,7 @@ const App: React.FunctionComponent = () => {
<div className="pb-app-main" style={{ position: 'relative' }}>
<SplitterLayout
customClassName={
isSettingShowDocsEnabled ? 'pb-show-docs' : 'pb-hide-docs'
sideView === 'docs' ? 'pb-show-docs' : 'pb-hide-docs'
}
onDragStart={(): void => setIsDragging(true)}
onDragEnd={(): void => setIsDragging(false)}
Expand All @@ -225,6 +142,22 @@ const App: React.FunctionComponent = () => {
>
<Editor />
</React.Suspense>
<Button
className="pb-app-doc-button"
minimal
large
icon={<Manual />}
title={
sideView === 'docs'
? i18n.translate('docs.hide')
: i18n.translate('docs.show')
}
onClick={() =>
setSideView(
sideView === 'docs' ? 'off' : 'docs',
)
}
/>
</main>
<aside
className="pb-app-terminal"
Expand Down
21 changes: 18 additions & 3 deletions src/app/app.scss
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
// Copyright (c) 2020-2022 The Pybricks Authors
// Copyright (c) 2020-2026 The Pybricks Authors

// Custom styling for the App control.

Expand Down Expand Up @@ -71,13 +71,21 @@
height: 100%;
display: flex;
flex-direction: column;
position: relative;

& .pb-editor {
min-height: 0;
flex: 1 1 auto;
}
}

.pb-app-doc-button {
position: absolute;
bottom: bp.$pt-grid-size;
right: bp.$pt-grid-size;
z-index: bp.$pt-z-index-overlay;
}

$splitter-background-color: bp.$light-gray2;
$splitter-background-color-hover: bp.$gray4;
$dark-splitter-background-color: bp.$dark-gray5;
Expand Down Expand Up @@ -184,7 +192,14 @@ $dark-splitter-color-hover: color.adjust(
}

// hide the docs and resize separator

// Use visibility rather than display:none so the iframe keeps its layout and
// scroll position when the docs panel is toggled.
// Collapse the secondary pane and splitter to zero width so they don't
// consume space, while keeping the iframe in the DOM.
div.pb-hide-docs > :not(.layout-pane-primary) {
display: none;
visibility: hidden;
pointer-events: none;
flex-basis: 0 !important;
width: 0 !important;
min-width: 0 !important;
}
55 changes: 0 additions & 55 deletions src/app/hooks.ts

This file was deleted.

4 changes: 4 additions & 0 deletions src/app/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,9 @@
"editor": "Editor",
"terminal": "Terminal",
"documentation": "Documentation"
},
"docs": {
"show": "Show documentation",
"hide": "Hide documentation"
}
}
38 changes: 2 additions & 36 deletions src/editor/Editor.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
// Copyright (c) 2020-2023 The Pybricks Authors
// Copyright (c) 2020-2026 The Pybricks Authors

import './editor.scss';
import {
Expand All @@ -16,15 +16,7 @@ import {
Tabs,
Text,
} from '@blueprintjs/core';
import {
Blank,
Clipboard,
Cross,
Duplicate,
Manual,
Redo,
Undo,
} from '@blueprintjs/icons';
import { Blank, Clipboard, Cross, Duplicate, Redo, Undo } from '@blueprintjs/icons';
import classNames from 'classnames';
import * as monaco from 'monaco-editor';
import tomorrowNightEightiesTheme from 'monaco-themes/themes/Tomorrow-Night-Eighties.json';
Expand All @@ -37,7 +29,6 @@ import { UUID } from '../fileStorage';
import { useFileStoragePath } from '../fileStorage/hooks';
import { compile } from '../mpy/actions';
import { useSelector } from '../reducers';
import { useSettingIsShowDocsEnabled } from '../settings/hooks';
import { isMacOS } from '../utils/os';
import Welcome from './Welcome';
import { editorActivateFile, editorCloseFile } from './actions';
Expand Down Expand Up @@ -387,8 +378,6 @@ const Editor: React.FunctionComponent = () => {
const dispatch = useDispatch();

const [editor, setEditor] = useState<monaco.editor.IStandaloneCodeEditor>();
const { isSettingShowDocsEnabled, toggleIsSettingShowDocsEnabled } =
useSettingIsShowDocsEnabled();
const { isDarkMode } = useTernaryDarkMode();

const i18n = useI18n();
Expand All @@ -409,17 +398,6 @@ const Editor: React.FunctionComponent = () => {
[i18n],
);

useEditorAction(
editor,
() => ({
id: 'pybricks.action.toggleDocs',
label: i18n.translate('toggleDocs'),
run: () => toggleIsSettingShowDocsEnabled(),
keybindings: [monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyD],
}),
[i18n, toggleIsSettingShowDocsEnabled],
);

useEditorAction(
editor,
() => ({
Expand Down Expand Up @@ -521,18 +499,6 @@ const Editor: React.FunctionComponent = () => {
<div className="pb-editor-monaco" ref={editorRef} />
</ContextMenu>
</ResizeSensor>
<Button
className="pb-editor-doc-button"
minimal
large
icon={<Manual />}
title={
isSettingShowDocsEnabled
? i18n.translate('docs.hide')
: i18n.translate('docs.show')
}
onClick={toggleIsSettingShowDocsEnabled}
/>
</div>
);
};
Expand Down
7 changes: 0 additions & 7 deletions src/editor/editor.scss
Original file line number Diff line number Diff line change
Expand Up @@ -93,13 +93,6 @@
color: bp.$pt-dark-text-color-muted;
}
}

&-doc-button {
position: absolute;
bottom: bp.$pt-grid-size;
right: bp.$pt-grid-size;
z-index: bp.$pt-z-index-overlay;
}
}

.monaco-editor {
Expand Down
7 changes: 1 addition & 6 deletions src/editor/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,11 @@
"welcome": "Welcome",
"placeholder": "Write your program here...",
"check": "Check syntax",
"toggleDocs": "Toggle documentation",
"copy": "Copy",
"paste": "Paste",
"selectAll": "Select all",
"undo": "Undo",
"redo": "Redo",
"closeFile": { "tooltip": "Close {fileName}" },
"contextMenu": { "label": "Editor context menu" },
"docs": {
"show": "Show documentation",
"hide": "Hide documentation"
}
"contextMenu": { "label": "Editor context menu" }
}
Loading