Skip to content
Merged
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
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.7.75
1.7.76
Binary file added docs/screenshots/cron-create.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/screenshots/cron-detail.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/screenshots/cron.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/screenshots/git.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/screenshots/settings.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/screenshots/terminal-logs.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/screenshots/terminal-process-drawer.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions frontend/src/components/dashboard/grid/WidgetEditor.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,7 @@ export function WidgetEditor({

return (
<Drawer
flush
open
onOpenChange={(next) => { if (!next) onClose?.(); }}
title={title}
Expand Down
1 change: 1 addition & 0 deletions frontend/src/components/dashboard/grid/WidgetLibrary.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,7 @@ export function WidgetLibrary({ types = [], onAdd, onClose }) {

return (
<Drawer
flush
open
onOpenChange={(next) => { if (!next) onClose?.(); }}
title="Add a widget"
Expand Down
1 change: 1 addition & 0 deletions frontend/src/components/databases/EngineCatalogDrawer.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@ export default function EngineCatalogDrawer({

return (
<Drawer
flush
open={open}
onOpenChange={onOpenChange}
title="Add a database engine"
Expand Down
1 change: 1 addition & 0 deletions frontend/src/components/databases/EngineInstallDrawer.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,7 @@ export default function EngineInstallDrawer({ entry, open, onOpenChange, onInsta

return (
<Drawer
flush
open={open}
onOpenChange={(next) => { if (!next) setResult(null); onOpenChange(next); }}
title={result ? `${template.name} is installing` : `Install ${template.name}`}
Expand Down
29 changes: 13 additions & 16 deletions frontend/src/components/docker/ContainersTab.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import LogToolbar from '../log-viewer/LogToolbar';
import LogContent from '../log-viewer/LogContent';
import EmptyState from '../EmptyState';
import Modal from '@/components/Modal';
import { Drawer } from '@/components/ds';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Textarea } from '@/components/ui/textarea';
Expand Down Expand Up @@ -944,20 +945,17 @@ const ContainerLogsModal = ({ container, onClose }) => {
}

return (
<>
<div className="preview-drawer-backdrop" onClick={onClose} />
<aside className="preview-drawer">
<header className="preview-drawer-header">
<Box size={20} style={{ color: 'var(--accent-primary)' }} />
<div className="preview-drawer-title">
<h3>{getContainerName(container)}</h3>
<p className="preview-drawer-path">{getContainerImage(container)} - {shortId(containerId)}</p>
</div>
<button type="button" className="preview-drawer-close" onClick={onClose}>
<X size={18} />
</button>
</header>

// The shared DS drawer. (The container INSPECTOR next door stays on the
// `dx-*` classes — that is the Docker workspace's own language.)
<Drawer
open
onOpenChange={(open) => { if (!open) onClose(); }}
title={getContainerName(container)}
subtitle={`${getContainerImage(container)} · ${shortId(containerId)}`}
icon={<Box size={18} />}
width={520}
flush
>
<div className="preview-drawer-meta">
<div className="meta-item">
<span className="meta-label">Status</span>
Expand Down Expand Up @@ -1014,8 +1012,7 @@ const ContainerLogsModal = ({ container, onClose }) => {
searchPattern={appliedSearch}
/>
</div>
</aside>
</>
</Drawer>
);
};

Expand Down
19 changes: 18 additions & 1 deletion frontend/src/components/ds/Drawer.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,19 @@ import { cn } from '@/lib/utils';
// subtitle="server filesystem" icon={<FileIcon/>} width={760}>
// …body…
// </Drawer>
//
// The body is PADDED by default, to the same 18px inset as the header — a
// drawer that forgets to pad itself used to render every field flush against
// the panel edge, and enough of them forgot that the drawers stopped looking
// like one component. Pass `flush` for bodies that own their own regions
// (a form with a pinned footer, a two-pane catalog, an editor, a log tail):
//
// <Drawer flush …>
// <form className="sk-formdrawer__form"> ← pads its own scroll region
// <div className="sk-formdrawer__body">…</div>
// <footer>…</footer>
// </form>
// </Drawer>
export function Drawer({
open,
onOpenChange,
Expand All @@ -21,6 +34,8 @@ export function Drawer({
side = 'right',
headerExtra,
className,
bodyClassName,
flush = false,
children,
}) {
return (
Expand All @@ -39,7 +54,9 @@ export function Drawer({
</div>
{headerExtra}
</div>
<div className="sk-drawer__body">{children}</div>
<div className={cn('sk-drawer__body', flush && 'sk-drawer__body--flush', bodyClassName)}>
{children}
</div>
</SheetContent>
</Sheet>
);
Expand Down
55 changes: 55 additions & 0 deletions frontend/src/layouts/PageLayout.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { cn } from '@/lib/utils';
import { PageTopbar } from '@/components/ds';

// Shell for a STANDALONE page — one that owns its whole route rather than
// living inside a PageTopbar tab group. It is the exact same frame
// TabGroupLayout gives a group (see layouts/TabGroupLayout.jsx): the top bar
// pins flush to the top of the content region edge-to-edge, and the body
// scrolls beneath it, centered at the reading max-width.
//
// <PageLayout icon={<Clock size={18} />} title="Cron Jobs" actions={…}>
// …page body…
// </PageLayout>
//
// Rendering a PageTopbar inside the default padded `.page-container` instead
// (what every standalone page used to do by hand) leaves the bar floating in
// the middle of the content well with a gutter on every side — it carries the
// sidebar's surface and a bottom border precisely because it is meant to be
// chrome pinned to the frame, not the first row of the page body.
//
// `fill` hands the body the whole region to manage its own scrolling (log
// consoles, file trees, terminals); the default centers and pads it.
export default function PageLayout({
icon,
title,
meta,
tabs,
navLabel,
actions,
fill = false,
className,
topbarClassName,
contentClassName,
children,
}) {
return (
<div className={cn('page-container page-container--full-bleed sk-pageshell', className)}>
<PageTopbar
className={topbarClassName}
icon={icon}
title={title}
meta={meta}
tabs={tabs}
navLabel={navLabel}
actions={actions}
/>
<div className="sk-pageshell__content">
{fill ? (
<div className={cn('sk-pageshell__fill', contentClassName)}>{children}</div>
) : (
<div className={cn('sk-pageshell__inner', contentClassName)}>{children}</div>
)}
</div>
</div>
);
}
8 changes: 3 additions & 5 deletions frontend/src/pages/AppMap.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
Map as MapIcon, Layers, Compass, Network, Info,
} from 'lucide-react';
import { Tabs, TabsList, TabsTrigger } from '@/components/ui/tabs';
import { PageTopbar } from '@/components/ds';
import PageLayout from '../layouts/PageLayout';
import useTabParam from '../hooks/useTabParam';

const VIEWS = [
Expand Down Expand Up @@ -284,9 +284,7 @@ export default function AppMap() {
const view = VIEWS.find(v => v.id === activeView) || VIEWS[0];

return (
<div className="app-map">
<PageTopbar icon={<MapIcon size={18} />} title="App Map" />

<PageLayout className="app-map" icon={<MapIcon size={18} />} title="App Map">
<Tabs value={activeView} onValueChange={setActiveView}>
<TabsList>
{VIEWS.map(v => (
Expand Down Expand Up @@ -329,6 +327,6 @@ export default function AppMap() {
<Diagram viewId={activeView} />
</ReactFlowProvider>
</div>
</div>
</PageLayout>
);
}
28 changes: 16 additions & 12 deletions frontend/src/pages/CloudflareZoneSettings.jsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useState, useEffect, useCallback } from 'react';
import { useParams, Link } from 'react-router-dom';
import { Cloud, ShieldCheck, Lock, Gauge, Database, Wand2, Eraser, Flame, Zap, Network, HardDrive } from 'lucide-react';
import { PageTopbar } from '@/components/ds';
import PageLayout from '../layouts/PageLayout';
import CloudflareWafPanel from '../components/cloudflare/CloudflareWafPanel';
import WorkersPanel from '../components/cloudflare/WorkersPanel';
import TunnelsPanel from '../components/cloudflare/TunnelsPanel';
Expand Down Expand Up @@ -153,8 +153,12 @@ const CloudflareZoneSettings = () => {

if (error) {
return (
<div className="app-detail-page app-detail-page--wide cf-zone">
<PageTopbar icon={<Cloud size={18} />} title={crumbs} />
<PageLayout
className="cf-zone"
contentClassName="app-detail-page app-detail-page--wide"
icon={<Cloud size={18} />}
title={crumbs}
>
<div className="cf-zone__body">
<EmptyState
icon={Cloud}
Expand All @@ -166,21 +170,21 @@ const CloudflareZoneSettings = () => {
<Link to="/dns"><Button variant="ghost">Back to DNS Zones</Button></Link>
</div>
</div>
</div>
</PageLayout>
);
}

const tabGroups = groups.filter(g => g.settings?.length);
const firstTab = tabGroups[0]?.key || 'actions';

return (
<div className="app-detail-page app-detail-page--wide cf-zone">
<PageTopbar
icon={<Cloud size={18} />}
title={crumbs}
meta={zone?.provider_zone_id ? `Zone ${zone.provider_zone_id}` : undefined}
/>

<PageLayout
className="cf-zone"
contentClassName="app-detail-page app-detail-page--wide"
icon={<Cloud size={18} />}
title={crumbs}
meta={zone?.provider_zone_id ? `Zone ${zone.provider_zone_id}` : undefined}
>
<div className="cf-zone__body">
<Tabs defaultValue={firstTab} className="cf-zone__tabs">
<TabsList>
Expand Down Expand Up @@ -316,7 +320,7 @@ const CloudflareZoneSettings = () => {
variant="danger"
/>
)}
</div>
</PageLayout>
);
};

Expand Down
30 changes: 15 additions & 15 deletions frontend/src/pages/Coexistence.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import {
Eye, ArrowRightLeft, ServerCog, ShieldCheck, RadioTower, Upload,
RefreshCw, ArrowRight,
} from 'lucide-react';
import { PageTopbar, Pill } from '@/components/ds';
import { Pill } from '@/components/ds';
import PageLayout from '../layouts/PageLayout';
import { Button } from '@/components/ui/button';
import EmptyState from '@/components/EmptyState';
import api from '../services/api';
Expand Down Expand Up @@ -91,19 +92,18 @@ export default function Coexistence() {
const observed = servers.filter(isObserved);

return (
<div className="page-container coexistence">
<PageTopbar
icon={<RadioTower size={18} />}
title="Coexistence"
meta="Running alongside another panel"
actions={
<Button variant="outline" size="sm" onClick={load} disabled={loading}>
<RefreshCw size={15} />
Refresh
</Button>
}
/>

<PageLayout
className="coexistence"
icon={<RadioTower size={18} />}
title="Coexistence"
meta="Running alongside another panel"
actions={
<Button variant="outline" size="sm" onClick={load} disabled={loading}>
<RefreshCw size={15} />
Refresh
</Button>
}
>
<p className="coexistence__intro app-panel-hint">
ServerKit has three explicit adoption modes. Pick the one that matches where the
box is today — you can move between them as you migrate. The hard rule: keep exactly
Expand Down Expand Up @@ -212,6 +212,6 @@ export default function Coexistence() {
</div>
</section>
</div>
</div>
</PageLayout>
);
}
Loading