Skip to content

Commit 7f915f7

Browse files
Merge pull request #4 from php-debugger/rewrite-introduction
rewrite introduction page with benefit grid
2 parents db3555d + f2123f8 commit 7f915f7

8 files changed

Lines changed: 289 additions & 24 deletions

File tree

‎docs/getting-started/introduction.md‎

Lines changed: 0 additions & 23 deletions
This file was deleted.
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
---
2+
title: Introduction
3+
---
4+
5+
import BenefitGrid from '@site/src/components/BenefitGrid';
6+
7+
[PHP Debugger](https://github.com/php-debugger/php-debugger) is a step debugger for PHP, and nothing else. Every other feature that
8+
normally ships alongside one — the profiler, the coverage collector, the tracer — has
9+
been left out. What remains is a debugger you can leave switched on permanently,
10+
because when you are not using it you can barely tell it is there.
11+
12+
<BenefitGrid />
13+
14+
## What's Next?
15+
16+
Ready to get started? Install PHP Debugger and try the quick start guide:
17+
18+
- [Installation Guide](./installation.md)
19+
- [Quick Start](./quick-start.md)
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
import styles from './styles.module.css';
2+
3+
const iconProps = {
4+
width: 26,
5+
height: 26,
6+
viewBox: '0 0 24 24',
7+
fill: 'none',
8+
stroke: 'currentColor',
9+
strokeWidth: 1.8,
10+
strokeLinecap: 'round',
11+
strokeLinejoin: 'round',
12+
};
13+
14+
const benefits = [
15+
{
16+
title: 'Near-zero overhead when idle',
17+
text: 'With no debug client connected, the debugger stays out of the way. A typical web request does around 1% more work than running with no debugger at all.',
18+
icon: (
19+
<svg {...iconProps}>
20+
<path d="M3 16a9 9 0 1 1 18 0" />
21+
<path d="M12 16l5.5-6" />
22+
<circle cx="12" cy="16" r="1.4" />
23+
</svg>
24+
),
25+
},
26+
{
27+
title: 'Cheap while you are attached',
28+
text: 'Keep your IDE connected all day. A session you are attached to but not actively stepping through still costs very little.',
29+
icon: (
30+
<svg {...iconProps}>
31+
<circle cx="12" cy="12" r="9" />
32+
<path d="M10 9v6M14 9v6" />
33+
</svg>
34+
),
35+
},
36+
{
37+
title: 'Drop-in compatible',
38+
text: 'Existing INI settings, IDE configurations, and helper functions keep working. In most projects there is almost nothing to migrate — just the line that loads the extension.',
39+
icon: (
40+
<svg {...iconProps}>
41+
<path d="M9 3v6M15 3v6" />
42+
<path d="M7 9h10v3a5 5 0 0 1-10 0V9z" />
43+
<path d="M12 17v4" />
44+
</svg>
45+
),
46+
},
47+
{
48+
title: 'Works with your editor',
49+
text: 'Full DBGp protocol support means any IDE or tool that speaks it just works — PhpStorm, VS Code, Neovim, and anything else in your setup.',
50+
icon: (
51+
<svg {...iconProps}>
52+
<rect x="3" y="4" width="18" height="13" rx="2" />
53+
<path d="M8 21h8M12 17v4" />
54+
</svg>
55+
),
56+
},
57+
{
58+
title: 'Everything you expect',
59+
text: 'Breakpoints and conditional breakpoints. Step over, into, and out. Inspect variables, objects, and arrays, and watch expressions change as you go.',
60+
icon: (
61+
<svg {...iconProps}>
62+
<rect x="6" y="7" width="12" height="13" rx="6" />
63+
<path d="M9 7.5V6a3 3 0 0 1 6 0v1.5" />
64+
<path d="M12 11v8" />
65+
<path d="M2.5 11.5H6M18 11.5h3.5" />
66+
<path d="M3.5 17.5 6 16M20.5 17.5 18 16" />
67+
</svg>
68+
),
69+
},
70+
{
71+
title: 'One job, done well',
72+
text: 'No profiler, no code coverage, no tracing. Step debugging is the only thing here, which is exactly why the rest of the time it costs you so little.',
73+
icon: (
74+
<svg {...iconProps}>
75+
<circle cx="12" cy="12" r="9" />
76+
<circle cx="12" cy="12" r="5" />
77+
<circle cx="12" cy="12" r="1.4" />
78+
</svg>
79+
),
80+
},
81+
{
82+
title: 'Nothing to install',
83+
text: 'Use it as a regular extension, or reach for a container image with the debugger compiled straight into the interpreter. Change one line of your Dockerfile and you are done.',
84+
icon: (
85+
<svg {...iconProps}>
86+
<path d="M12 2.5 20.5 7v10L12 21.5 3.5 17V7z" />
87+
<path d="M8.5 12l2.5 2.5 4.5-4.5" />
88+
</svg>
89+
),
90+
},
91+
{
92+
title: 'Ready the moment you are',
93+
text: 'However you install it, debugging is on by default and starts with every request. Set a breakpoint, hit your app, and the session is already there — no trigger to remember.',
94+
icon: (
95+
<svg {...iconProps}>
96+
<circle cx="12" cy="12" r="9" />
97+
<path d="M10 8.5l6 3.5-6 3.5z" />
98+
</svg>
99+
),
100+
},
101+
];
102+
103+
export default function BenefitGrid() {
104+
return (
105+
<div className={styles.grid}>
106+
{benefits.map(({title, text, icon}) => (
107+
<div key={title} className={styles.card}>
108+
<span className={styles.icon}>{icon}</span>
109+
<h3 className={styles.cardTitle}>{title}</h3>
110+
<p className={styles.cardText}>{text}</p>
111+
</div>
112+
))}
113+
</div>
114+
);
115+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
.grid {
2+
display: grid;
3+
grid-template-columns: repeat(2, minmax(0, 1fr));
4+
gap: 1rem;
5+
margin: 2rem 0 2.5rem;
6+
}
7+
8+
.card {
9+
border: 1px solid var(--phpdbg-card-border);
10+
border-radius: 10px;
11+
background: var(--phpdbg-surface);
12+
padding: 1.25rem 1.35rem 1.35rem;
13+
}
14+
15+
.icon {
16+
display: block;
17+
color: var(--ifm-color-primary);
18+
margin-bottom: 0.75rem;
19+
}
20+
21+
.cardTitle {
22+
font-size: 1.05rem;
23+
line-height: 1.3;
24+
margin: 0 0 0.4rem;
25+
}
26+
27+
.cardText {
28+
font-size: 0.9rem;
29+
line-height: 1.6;
30+
color: var(--ifm-color-emphasis-700);
31+
margin: 0;
32+
}
33+
34+
@media (max-width: 768px) {
35+
.grid {
36+
grid-template-columns: minmax(0, 1fr);
37+
}
38+
}

‎src/components/HomeCards/index.js‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ export default function HomeCards() {
6060
</svg>
6161
}
6262
title="Key Features"
63-
linkTo="/getting-started/introduction#key-features"
63+
linkTo="/getting-started/introduction"
6464
linkLabel="Explore all features">
6565
<ul className={styles.checkList}>
6666
{keyFeatures.map((feature) => (

‎src/css/custom.css‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,3 +149,10 @@ article a {
149149
.breadcrumbs__link {
150150
font-size: 0.8rem;
151151
}
152+
153+
/* Anchor that src/theme/DocItem/Content puts on the page title, so the "on this
154+
page" title entry has something to link to. Docusaurus only gives its own
155+
headings the offset that clears the sticky navbar, so set it here too. */
156+
#page-top {
157+
scroll-margin-top: calc(var(--ifm-navbar-height) + 1rem);
158+
}

‎src/theme/DocItem/Content/index.js‎

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import clsx from 'clsx';
2+
import {ThemeClassNames} from '@docusaurus/theme-common';
3+
import {useDoc} from '@docusaurus/plugin-content-docs/client';
4+
import Heading from '@theme/Heading';
5+
import MDXContent from '@theme/MDXContent';
6+
7+
/* Ejected from @docusaurus/theme-classic so the synthetic page title can carry
8+
an anchor. The table of contents is built from h2/h3 headings only, so the
9+
title is never in it; without an id here there is nothing for the "on this
10+
page" entry to link to. src/theme/TOC prepends that entry and targets this id. */
11+
export const PAGE_TOP_ID = 'page-top';
12+
13+
/* Docusaurus renders a "synthetic title" from front matter only when the page
14+
has not asked to hide it and the content does not already open with its own
15+
h1. src/theme/TOC repeats this test, so keep the two in step. */
16+
function useSyntheticTitle() {
17+
const {metadata, frontMatter, contentTitle} = useDoc();
18+
const shouldRender =
19+
!frontMatter.hide_title && typeof contentTitle === 'undefined';
20+
if (!shouldRender) {
21+
return null;
22+
}
23+
return metadata.title;
24+
}
25+
26+
export default function DocItemContent({children}) {
27+
const syntheticTitle = useSyntheticTitle();
28+
return (
29+
<div className={clsx(ThemeClassNames.docs.docMarkdown, 'markdown')}>
30+
{syntheticTitle && (
31+
<header id={PAGE_TOP_ID}>
32+
<Heading as="h1">{syntheticTitle}</Heading>
33+
</header>
34+
)}
35+
<MDXContent>{children}</MDXContent>
36+
</div>
37+
);
38+
}

‎src/theme/TOCItems/Tree/index.js‎

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import React from 'react';
2+
import Link from '@docusaurus/Link';
3+
import {useDoc} from '@docusaurus/plugin-content-docs/client';
4+
import {PAGE_TOP_ID} from '@theme/DocItem/Content';
5+
6+
/* Ejected from @docusaurus/theme-classic to put the page title at the top of
7+
the table of contents. Docusaurus builds the list from h2/h3 headings alone,
8+
so the title -- rendered as a synthetic h1 from front matter -- never appears.
9+
10+
Ejected rather than wrapped because the component recurses into itself, so a
11+
wrapper cannot reach inside the list it renders. Both the desktop sidebar and
12+
the mobile "on this page" dropdown render through here, so they stay in step. */
13+
14+
/* Docusaurus only renders a synthetic title when the page has not hidden it and
15+
the content does not already open with its own h1. src/theme/DocItem/Content
16+
makes the same test before adding the anchor; keep the two in step. */
17+
function useSyntheticTitle() {
18+
const {metadata, frontMatter, contentTitle} = useDoc();
19+
if (frontMatter.hide_title || typeof contentTitle !== 'undefined') {
20+
return null;
21+
}
22+
return metadata.title;
23+
}
24+
25+
/* A plain <a>, not a Docusaurus <Link>, on purpose. The build-time broken-anchor
26+
checker inspects <Link> only, and derives the valid anchors from the page's
27+
headings -- it cannot see an id added by a theme component, so a <Link> here
28+
reports a broken anchor on every page that has a title. */
29+
function PageTitleItem({linkClassName}) {
30+
const title = useSyntheticTitle();
31+
if (!title) {
32+
return null;
33+
}
34+
return (
35+
<li>
36+
<a className={linkClassName ?? undefined} href={`#${PAGE_TOP_ID}`}>
37+
{title}
38+
</a>
39+
</li>
40+
);
41+
}
42+
43+
function TOCItemTree({toc, className, linkClassName, isChild}) {
44+
if (!toc.length) {
45+
return null;
46+
}
47+
return (
48+
<ul className={isChild ? undefined : className}>
49+
{!isChild && <PageTitleItem linkClassName={linkClassName} />}
50+
{toc.map((heading) => (
51+
<li key={heading.id}>
52+
<Link
53+
to={`#${heading.id}`}
54+
className={linkClassName ?? undefined}
55+
// Developer provided the HTML, so assume it's safe.
56+
dangerouslySetInnerHTML={{__html: heading.value}}
57+
/>
58+
<TOCItemTree
59+
isChild
60+
toc={heading.children}
61+
className={className}
62+
linkClassName={linkClassName}
63+
/>
64+
</li>
65+
))}
66+
</ul>
67+
);
68+
}
69+
70+
// Memo only the tree root is enough
71+
export default React.memo(TOCItemTree);

0 commit comments

Comments
 (0)