Skip to content
Draft
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 app/Notifications/PluginReviewChecksIncomplete.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ class PluginReviewChecksIncomplete extends Notification implements ShouldQueue
{
use Queueable;

private const DOCS_BASE = 'https://nativephp.com/docs/mobile/3/plugins';
private const DOCS_BASE = 'https://nativephp.com/docs/mobile/plugins';

/**
* @var array<string, array{label: string, passing_label: string, docs_url: string, docs_label: string}>
Expand Down
30 changes: 28 additions & 2 deletions app/Support/GitHub.php
Original file line number Diff line number Diff line change
Expand Up @@ -70,12 +70,38 @@ public function releases(): Collection
) ?? collect();
}

public function releasesAfter(string $version): Collection
/**
* Releases strictly after the given version, optionally capped below
* another version and its prereleases — without a cap, a versioned
* changelog would list the next major's releases too (4.0.0-rc.1
* compares greater than any 3.x tag).
*/
public function releasesAfter(string $version, ?string $before = null): Collection
{
$version = ltrim($version, 'v');
$ceiling = $before === null ? null : ltrim($before, 'v').'-dev';

return $this->releases()->filter(function (Release $release) use ($version, $ceiling) {
$tag = ltrim((string) $release->tag_name, 'v');

return version_compare($tag, $version, '>')
&& ($ceiling === null || version_compare($tag, $ceiling, '<'));
})->values();
}

/**
* Releases for the given version and everything later, including that
* prefix version itself and its prereleases. releasesAfter("4.0.0")
* would exclude 4.0.0-rc.1 because version_compare ranks prereleases
* below the release; "dev" ranks below every other prerelease marker,
* so a "-dev" floor admits alphas, betas, RCs and the release itself.
*/
public function releasesFrom(string $version): Collection
{
$floor = ltrim($version, 'v').'-dev';

return $this->releases()->filter(
fn (Release $release) => version_compare(ltrim((string) $release->tag_name, 'v'), $version, '>')
fn (Release $release) => version_compare(ltrim((string) $release->tag_name, 'v'), $floor, '>=')
)->values();
}

Expand Down
16 changes: 11 additions & 5 deletions app/Support/GitHub/Release.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,25 +49,31 @@ public function getBodyForMarkdown(): string
{
$body = $this->body;

// Convert any URLs to Markdown links
// Convert bare URLs to Markdown links. Release bodies can already
// contain Markdown links, so URLs preceded by "(" or "<" are left
// alone — wrapping those again nests link syntax and the page ends
// up rendering both the text and the URL. Closing ")" and "]" are
// excluded from the URL so a link's own delimiters are never
// swallowed into it.
if ($this->convertLinks) {
$body = preg_replace(
'/https?:\/\/[^\s]+\/pull\/(\d+)/',
'/(?<![(<])https?:\/\/[^\s)\]]+\/pull\/(\d+)/',
'[#$1]($0)',
$body
);

$body = preg_replace(
'/(https?:\/\/(?![^\s]+\/pull\/\d+)[^\s]+)/',
'/(?<![(<])(https?:\/\/(?![^\s]+\/pull\/\d+)[^\s)\]]+)/',
'[$1]($1)',
$body
);
}

// Change any @ tags to markdown links to GitHub
// Change any @ tags to markdown links to GitHub, unless they are
// already the text of a Markdown link or part of an email address.
if ($this->withUserLinks) {
$body = preg_replace(
'/@([a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?)/',
'/(?<![\w\[])@([a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?)/',
'[@$1](https://github.com/$1)',
$body
);
Expand Down
5 changes: 3 additions & 2 deletions config/docs.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

'latest_versions' => [
'desktop' => 2,
'mobile' => 3,
'mobile' => 4,
],

/*
Expand All @@ -32,7 +32,7 @@

'prerelease_versions' => [
'desktop' => [],
'mobile' => [4],
'mobile' => [],
],

/*
Expand All @@ -53,6 +53,7 @@
4 => [
'the-basics/native-components' => 'the-basics/native-ui',
'the-basics/dialog' => 'the-basics/dialogs',
'the-basics/web-view' => 'edge-components/web-view',
'getting-started/deployment' => 'publishing/introduction',
'plugins/vibe' => 'digging-deeper/websockets',

Expand Down
9 changes: 9 additions & 0 deletions resources/css/app.css
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,15 @@
@source '../../storage/framework/views/*.php';
@source '../../vendor/filament/**/*.blade.php';

/*
* flux.css pulls in the flux-pro stubs, and the carousel slide stub declares
* `[snap="mandatory"_&]:snap-always` — one bracket short, so it compiles to the
* invalid selector `snap="mandatory" &` and Lightning CSS warns on every build.
* We don't use <flux:carousel>, so skip that stub. Drop this once Flux fixes it
* upstream; remove it first if we ever adopt the carousel.
*/
@source not '../../vendor/livewire/flux-pro/stubs/resources/views/flux/carousel';

/* Safelist classes used in dynamic content (blog posts from database) */
@source inline("grid-cols-1 grid-cols-2 grid-cols-3 grid-cols-4 grid-cols-5 grid-cols-6");

Expand Down
24 changes: 22 additions & 2 deletions resources/js/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -113,10 +113,28 @@ Alpine.data('countdown', (iso) => ({
}, // tidy up
}))

// Which platform path the homepage is showing: 'mobile' | 'desktop'.
// Shared across sections (hero, explainer) and remembered between visits.
// Registered on alpine:init because $persist only exists once Livewire has
// registered Alpine's plugins.
document.addEventListener('alpine:init', () => {
Alpine.store('platform', {
current: Alpine.$persist('mobile').as('nativephpPlatform'),
is(name) {
return this.current === name
},
select(name) {
this.current = name
},
})
})

Livewire.start()

// Docsearch
const docsPathMatch = window.location.pathname.match(/^\/docs\/(desktop|mobile)\/(\d+)/)
const docsPathMatch = window.location.pathname.match(
/^\/docs\/(desktop|mobile)\/(\d+)/,
)
const docsearchOptions = {
appId: 'ZNII9QZ8WI',
apiKey: '9be495a1aaf367b47c873d30a8e7ccf5',
Expand Down Expand Up @@ -146,7 +164,9 @@ docsearch({
// pressing Cmd+K only registers one handler (avoiding duplicate modals).
const mobileContainer = document.getElementById('docsearch-mobile')
if (mobileContainer) {
const desktopButton = document.querySelector('#docsearch-desktop .DocSearch-Button')
const desktopButton = document.querySelector(
'#docsearch-desktop .DocSearch-Button',
)
if (desktopButton) {
const mobileButton = desktopButton.cloneNode(true)
mobileContainer.appendChild(mobileButton)
Expand Down
2 changes: 1 addition & 1 deletion resources/views/components/blog/ad-rotation.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ class="group relative z-0 grid place-items-center overflow-hidden rounded-2xl bg

{{-- Price hint --}}
<div class="mt-2 text-xs text-sky-100">
Plans from $19/mo
Plans from $10/mo
</div>

{{-- Decorative stars --}}
Expand Down
4 changes: 2 additions & 2 deletions resources/views/components/docs/platform-switcher.blade.php
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
@php
$isMobile = request()->is('docs/mobile/*');
$mobileHref = '/docs/mobile/3';
$desktopHref = '/docs/desktop/2';
$mobileHref = '/docs/mobile/'.config('docs.latest_versions.mobile');
$desktopHref = '/docs/desktop/'.config('docs.latest_versions.desktop');
@endphp

<a
Expand Down
4 changes: 2 additions & 2 deletions resources/views/components/footer.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -356,7 +356,7 @@ class="flex flex-col items-start text-sm text-gray-500 dark:text-gray-400"
>
<li>
<a
href="/docs/mobile/3/getting-started/introduction"
href="/docs/mobile/getting-started/introduction"
class="inline-block px-px py-1.5 transition duration-300 will-change-transform hover:translate-x-1 hover:text-gray-700 dark:hover:text-gray-300"
>
Documentation
Expand Down Expand Up @@ -420,7 +420,7 @@ class="flex flex-col items-start text-sm text-gray-500 dark:text-gray-400"
>
<li>
<a
href="/docs/desktop/2/getting-started/introduction"
href="/docs/desktop/getting-started/introduction"
class="inline-block px-px py-1.5 transition duration-300 will-change-transform hover:translate-x-1 hover:text-gray-700 dark:hover:text-gray-300"
>
Documentation
Expand Down
18 changes: 9 additions & 9 deletions resources/views/components/home/announcements.blade.php
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
{{-- Announcements Section: Plugins (full width) + Bifrost (50%) & Course/Jump (50% stacked) --}}
{{-- Announcements Section: Jump/Course (50% stacked) & Bifrost (50%) + Plugins (full width) --}}
<section class="mt-4" aria-label="New announcements">
<div class="flex flex-col gap-4">
{{-- Plugins Announcement (Full Width) --}}
<x-home.plugins-announcement />

{{-- Course & Jump + Bifrost Row --}}
{{-- Jump & Course + Bifrost Row --}}
<div class="grid gap-4 xl:grid-cols-2">
{{-- Left Column - Course & Jump --}}
{{-- Left Column - Jump & Course --}}
<div class="grid gap-4 md:grid-cols-2 xl:grid-cols-1">
{{-- Course Card --}}
<x-home.course-card />

{{-- Jump Card --}}
<x-home.jump-card />

{{-- Course Card --}}
<x-home.course-card />
</div>

{{-- Bifrost Banner (Right - 50%) --}}
<x-home.bifrost-card />
</div>

{{-- Plugins Announcement (Full Width) --}}
<x-home.plugins-announcement />
</div>
</section>
17 changes: 11 additions & 6 deletions resources/views/components/home/bifrost-card.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
target="_blank"
rel="noopener noreferrer"
onclick="fathom.trackEvent('bifrost_card_click');"
class="group relative block h-full overflow-hidden rounded-2xl bg-gradient-to-br from-sky-500/20 via-indigo-500/10 to-purple-500/20 p-0.5 ring-1 ring-zinc-200/50 transition duration-300 hover:ring-sky-400/50 dark:ring-sky-500/30"
class="group relative block h-full overflow-hidden rounded-2xl bg-gradient-to-br from-sky-500/20 via-indigo-500/10 to-purple-500/20 p-0.5 ring-1 ring-zinc-200/50 transition-shadow duration-300 hover:ring-sky-400/50 dark:ring-sky-500/30"
x-init="
() => {
motion.inView($el, (element) => {
Expand All @@ -15,15 +15,15 @@ class="group relative block h-full overflow-hidden rounded-2xl bg-gradient-to-br
y: 0,
autoAlpha: 1,
duration: 0.6,
delay: 0.1,
delay: 0.3,
ease: 'power2.out',
},
)
})
}
"
>
<div class="relative flex h-full flex-col overflow-hidden rounded-xl bg-gradient-to-br from-[#F9F9F9] via-white to-[#F9F9F9] p-6 md:p-8 dark:from-slate-950 dark:via-slate-900 dark:to-slate-950">
<div class="relative flex h-full flex-col overflow-hidden rounded-xl bg-gradient-to-br from-[#F9F9F9] via-white to-[#F9F9F9] p-5 md:p-6 dark:from-slate-950 dark:via-slate-900 dark:to-slate-950">
{{-- Animated glow --}}
<div
x-init="
Expand All @@ -38,7 +38,7 @@ class="group relative block h-full overflow-hidden rounded-2xl bg-gradient-to-br
})
}
"
class="pointer-events-none absolute -right-10 -top-10 size-40 rounded-full bg-sky-500/20 blur-[50px] transition duration-500 group-hover:bg-sky-500/30"
class="pointer-events-none absolute -right-10 -top-10 size-40 rounded-full bg-sky-500/20 blur-[50px] transition-colors duration-500 group-hover:bg-sky-500/30"
aria-hidden="true"
></div>
<div
Expand All @@ -54,7 +54,7 @@ class="pointer-events-none absolute -right-10 -top-10 size-40 rounded-full bg-sk
})
}
"
class="pointer-events-none absolute -bottom-10 -left-10 size-32 rounded-full bg-purple-500/15 blur-[40px] transition duration-500 group-hover:bg-purple-500/25"
class="pointer-events-none absolute -bottom-10 -left-10 size-32 rounded-full bg-purple-500/15 blur-[40px] transition-colors duration-500 group-hover:bg-purple-500/25"
aria-hidden="true"
></div>

Expand Down Expand Up @@ -86,8 +86,13 @@ class="pointer-events-none absolute -bottom-10 -left-10 size-32 rounded-full bg-
<x-illustrations.bifrost-diagram />
</div>

{{-- Price hint --}}
<p class="mt-4 text-xs text-gray-500 dark:text-slate-500">
Plans start from just $10/month
</p>

{{-- CTA --}}
<div class="mt-4 flex items-center gap-2 text-sm font-medium text-sky-600 transition duration-300 group-hover:text-sky-500 dark:text-sky-400 dark:group-hover:text-sky-300">
<div class="mt-1.5 flex items-center gap-2 text-sm font-medium text-sky-600 transition duration-300 group-hover:text-sky-500 dark:text-sky-400 dark:group-hover:text-sky-300">
<span>Ship it!</span>
<svg
class="size-4 transition duration-300 group-hover:translate-x-1"
Expand Down
6 changes: 3 additions & 3 deletions resources/views/components/home/course-card.blade.php
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{{-- Course Card - NativePHP Masterclass --}}
<a
href="{{ route('course') }}"
class="group relative block h-full overflow-hidden rounded-2xl bg-gradient-to-br from-emerald-500/20 via-teal-500/10 to-cyan-500/20 p-0.5 ring-1 ring-zinc-200/50 transition duration-300 hover:ring-emerald-400/50 dark:ring-emerald-500/30"
class="group relative block h-full overflow-hidden rounded-2xl bg-gradient-to-br from-emerald-500/20 via-teal-500/10 to-cyan-500/20 p-0.5 ring-1 ring-zinc-200/50 transition-shadow duration-300 hover:ring-emerald-400/50 dark:ring-emerald-500/30"
x-init="
() => {
motion.inView($el, (element) => {
Expand All @@ -12,7 +12,7 @@ class="group relative block h-full overflow-hidden rounded-2xl bg-gradient-to-br
y: 0,
autoAlpha: 1,
duration: 0.6,
delay: 0.2,
delay: 0.15,
ease: 'power2.out',
},
)
Expand All @@ -35,7 +35,7 @@ class="group relative block h-full overflow-hidden rounded-2xl bg-gradient-to-br
})
}
"
class="pointer-events-none absolute -right-10 -top-10 size-32 rounded-full bg-emerald-500/20 blur-[40px] transition duration-500 group-hover:bg-emerald-500/30"
class="pointer-events-none absolute -right-10 -top-10 size-32 rounded-full bg-emerald-500/20 blur-[40px] transition-colors duration-500 group-hover:bg-emerald-500/30"
aria-hidden="true"
></div>

Expand Down
Loading
Loading