Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
b966e9a
Playground file browser
adamziel Oct 21, 2025
d09b6f0
resizable site panel
adamziel Oct 21, 2025
75f935d
resizable site panel
adamziel Oct 22, 2025
5223de8
Merge
adamziel Oct 23, 2025
6f6bfb3
Don't move the focus back to the file tree too early
adamziel Oct 23, 2025
c9e8507
Double click = focus on the file. Single click = select file
adamziel Oct 23, 2025
6e9934d
Enter = focus on the code editor
adamziel Oct 23, 2025
d04a1f5
Immediately open newly created files
adamziel Oct 23, 2025
2c0686c
Focus on the first context menu item
adamziel Oct 23, 2025
7d69e57
E2E the file picker
adamziel Oct 23, 2025
cb22d52
Long editor
adamziel Oct 23, 2025
c4282d9
Retain cursor position in the code editor
adamziel Oct 23, 2025
2378604
Reopen the last active tab when Playground is reopened
adamziel Oct 23, 2025
bfa8f56
Lint, typecheck
adamziel Oct 23, 2025
41707a5
remove dev artifacts
adamziel Oct 23, 2025
6fa8eee
Merge branch 'trunk' into file-browser
adamziel Oct 23, 2025
43ddc83
Load non-php language extensions asynchronously
adamziel Oct 23, 2025
97c83a6
Simplify logic, improve comments
adamziel Oct 23, 2025
9712e4b
Simplify logic, remove constants file, reuse documentRoot, reuse WP i…
adamziel Oct 23, 2025
63afe2c
Display a clickable download link when editing binary files
adamziel Oct 23, 2025
cd5a154
Reuse php-wasm/util fs helpers instead of reimplementing them
adamziel Oct 23, 2025
402d7f5
Rename isProbablyTextBuffer to seemsLikeBinary
adamziel Oct 23, 2025
bb80aa7
Remove 300ms delay when opening a file
adamziel Oct 24, 2025
2e500e1
lint
adamziel Oct 24, 2025
ac748f3
Add Blueprint editor
adamziel Oct 24, 2025
56e1941
Don't lose focus when typing in the Blueprint editor
adamziel Oct 24, 2025
91d1331
Recreate Playground after clicking the button
adamziel Oct 24, 2025
31f9587
UI improvements
adamziel Oct 24, 2025
0c6aa13
UI improvements
adamziel Oct 24, 2025
29dec41
UI improvements
adamziel Oct 24, 2025
f8fa230
Merge branch 'trunk' into blueprint-editor
adamziel Oct 26, 2025
1182e53
Fix the test
adamziel Oct 27, 2025
3d7d5f2
Add e2e tests
adamziel Oct 28, 2025
9593220
Lint, typecheck
adamziel Oct 28, 2025
bdcd709
Make the Blueprints editor component optional for the offline mode
adamziel Nov 21, 2025
f18b5c3
Remove the "View Blueprint" menu item
adamziel Nov 21, 2025
29f0b84
Merge branch 'trunk' into blueprint-editor
adamziel Nov 21, 2025
c001fa4
Merge branch 'trunk' into blueprint-editor
adamziel Nov 21, 2025
7bbe613
Merge branch 'trunk' into blueprint-editor
adamziel Nov 21, 2025
2d29dc0
reenable firefox proejct
adamziel Nov 21, 2025
4a4bf00
restore headless option
adamziel Nov 21, 2025
d438d33
Merge branch 'trunk' into blueprint-editor
adamziel Nov 25, 2025
46deac5
revert docs changes
adamziel Nov 25, 2025
8055241
Reformat
adamziel Nov 25, 2025
4bbc1ac
reformat
adamziel Nov 25, 2025
dd5d6f8
reformat
adamziel Nov 25, 2025
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
Prev Previous commit
Next Next commit
resizable site panel
  • Loading branch information
adamziel committed Oct 21, 2025
commit d09b6f040916a91fc3e733002dd4cf27ac59d37b
108 changes: 70 additions & 38 deletions packages/playground/website/src/components/layout/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import css from './style.module.css';
import classNames from 'classnames';

import { SiteManager } from '../site-manager';
import { CSSTransition } from 'react-transition-group';
Expand All @@ -25,14 +26,17 @@ import {
supportedDisplayModes,
PlaygroundViewport,
} from '../playground-viewport';
import {
setActiveModal,
setSiteManagerOpen,
} from '../../lib/state/redux/slice-ui';
import { setActiveModal } from '../../lib/state/redux/slice-ui';
import { ImportFormModal } from '../import-form-modal';
import { PreviewPRModal } from '../../github/preview-pr';
import { MissingSiteModal } from '../missing-site-modal';
import { RenameSiteModal } from '../rename-site-modal';
import {
PanelGroup,
Panel,
PanelResizeHandle,
type ImperativePanelHandle,
} from 'react-resizable-panels';

acquireOAuthTokenIfNeeded();

Expand Down Expand Up @@ -62,47 +66,75 @@ export function Layout() {
(state) => state.ui.siteManagerIsOpen
);
const siteManagerWrapperRef = useRef<HTMLDivElement>(null);
const dispatch = useAppDispatch();
const siteManagerPanelRef = useRef<ImperativePanelHandle | null>(null);
const [defaultPanelSize] = useState(() => {
if (typeof window === 'undefined') {
return 55;
}
const defaultWidth = 320 + 555 + 24; // sidebar + info + borders
const percent = (defaultWidth / window.innerWidth) * 100;
return Math.max(35, Math.min(70, Math.round(percent)));
});

useEffect(() => {
if (!siteManagerPanelRef.current) {
return;
}
if (siteManagerIsOpen) {
siteManagerPanelRef.current.expand();
} else {
siteManagerPanelRef.current.collapse();
}
}, [siteManagerIsOpen]);

return (
<div className={`${css.layout}`}>
<Modals />
<CSSTransition
nodeRef={siteManagerWrapperRef}
in={siteManagerIsOpen}
timeout={500}
classNames={{
enter: css.siteManagerWrapperEnter,
enterActive: css.siteManagerWrapperEnterActive,
exit: css.siteManagerWrapperExit,
exitActive: css.siteManagerWrapperExitActive,
}}
unmountOnExit
<PanelGroup
direction="horizontal"
className={css.mainPanels}
autoSaveId="layout-panels"
>
<div
ref={siteManagerWrapperRef}
className={css.siteManagerWrapper}
<Panel
ref={siteManagerPanelRef}
defaultSize={defaultPanelSize}
minSize={30}
collapsible
collapsedSize={0}
>
<SiteManager />
</div>
</CSSTransition>
<div className={css.siteView}>
{siteManagerIsOpen && (
<div
title="Open site"
className={css.siteViewOverlay}
onClick={() => {
dispatch(setSiteManagerOpen(false));
<CSSTransition
nodeRef={siteManagerWrapperRef}
in={siteManagerIsOpen}
timeout={300}
classNames={{
enter: css.siteManagerWrapperEnter,
enterActive: css.siteManagerWrapperEnterActive,
exit: css.siteManagerWrapperExit,
exitActive: css.siteManagerWrapperExitActive,
}}
/>
)}
<div className={css.siteViewContent}>
<PlaygroundViewport
displayMode={displayMode}
hideToolbar={siteManagerIsOpen}
/>
</div>
</div>
unmountOnExit
>
<div
ref={siteManagerWrapperRef}
className={css.siteManagerWrapper}
>
<SiteManager />
</div>
</CSSTransition>
</Panel>
<PanelResizeHandle
className={classNames(css.layoutResizeHandle, {
[css.handleHidden]: !siteManagerIsOpen,
})}
/>
<Panel minSize={30}>
<div className={css.siteView}>
<div className={css.siteViewContent}>
<PlaygroundViewport displayMode={displayMode} />
</div>
</div>
</Panel>
</PanelGroup>
</div>
);
}
Expand Down
140 changes: 42 additions & 98 deletions packages/playground/website/src/components/layout/style.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
--site-manager-site-info-min-width: 555px;
--site-manager-width-desktop: calc(
var(--site-manager-site-list-width) +
var(--site-manager-site-info-min-width) +
2 * var(--site-manager-border-width)
var(--site-manager-site-info-min-width) + 2 *
var(--site-manager-border-width)
);
--site-view-min-width: 320px;
--site-manager-background-color: #1e1e1e;
Expand All @@ -29,134 +29,78 @@ body {
overflow: hidden;
}

.site-view {
.main-panels {
display: flex;
width: 100%;
height: 100%;
}

.site-manager-wrapper {
max-width: var(--site-manager-width-desktop);
@media (max-width: 875px) {
min-width: 0;
width: 100%;
}
height: 100%;
display: flex;
}

.site-manager-wrapper-enter {
opacity: 0;
max-width: 0;
}

.site-manager-wrapper-exit,
.site-manager-wrapper-enter-active,
.site-manager-wrapper-exit-active {
transition: opacity 300ms ease;
}

.site-manager-wrapper-enter-active {
opacity: 1;
transform: none;
max-width: var(--site-manager-width-desktop);
@media (max-width: 875px) {
width: 100%;
}
}

/*
* Repeated rule to ensure it's more specific than
* .site-manager-wrapper-exit
*/
.site-manager-wrapper-exit-active {
opacity: 0;
max-width: 0;
.site-manager-wrapper-exit {
opacity: 1;
}

.site-manager-wrapper-exit-active,
.site-manager-wrapper-enter-active {
transition:
/*
* Workaround: Animate max-width to allow `width: auto`
* The `width` property needs to be `auto` to ensure the
* site manager panel expands and shrinks with its content.
* Unfortunately, we can't animate width from `0` to `auto`.
* Therefore, we animate the `max-width` property instead.
* We assign a large enough number (1300px) to make sure it won't
* interfere with regular use cases, and give it more time to animate
* so that it hits its `auto` width approximately around the same time
* as the `opacity` transition finishes.
*/ max-width
450ms,
opacity 300ms;
.site-manager-wrapper-exit-active {
opacity: 0;
}

.site-view {
position: relative;
flex: 1 1 auto;
min-width: var(--site-view-min-width);
height: 100dvh;
border: 0px solid var(--site-manager-background-color);
transition: border-radius 300ms ease, border-width 300ms ease,
transform 300ms ease;
border-radius: 0;
border-width: 0;
.layoutResizeHandle {
width: 12px;
cursor: col-resize;
display: flex;
align-items: center;
justify-content: center;
}

.site-manager-wrapper + .site-view {
position: relative;
border-width: var(--site-manager-border-width);
border-left-width: 0;
.site-view-content {
border-radius: var(--site-manager-border-radius);
}
.layoutResizeHandle::before {
content: '';
display: block;
width: 2px;
height: 70%;
border-radius: 1px;
background: rgba(255, 255, 255, 0.18);
}

.site-manager-wrapper:not(
.site-manager-wrapper-exit-active,
.site-manager-wrapper-enter-active
)
+ .site-view:hover {
transform: scale(1.01);
.handleHidden {
visibility: hidden;
pointer-events: none;
}

.site-manager-wrapper-exit-active + .site-view {
border-radius: 0;
border-width: 0;
.site-view-content {
border-radius: 0;
}
.site-view {
height: 100%;
display: flex;
}

.site-view-content {
overflow: hidden;
transition: border-radius 300ms;
height: 100%;
border-radius: var(--site-manager-border-radius);
border: var(--site-manager-border-width) solid
var(--site-manager-background-color);
border-left-width: 0;
width: 100%;
transition: border-radius 300ms ease;
}

.site-view-overlay {
content: '';
display: block;
width: 100%;
height: 100dvh;
background-color: transparent;
position: absolute;
top: 0;
left: 0;
z-index: 1;
cursor: pointer;
}

/*
* Unfortunately we cannot use calc() in media queries.
*
* 1166px = --site-manager-width + --site-view-min-width
*
* This manual calculation ensures the site view gets hidden
* on smaller screens and never overflows out of the screen.
*/
@media (max-width: 1126px) {
.site-manager-wrapper + .site-view {
.layoutResizeHandle {
display: none;
}
.site-manager-wrapper,
.site-manager-wrapper-exit,
.site-manager-wrapper-enter-active {
width: 100%;
}
.site-manager-wrapper-exit-active + .site-view {
display: block;
}
}
Loading