diff --git a/app/Http/Controllers/AboutPageController.php b/app/Http/Controllers/AboutPageController.php
deleted file mode 100644
index d174e550..00000000
--- a/app/Http/Controllers/AboutPageController.php
+++ /dev/null
@@ -1,16 +0,0 @@
- About::first()?->content,
- ]);
- }
-}
diff --git a/app/Http/Controllers/Admin/AboutController.php b/app/Http/Controllers/Admin/AboutController.php
deleted file mode 100644
index cc5be0ad..00000000
--- a/app/Http/Controllers/Admin/AboutController.php
+++ /dev/null
@@ -1,32 +0,0 @@
- $about,
- ]);
- }
-
- public function update(Request $request): RedirectResponse
- {
- $about = About::firstOrNew();
- $about->content = $request->input('content');
- $about->save();
-
- return redirect()->route('admin.about.show');
- }
-}
diff --git a/app/Http/Controllers/Admin/TokensController.php b/app/Http/Controllers/Admin/TokensController.php
index 02b9660c..1b5348f9 100644
--- a/app/Http/Controllers/Admin/TokensController.php
+++ b/app/Http/Controllers/Admin/TokensController.php
@@ -6,9 +6,7 @@ namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Models\MicropubToken;
-use App\Services\TokenService;
use Illuminate\Http\RedirectResponse;
-use Illuminate\Http\Request;
use Illuminate\View\View;
class TokensController extends Controller
@@ -23,36 +21,6 @@ class TokensController extends Controller
return view('admin.tokens.index', compact('tokens'));
}
- /**
- * Show the form to manually generate a new Micropub token.
- *
- * This is for clients (e.g. iA Writer) that don't support the IndieAuth
- * PKCE flow and instead expect to be given a token directly.
- */
- public function create(): View
- {
- return view('admin.tokens.create');
- }
-
- /**
- * Manually generate a new Micropub token.
- */
- public function store(Request $request): RedirectResponse
- {
- $validated = $request->validate([
- 'client_id' => 'required|string',
- 'scope' => 'required|array|min:1',
- ]);
-
- $token = resolve(TokenService::class)->getNewToken([
- 'me' => config('app.url'),
- 'client_id' => $validated['client_id'],
- 'scope' => implode(' ', $validated['scope']),
- ]);
-
- return redirect('/admin/tokens')->with('new_token', $token);
- }
-
/**
* Revoke a Micropub token.
*/
diff --git a/app/Http/Controllers/MicropubController.php b/app/Http/Controllers/MicropubController.php
index 72242150..2df5d432 100644
--- a/app/Http/Controllers/MicropubController.php
+++ b/app/Http/Controllers/MicropubController.php
@@ -70,9 +70,7 @@ class MicropubController extends Controller
'error' => 'invalid_request',
'error_description' => 'No known note with given ID',
], 404);
- } catch (MicropubUnsupportedModelException $e) {
- report($e);
-
+ } catch (MicropubUnsupportedModelException) {
return response()->json([
'error' => 'invalid',
'error_description' => 'This implementation currently only supports the updating of notes',
@@ -82,16 +80,12 @@ class MicropubController extends Controller
'error' => 'invalid_request',
'error_description' => $e->getMessage(),
], 400);
- } catch (MicropubHandlerException $e) {
- report($e);
-
+ } catch (MicropubHandlerException) {
return response()->json([
'error' => 'unsupported_operation',
'error_description' => 'The request could not be processed by this server',
], 500);
- } catch (\Throwable $e) {
- report($e);
-
+ } catch (\Exception $e) {
return response()->json([
'error' => 'server_error',
'error_description' => 'An error occurred processing the request',
diff --git a/app/Models/About.php b/app/Models/About.php
deleted file mode 100644
index 26b6a719..00000000
--- a/app/Models/About.php
+++ /dev/null
@@ -1,13 +0,0 @@
- [
'source' => 'title',
- 'includeTrashed' => true,
],
];
}
@@ -94,13 +93,6 @@ class Article extends Model
);
}
- protected function uri(): Attribute
- {
- return Attribute::get(
- get: fn () => config('app.url').$this->link,
- );
- }
-
/**
* Scope a query to only include articles from a particular year/month.
*/
diff --git a/app/Services/ArticleService.php b/app/Services/ArticleService.php
index ab91f9e4..2372ffb7 100644
--- a/app/Services/ArticleService.php
+++ b/app/Services/ArticleService.php
@@ -8,29 +8,12 @@ use App\Models\Article;
class ArticleService
{
- /**
- * @throws \InvalidArgumentException if a published article already has this title
- */
public function create(array $data): Article
{
- $attributes = [
+ return Article::create([
'title' => $data['name'],
'main' => $data['content'],
'published' => ($data['post-status'] ?? null) !== 'draft',
- ];
-
- $existing = Article::where('title', $data['name'])->first();
-
- if ($existing !== null) {
- if ($existing->published) {
- throw new \InvalidArgumentException("An article titled \"{$data['name']}\" has already been published");
- }
-
- $existing->update($attributes);
-
- return $existing;
- }
-
- return Article::create($attributes);
+ ]);
}
}
diff --git a/app/Services/Micropub/Handlers/EntryHandler.php b/app/Services/Micropub/Handlers/EntryHandler.php
index d79a0418..48bbb550 100644
--- a/app/Services/Micropub/Handlers/EntryHandler.php
+++ b/app/Services/Micropub/Handlers/EntryHandler.php
@@ -37,7 +37,7 @@ class EntryHandler implements MicropubHandlerInterface
$location = match (true) {
isset($dataArray['like-of']) => resolve(LikeService::class)->create($dataArray)->url,
isset($dataArray['bookmark-of']) => resolve(BookmarkService::class)->create($dataArray)->uri,
- isset($dataArray['name']) => resolve(ArticleService::class)->create($dataArray)->uri,
+ isset($dataArray['name']) => resolve(ArticleService::class)->create($dataArray)->link,
default => resolve(NoteService::class)->create($dataArray)->uri,
};
diff --git a/database/factories/AboutFactory.php b/database/factories/AboutFactory.php
deleted file mode 100644
index 5af2532e..00000000
--- a/database/factories/AboutFactory.php
+++ /dev/null
@@ -1,24 +0,0 @@
-
- */
-class AboutFactory extends Factory
-{
- /**
- * Define the model's default state.
- *
- * @return array
- */
- public function definition(): array
- {
- return [
- 'content' => $this->faker->paragraph,
- ];
- }
-}
diff --git a/database/migrations/2026_08_28_142830_create_about_table.php b/database/migrations/2026_08_28_142830_create_about_table.php
deleted file mode 100644
index 15ab6f84..00000000
--- a/database/migrations/2026_08_28_142830_create_about_table.php
+++ /dev/null
@@ -1,28 +0,0 @@
-id();
- $table->text('content');
- $table->timestamps();
- });
- }
-
- /**
- * Reverse the migrations.
- */
- public function down(): void
- {
- Schema::dropIfExists('about');
- }
-};
diff --git a/public/assets/css/app.css b/public/assets/css/app.css
index e255209f..5c50c0b4 100644
--- a/public/assets/css/app.css
+++ b/public/assets/css/app.css
@@ -1,2 +1,2 @@
-@layer reset{*,:before,:after{box-sizing:border-box}html{-webkit-text-size-adjust:none;-moz-text-size-adjust:none;text-size-adjust:none}body,h1,h2,h3,h4,p,figure,blockquote,dl,dd{margin-block-end:0}ul[role=list],ol[role=list]{list-style:none}body{line-height:1.5}h1,h2,h3,h4,button,input,label{line-height:1.1}h1,h2,h3,h4{text-wrap:balance}a:not([class]){text-decoration-skip-ink:auto;color:currentColor}img,picture{max-width:100%;height:auto;display:block}input,button,textarea,select{font-family:inherit;font-size:inherit}textarea:not([rows]){min-height:10em}:target{scroll-margin-block:5ex}@media (prefers-reduced-motion){::view-transition-group(*),::view-transition-old(*),::view-transition-new(*){animation:none!important}}}@layer base{:root{color-scheme:light dark;--primary-hue:307;--clr-text:light-dark(oklch(5% .1 var(--primary-hue)),oklch(100% .1 var(--primary-hue)));--clr-background:light-dark(oklch(100% .1 var(--primary-hue)),oklch(15% .15 var(--primary-hue)));--clr-border:light-dark(oklch(90% .2 var(--primary-hue)),oklch(25% .1 var(--primary-hue)));--clr-snow-fall:light-dark(oklch(25% .15 var(--primary-hue)),oklch(85% .2 var(--primary-hue)))}body{background-color:var(--clr-background);color:var(--clr-text)}:root{--border-radius:8px}html{font-size:125%}body{grid-template-rows:min-content 1fr min-content;grid-template-columns:1fr min(80ch,80vw) 1fr;padding-inline:4vw;display:grid;&>header{grid-column:1/-1}&>main{grid-column:1/-1;grid-template-columns:subgrid;display:grid;&>*{grid-column:2/3}&>.full-bleed{grid-column:1/-1}}&>footer{grid-column:1/-1;margin-block-start:2ex;& search form{flex-direction:row;align-items:center;gap:1ex;display:flex;& input{min-width:0}}}}.h-feed{flex-direction:column;gap:2ex;display:flex}}@layer components{.h-entry{& pre{padding:1rem}}body>header{grid-template-rows:auto auto;grid-template-columns:auto 1fr auto;align-items:baseline;gap:1rem;display:grid;& a{text-decoration:none}& h1{text-wrap:nowrap;margin:0}& nav{flex-direction:row;gap:.5rem;display:flex;@media screen and (width<=1100px){flex-wrap:wrap;grid-area:2/1/auto/span 3}}}.note{border:1px solid var(--clr-border);border-radius:var(--border-radius);flex-direction:column;padding:1ex 2ex;display:flex;& .e-content{&>p:first-child{margin-block-start:0}& .u-photo{width:100%;height:auto}}& .syndication-links{flex-direction:row;gap:1ex;display:flex;& svg{border-radius:4px;width:auto;height:1lh}}}.reply-to{border:1px solid var(--clr-border);border-top-left-radius:var(--border-radius);border-top-right-radius:var(--border-radius);border-bottom:none;margin-inline:2ex;padding-inline:1ex;font-size:85%}.pagination{flex-direction:row;justify-content:center;align-items:center;display:flex;& div{flex-direction:row;gap:1ex;display:flex}}#theme-selector{background-color:color-mix(in oklch, var(--clr-border) 40%, transparent);border-radius:999px;justify-self:start;align-items:center;gap:.25rem;padding:.25rem;display:flex;& button{appearance:none;color:inherit;background:0 0;border:none;border-radius:999px;place-items:center;padding:.375rem;transition:background-color .2s,color .2s;display:grid;&:hover{cursor:pointer}&:focus-visible{outline:2px solid var(--clr-text);outline-offset:2px}& svg{fill:currentColor;width:1.5rem;height:1.5rem}@media (hover:hover){&:hover{background-color:color-mix(in oklch, var(--clr-border) 70%, transparent)}&[aria-pressed=true]:hover{background-color:var(--clr-text)}}&[aria-pressed=true]{background-color:var(--clr-text);color:var(--clr-background)}}}.sr-only{clip-path:inset(50%);white-space:nowrap;border:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}::view-transition-group(changing-theme){animation-duration:1.25s}::view-transition-new(changing-theme),::view-transition-old(changing-theme){mix-blend-mode:normal}::view-transition-new(changing-theme){animation-name:reveal}::view-transition-old(changing-theme){animation:none}@keyframes reveal{0%{clip-path:polygon(-30% 0,-30% 0,-15% 100%,-10% 115%)}to{clip-path:polygon(-30% 0,130% 0,115% 100%,-10% 115%)}}.token-list-wrapper{border:1px solid var(--clr-border);background:light-dark(oklch(99% .02 var(--primary-hue)),oklch(22% .05 var(--primary-hue)));border-radius:16px;margin-block-start:1em;overflow:auto hidden}.token-list{table-layout:fixed;border-collapse:collapse;width:100%;min-width:640px;& th,& td{text-align:left;border-bottom:1px solid var(--clr-border);vertical-align:middle;padding:.9em 1.2em}& th{text-transform:uppercase;letter-spacing:.08em;opacity:.7;font-size:.8em;font-weight:700}& tr.is-revoked{opacity:.6}& a{word-break:break-all}& th:first-child,& td:first-child{width:31%}& th:nth-child(2),& td:nth-child(2){width:24%}& th:nth-child(3),& td:nth-child(3){width:15%}& th:nth-child(4),& td:nth-child(4){width:16%}& th:nth-child(5),& td:nth-child(5){text-align:right;width:14%}& tr:last-child td{border-bottom:none}}.scope-chips{flex-wrap:wrap;gap:.4em;display:flex}.scope-chip{background:light-dark(oklch(92% .05 var(--primary-hue)),oklch(35% .08 var(--primary-hue)));white-space:nowrap;border-radius:999px;padding:.2em .7em;font-size:.85em;display:inline-block}.badge,.token-list button.revoke{white-space:nowrap;border-radius:999px;align-items:center;gap:.4em;padding:.3em .8em;font-size:.85em;font-weight:600;line-height:1.5;display:inline-flex}.badge{background:0 0;border:1px solid}.badge-active{color:light-dark(oklch(35% .16 145),oklch(75% .16 145));& .dot{background:currentColor;border-radius:50%;width:6px;height:6px}}.badge-revoked{color:var(--clr-text);opacity:.7}.token-list button.revoke{color:light-dark(oklch(30% .15 25),oklch(92% .12 25));cursor:pointer;background:light-dark(oklch(92% .15 25),oklch(30% .12 25));border:1px solid #0000;transition:background-color .15s}.token-list button.revoke:hover{background:light-dark(oklch(80% .2 25),oklch(45% .18 25))}.token-reveal{border:1px solid var(--clr-border);background:light-dark(oklch(96% .08 145),oklch(28% .08 145));border-radius:16px;margin-block-end:1em;padding:1em 1.2em;& input{border:1px solid var(--clr-border);border-radius:8px;width:100%;padding:.5em .7em;font-family:monospace}}.scope-checkboxes{flex-wrap:wrap;align-items:center;gap:1em;display:flex}}
+@layer reset{*,:before,:after{box-sizing:border-box}html{-webkit-text-size-adjust:none;-moz-text-size-adjust:none;text-size-adjust:none}body,h1,h2,h3,h4,p,figure,blockquote,dl,dd{margin-block-end:0}ul[role=list],ol[role=list]{list-style:none}body{line-height:1.5}h1,h2,h3,h4,button,input,label{line-height:1.1}h1,h2,h3,h4{text-wrap:balance}a:not([class]){text-decoration-skip-ink:auto;color:currentColor}img,picture{max-width:100%;height:auto;display:block}input,button,textarea,select{font-family:inherit;font-size:inherit}textarea:not([rows]){min-height:10em}:target{scroll-margin-block:5ex}@media (prefers-reduced-motion){::view-transition-group(*),::view-transition-old(*),::view-transition-new(*){animation:none!important}}}@layer base{:root{color-scheme:light dark;--primary-hue:307;--clr-text:light-dark(oklch(5% .1 var(--primary-hue)),oklch(100% .1 var(--primary-hue)));--clr-background:light-dark(oklch(100% .1 var(--primary-hue)),oklch(15% .15 var(--primary-hue)));--clr-border:light-dark(oklch(90% .2 var(--primary-hue)),oklch(25% .1 var(--primary-hue)));--clr-snow-fall:light-dark(oklch(25% .15 var(--primary-hue)),oklch(85% .2 var(--primary-hue)))}body{background-color:var(--clr-background);color:var(--clr-text)}:root{--border-radius:8px}html{font-size:125%}body{grid-template-rows:min-content 1fr min-content;grid-template-columns:1fr min(80ch,80vw) 1fr;padding-inline:4vw;display:grid;&>header{grid-column:1/-1}&>main{grid-column:1/-1;grid-template-columns:subgrid;display:grid;&>*{grid-column:2/3}&>.full-bleed{grid-column:1/-1}}&>footer{grid-column:1/-1;margin-block-start:2ex;& search form{flex-direction:row;align-items:center;gap:1ex;display:flex;& input{min-width:0}}}}.h-feed{flex-direction:column;gap:2ex;display:flex}}@layer components{.h-entry{& pre{padding:1rem}}body>header{grid-template-rows:auto auto;grid-template-columns:auto 1fr auto;align-items:baseline;gap:1rem;display:grid;& a{text-decoration:none}& h1{text-wrap:nowrap;margin:0}& nav{flex-direction:row;gap:.5rem;display:flex;@media screen and (width<=1100px){flex-wrap:wrap;grid-area:2/1/auto/span 3}}}.note{border:1px solid var(--clr-border);border-radius:var(--border-radius);flex-direction:column;padding:1ex 2ex;display:flex;& .e-content{&>p:first-child{margin-block-start:0}& .u-photo{width:100%;height:auto}}& .syndication-links{flex-direction:row;gap:1ex;display:flex;& svg{border-radius:4px;width:auto;height:1lh}}}.reply-to{border:1px solid var(--clr-border);border-top-left-radius:var(--border-radius);border-top-right-radius:var(--border-radius);border-bottom:none;margin-inline:2ex;padding-inline:1ex;font-size:85%}.pagination{flex-direction:row;justify-content:center;align-items:center;display:flex;& div{flex-direction:row;gap:1ex;display:flex}}#theme-selector{background-color:color-mix(in oklch, var(--clr-border) 40%, transparent);border-radius:999px;justify-self:start;align-items:center;gap:.25rem;padding:.25rem;display:flex;& button{appearance:none;color:inherit;background:0 0;border:none;border-radius:999px;place-items:center;padding:.375rem;transition:background-color .2s,color .2s;display:grid;&:hover{cursor:pointer}&:focus-visible{outline:2px solid var(--clr-text);outline-offset:2px}& svg{fill:currentColor;width:1.5rem;height:1.5rem}@media (hover:hover){&:hover{background-color:color-mix(in oklch, var(--clr-border) 70%, transparent)}&[aria-pressed=true]:hover{background-color:var(--clr-text)}}&[aria-pressed=true]{background-color:var(--clr-text);color:var(--clr-background)}}}.sr-only{clip-path:inset(50%);white-space:nowrap;border:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}::view-transition-group(changing-theme){animation-duration:1.25s}::view-transition-new(changing-theme),::view-transition-old(changing-theme){mix-blend-mode:normal}::view-transition-new(changing-theme){animation-name:reveal}::view-transition-old(changing-theme){animation:none}@keyframes reveal{0%{clip-path:polygon(-30% 0,-30% 0,-15% 100%,-10% 115%)}to{clip-path:polygon(-30% 0,130% 0,115% 100%,-10% 115%)}}.token-list-wrapper{border:1px solid var(--clr-border);background:light-dark(oklch(99% .02 var(--primary-hue)),oklch(22% .05 var(--primary-hue)));border-radius:16px;margin-block-start:1em;overflow:auto hidden}.token-list{table-layout:fixed;border-collapse:collapse;width:100%;min-width:640px;& th,& td{text-align:left;border-bottom:1px solid var(--clr-border);vertical-align:middle;padding:.9em 1.2em}& th{text-transform:uppercase;letter-spacing:.08em;opacity:.7;font-size:.8em;font-weight:700}& tr.is-revoked{opacity:.6}& a{word-break:break-all}& th:first-child,& td:first-child{width:31%}& th:nth-child(2),& td:nth-child(2){width:24%}& th:nth-child(3),& td:nth-child(3){width:15%}& th:nth-child(4),& td:nth-child(4){width:16%}& th:nth-child(5),& td:nth-child(5){text-align:right;width:14%}& tr:last-child td{border-bottom:none}}.scope-chips{flex-wrap:wrap;gap:.4em;display:flex}.scope-chip{background:light-dark(oklch(92% .05 var(--primary-hue)),oklch(35% .08 var(--primary-hue)));white-space:nowrap;border-radius:999px;padding:.2em .7em;font-size:.85em;display:inline-block}.badge,.token-list button.revoke{white-space:nowrap;border-radius:999px;align-items:center;gap:.4em;padding:.3em .8em;font-size:.85em;font-weight:600;line-height:1.5;display:inline-flex}.badge{background:0 0;border:1px solid}.badge-active{color:light-dark(oklch(35% .16 145),oklch(75% .16 145));& .dot{background:currentColor;border-radius:50%;width:6px;height:6px}}.badge-revoked{color:var(--clr-text);opacity:.7}.token-list button.revoke{color:light-dark(oklch(30% .15 25),oklch(92% .12 25));cursor:pointer;background:light-dark(oklch(92% .15 25),oklch(30% .12 25));border:1px solid #0000;transition:background-color .15s}.token-list button.revoke:hover{background:light-dark(oklch(80% .2 25),oklch(45% .18 25))}}
/*# sourceMappingURL=/assets/css/app.css.map */
diff --git a/public/assets/css/app.css.br b/public/assets/css/app.css.br
index 200e0d25..9ca789b9 100644
Binary files a/public/assets/css/app.css.br and b/public/assets/css/app.css.br differ
diff --git a/public/assets/css/app.css.map b/public/assets/css/app.css.map
index 6413bbe9..fef4e32c 100644
--- a/public/assets/css/app.css.map
+++ b/public/assets/css/app.css.map
@@ -1 +1 @@
-{"version":3,"mappings":"AACA,aCGE,uCAOA,oFASA,8DAMA,4CAMA,qBAKA,+CAMA,8BAMA,gEAMA,qDAQA,mEAOA,qCAKA,gCAKA,gCACE,wGDhFJ,YEAE,oaAwBA,kECxBA,0BAIA,oBAIA,iIAME,0BAIA,mEAKE,oBAIA,gCAKF,iDAIE,yEAME,uBAON,oDHjDF,kBGyDE,SACE,oBC1DF,wHAOE,yBAIA,+BAKA,gDAKE,kCAAqC,2CIrBzC,+HAOE,aACE,qCAIA,mCAMF,6DAKE,gDAQJ,4MH/BA,sFAME,+CENF,0LASE,gLAWE,uBAIA,qEAKA,mDAMA,qBACE,iFAIA,6DAKF,oFAOJ,uIAYA,iEAIA,kGAKA,4DAIA,qDAIA,mIDjFA,iNAWA,mFAME,6GAQA,6FAQA,2BAIA,yBAIA,4CAKA,8CAKA,8CAKA,8CAKA,+DAMA,uCAKF,kDAMA,qMAYA,0LAaA,uCAKA,sEAGE,uEAQF,gDAKA,kNAQA,0FAIA,wKAUE,iHASF","sources":["resources/css/app.css","resources/css/colours.css","resources/css/layout.css","resources/css/header.css","resources/css/reset.css","resources/css/pagination.css","resources/css/admin-tokens.css","resources/css/theme-selector.css","resources/css/notes.css"],"sourcesContent":["/* First thing we need to do is declare our cascade layers */\n@layer reset, base, components;\n\n@import url('reset.css');\n@import url('colours.css');\n@import url('layout.css');\n@import url('header.css');\n@import url('notes.css');\n@import url('pagination.css');\n@import url('theme-selector.css');\n@import url('admin-tokens.css');\n","@layer base {\n :root {\n color-scheme: light dark;\n\n --primary-hue: 307;\n --clr-text: light-dark(\n oklch(5% 0.1 var(--primary-hue)),\n oklch(100% 0.1 var(--primary-hue))\n );\n --clr-background: light-dark(\n oklch(100% 0.1 var(--primary-hue)),\n oklch(15% 0.15 var(--primary-hue))\n );\n --clr-border: light-dark(\n oklch(90% 0.2 var(--primary-hue)),\n oklch(25% 0.1 var(--primary-hue))\n );\n\n /* Adding snow fall colour whilst we are using it */\n --clr-snow-fall: light-dark(\n oklch(25% 0.15 var(--primary-hue)),\n oklch(85% 0.2 var(--primary-hue))\n );\n }\n\n body {\n background-color: var(--clr-background);\n color: var(--clr-text);\n }\n}\n","@layer base {\n :root {\n --border-radius: 8px;\n }\n\n html {\n font-size: 125%;\n }\n\n body {\n display: grid;\n grid-template-columns: 1fr min(80ch, 80vw) 1fr;\n grid-template-rows: min-content 1fr min-content;\n padding-inline: 4vw;\n\n > header {\n grid-column: 1/-1;\n }\n\n > main {\n grid-column: 1/-1;\n display: grid;\n grid-template-columns: subgrid;\n\n > * {\n grid-column: 2/3;\n }\n\n > .full-bleed {\n grid-column: 1/-1;\n }\n }\n\n > footer {\n grid-column: 1/-1;\n margin-block-start: 2ex;\n\n search form {\n display: flex;\n flex-direction: row;\n align-items: center;\n gap: 1ex;\n\n input {\n min-width: 0;\n }\n }\n }\n }\n\n .h-feed {\n display: flex;\n flex-direction: column;\n gap: 2ex;\n }\n}\n\n@layer components {\n .h-entry {\n pre {\n padding: 1rem;\n }\n }\n}\n\n","@layer components {\n body > header {\n display: grid;\n grid-template-columns: auto 1fr auto;\n grid-template-rows: auto auto;\n align-items: baseline;\n gap: 1rem;\n\n a {\n text-decoration: none;\n }\n\n h1 {\n margin: 0;\n text-wrap: nowrap;\n }\n\n nav {\n display: flex;\n flex-direction: row;\n gap: .5rem;\n\n @media screen and (width <= 1100px) {\n grid-row: 2;\n grid-column: 1 / span 3;\n flex-wrap: wrap;\n }\n }\n }\n}\n","@layer reset {\n /* Sourced from https://piccalil.li/blog/a-more-modern-css-reset/ */\n\n /* Box sizing rules */\n *,\n *::before,\n *::after {\n box-sizing: border-box;\n }\n\n /* Prevent font size inflation */\n html {\n /* stylelint-disable property-no-vendor-prefix */\n -moz-text-size-adjust: none;\n -webkit-text-size-adjust: none;\n /* stylelint-enable property-no-vendor-prefix */\n text-size-adjust: none;\n }\n\n /* Remove default margin in favour of better control in authored CSS */\n body, h1, h2, h3, h4, p,\n figure, blockquote, dl, dd {\n margin-block-end: 0;\n }\n\n /* Remove list styles on ul, ol elements with a list role, which suggests default styling will be removed */\n ul[role='list'],\n ol[role='list'] {\n list-style: none;\n }\n\n /* Set core body defaults */\n body {\n line-height: 1.5;\n }\n\n /* Set shorter line heights on headings and interactive elements */\n h1, h2, h3, h4,\n button, input, label {\n line-height: 1.1;\n }\n\n /* Balance text wrapping on headings */\n h1, h2,\n h3, h4 {\n text-wrap: balance;\n }\n\n /* A elements that don't have a class get default styles */\n a:not([class]) {\n text-decoration-skip-ink: auto;\n color: currentcolor;\n }\n\n /* Make images easier to work with */\n img,\n picture {\n max-width: 100%;\n height: auto;\n display: block;\n }\n\n /* Inherit fonts for inputs and buttons */\n input, button,\n textarea, select {\n font-family: inherit;\n font-size: inherit;\n }\n\n /* Make sure textareas without a rows attribute are not tiny */\n textarea:not([rows]) {\n min-height: 10em;\n }\n\n /* Anything that has been anchored to should have extra scroll margin */\n :target {\n scroll-margin-block: 5ex;\n }\n\n /* Disable view transition animations for users who don’t want them */\n @media (prefers-reduced-motion) {\n ::view-transition-group(*),\n ::view-transition-old(*),\n ::view-transition-new(*) {\n animation: none !important;\n }\n }\n}\n","@layer components {\n .pagination {\n display: flex;\n flex-direction: row;\n justify-content: center;\n align-items: center;\n\n div {\n display: flex;\n flex-direction: row;\n gap: 1ex;\n }\n }\n}\n","@layer components {\n .token-list-wrapper {\n margin-block-start: 1em;\n border: 1px solid var(--clr-border);\n border-radius: 16px;\n overflow: auto hidden;\n background: light-dark(\n oklch(99% 0.02 var(--primary-hue)),\n oklch(22% 0.05 var(--primary-hue))\n );\n }\n\n .token-list {\n width: 100%;\n min-width: 640px;\n table-layout: fixed;\n border-collapse: collapse;\n\n th,\n td {\n text-align: left;\n padding: 0.9em 1.2em;\n border-bottom: 1px solid var(--clr-border);\n vertical-align: middle;\n }\n\n th {\n font-size: 0.8em;\n font-weight: 700;\n text-transform: uppercase;\n letter-spacing: 0.08em;\n opacity: 0.7;\n }\n\n tr.is-revoked {\n opacity: 0.6;\n }\n\n a {\n word-break: break-all;\n }\n\n th:nth-child(1),\n td:nth-child(1) {\n width: 31%;\n }\n\n th:nth-child(2),\n td:nth-child(2) {\n width: 24%;\n }\n\n th:nth-child(3),\n td:nth-child(3) {\n width: 15%;\n }\n\n th:nth-child(4),\n td:nth-child(4) {\n width: 16%;\n }\n\n th:nth-child(5),\n td:nth-child(5) {\n width: 14%;\n text-align: right;\n }\n\n tr:last-child td {\n border-bottom: none;\n }\n }\n\n .scope-chips {\n display: flex;\n flex-wrap: wrap;\n gap: 0.4em;\n }\n\n .scope-chip {\n display: inline-block;\n padding: 0.2em 0.7em;\n border-radius: 999px;\n background: light-dark(\n oklch(92% 0.05 var(--primary-hue)),\n oklch(35% 0.08 var(--primary-hue))\n );\n font-size: 0.85em;\n white-space: nowrap;\n }\n\n .badge,\n .token-list button.revoke {\n display: inline-flex;\n align-items: center;\n gap: 0.4em;\n padding: 0.3em 0.8em;\n border-radius: 999px;\n font-size: 0.85em;\n font-weight: 600;\n line-height: 1.5;\n white-space: nowrap;\n }\n\n .badge {\n background: transparent;\n border: 1px solid currentcolor;\n }\n\n .badge-active {\n color: light-dark(oklch(35% 0.16 145deg), oklch(75% 0.16 145deg));\n\n .dot {\n width: 6px;\n height: 6px;\n border-radius: 50%;\n background: currentcolor;\n }\n }\n\n .badge-revoked {\n color: var(--clr-text);\n opacity: 0.7;\n }\n\n .token-list button.revoke {\n background: light-dark(oklch(92% 0.15 25deg), oklch(30% 0.12 25deg));\n color: light-dark(oklch(30% 0.15 25deg), oklch(92% 0.12 25deg));\n border: 1px solid transparent;\n cursor: pointer;\n transition: background-color 150ms ease;\n }\n\n .token-list button.revoke:hover {\n background: light-dark(oklch(80% 0.2 25deg), oklch(45% 0.18 25deg));\n }\n\n .token-reveal {\n margin-block-end: 1em;\n padding: 1em 1.2em;\n border: 1px solid var(--clr-border);\n border-radius: 16px;\n background: light-dark(\n oklch(96% 0.08 145deg),\n oklch(28% 0.08 145deg)\n );\n\n input {\n width: 100%;\n font-family: monospace;\n padding: 0.5em 0.7em;\n border-radius: 8px;\n border: 1px solid var(--clr-border);\n }\n }\n\n .scope-checkboxes {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 1em;\n }\n}\n","@layer components {\n #theme-selector {\n display: flex;\n justify-self: start;\n align-items: center;\n gap: .25rem;\n padding: .25rem;\n border-radius: 999px;\n background-color: color-mix(in oklch, var(--clr-border) 40%, transparent);\n\n button {\n display: grid;\n place-items: center;\n appearance: none;\n border: none;\n border-radius: 999px;\n padding: .375rem;\n background: transparent;\n color: inherit;\n transition: background-color .2s, color .2s;\n\n &:hover {\n cursor: pointer;\n }\n\n &:focus-visible {\n outline: 2px solid var(--clr-text);\n outline-offset: 2px;\n }\n\n svg {\n width: 1.5rem;\n height: 1.5rem;\n fill: currentcolor;\n }\n\n @media (hover: hover) {\n &:hover {\n background-color: color-mix(in oklch, var(--clr-border) 70%, transparent);\n }\n\n &[aria-pressed=\"true\"]:hover {\n background-color: var(--clr-text);\n }\n }\n\n &[aria-pressed=\"true\"] {\n background-color: var(--clr-text);\n color: var(--clr-background);\n }\n }\n }\n\n .sr-only {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n margin: -1px;\n overflow: hidden;\n clip-path: inset(50%);\n white-space: nowrap;\n border: 0;\n }\n\n ::view-transition-group(changing-theme) {\n animation-duration: 1.25s;\n }\n\n ::view-transition-new(changing-theme),\n ::view-transition-old(changing-theme) {\n mix-blend-mode: normal;\n }\n\n ::view-transition-new(changing-theme) {\n animation-name: reveal;\n }\n\n ::view-transition-old(changing-theme) {\n animation: none;\n }\n\n @keyframes reveal {\n from {\n clip-path: polygon(-30% 0, -30% 0, -15% 100%, -10% 115%);\n }\n\n to {\n clip-path: polygon(-30% 0, 130% 0, 115% 100%, -10% 115%);\n }\n }\n}\n","@layer components {\n .note {\n display: flex;\n flex-direction: column;\n border: 1px solid var(--clr-border);\n border-radius: var(--border-radius);\n padding: 1ex 2ex;\n\n .e-content {\n > p:first-child {\n margin-block-start: 0;\n }\n\n .u-photo {\n width: 100%;\n height: auto;\n }\n }\n\n .syndication-links {\n display: flex;\n flex-direction: row;\n gap: 1ex;\n\n svg {\n border-radius: 4px;\n width: auto;\n height: 1lh;\n }\n }\n }\n\n .reply-to {\n font-size: 85%;\n margin-inline: 2ex;\n padding-inline: 1ex;\n border: 1px solid var(--clr-border);\n border-bottom: none;\n border-top-left-radius: var(--border-radius);\n border-top-right-radius: var(--border-radius);\n }\n}\n"],"names":[]}
\ No newline at end of file
+{"version":3,"mappings":"AACA,aCGE,uCAOA,oFASA,8DAMA,4CAMA,qBAKA,+CAMA,8BAMA,gEAMA,qDAQA,mEAOA,qCAKA,gCAKA,gCACE,wGDhFJ,YOAE,oaAwBA,kELxBA,0BAIA,oBAIA,iIAME,0BAIA,mEAKE,oBAIA,gCAKF,iDAIE,yEAME,uBAON,oDFjDF,kBEyDE,SACE,oBG1DF,wHAOE,yBAIA,+BAKA,gDAKE,kCAAqC,2CGrBzC,+HAOE,aACE,qCAIA,mCAMF,6DAKE,gDAQJ,4ML/BA,sFAME,+CGNF,0LASE,gLAWE,uBAIA,qEAKA,mDAMA,qBACE,iFAIA,6DAKF,oFAOJ,uIAYA,iEAIA,kGAKA,4DAIA,qDAIA,mIFjFA,iNAWA,mFAME,6GAQA,6FAQA,2BAIA,yBAIA,4CAKA,8CAKA,8CAKA,8CAKA,+DAMA,uCAKF,kDAMA,qMAYA,0LAaA,uCAKA,sEAGE,uEAQF,gDAKA,kNAQA","sources":["resources/css/app.css","resources/css/theme-selector.css","resources/css/layout.css","resources/css/pagination.css","resources/css/admin-tokens.css","resources/css/header.css","resources/css/colours.css","resources/css/reset.css","resources/css/notes.css"],"sourcesContent":["/* First thing we need to do is declare our cascade layers */\n@layer reset, base, components;\n\n@import url('reset.css');\n@import url('colours.css');\n@import url('layout.css');\n@import url('header.css');\n@import url('notes.css');\n@import url('pagination.css');\n@import url('theme-selector.css');\n@import url('admin-tokens.css');\n","@layer components {\n #theme-selector {\n display: flex;\n justify-self: start;\n align-items: center;\n gap: .25rem;\n padding: .25rem;\n border-radius: 999px;\n background-color: color-mix(in oklch, var(--clr-border) 40%, transparent);\n\n button {\n display: grid;\n place-items: center;\n appearance: none;\n border: none;\n border-radius: 999px;\n padding: .375rem;\n background: transparent;\n color: inherit;\n transition: background-color .2s, color .2s;\n\n &:hover {\n cursor: pointer;\n }\n\n &:focus-visible {\n outline: 2px solid var(--clr-text);\n outline-offset: 2px;\n }\n\n svg {\n width: 1.5rem;\n height: 1.5rem;\n fill: currentcolor;\n }\n\n @media (hover: hover) {\n &:hover {\n background-color: color-mix(in oklch, var(--clr-border) 70%, transparent);\n }\n\n &[aria-pressed=\"true\"]:hover {\n background-color: var(--clr-text);\n }\n }\n\n &[aria-pressed=\"true\"] {\n background-color: var(--clr-text);\n color: var(--clr-background);\n }\n }\n }\n\n .sr-only {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n margin: -1px;\n overflow: hidden;\n clip-path: inset(50%);\n white-space: nowrap;\n border: 0;\n }\n\n ::view-transition-group(changing-theme) {\n animation-duration: 1.25s;\n }\n\n ::view-transition-new(changing-theme),\n ::view-transition-old(changing-theme) {\n mix-blend-mode: normal;\n }\n\n ::view-transition-new(changing-theme) {\n animation-name: reveal;\n }\n\n ::view-transition-old(changing-theme) {\n animation: none;\n }\n\n @keyframes reveal {\n from {\n clip-path: polygon(-30% 0, -30% 0, -15% 100%, -10% 115%);\n }\n\n to {\n clip-path: polygon(-30% 0, 130% 0, 115% 100%, -10% 115%);\n }\n }\n}\n","@layer base {\n :root {\n --border-radius: 8px;\n }\n\n html {\n font-size: 125%;\n }\n\n body {\n display: grid;\n grid-template-columns: 1fr min(80ch, 80vw) 1fr;\n grid-template-rows: min-content 1fr min-content;\n padding-inline: 4vw;\n\n > header {\n grid-column: 1/-1;\n }\n\n > main {\n grid-column: 1/-1;\n display: grid;\n grid-template-columns: subgrid;\n\n > * {\n grid-column: 2/3;\n }\n\n > .full-bleed {\n grid-column: 1/-1;\n }\n }\n\n > footer {\n grid-column: 1/-1;\n margin-block-start: 2ex;\n\n search form {\n display: flex;\n flex-direction: row;\n align-items: center;\n gap: 1ex;\n\n input {\n min-width: 0;\n }\n }\n }\n }\n\n .h-feed {\n display: flex;\n flex-direction: column;\n gap: 2ex;\n }\n}\n\n@layer components {\n .h-entry {\n pre {\n padding: 1rem;\n }\n }\n}\n\n","@layer components {\n .pagination {\n display: flex;\n flex-direction: row;\n justify-content: center;\n align-items: center;\n\n div {\n display: flex;\n flex-direction: row;\n gap: 1ex;\n }\n }\n}\n","@layer components {\n .token-list-wrapper {\n margin-block-start: 1em;\n border: 1px solid var(--clr-border);\n border-radius: 16px;\n overflow: auto hidden;\n background: light-dark(\n oklch(99% 0.02 var(--primary-hue)),\n oklch(22% 0.05 var(--primary-hue))\n );\n }\n\n .token-list {\n width: 100%;\n min-width: 640px;\n table-layout: fixed;\n border-collapse: collapse;\n\n th,\n td {\n text-align: left;\n padding: 0.9em 1.2em;\n border-bottom: 1px solid var(--clr-border);\n vertical-align: middle;\n }\n\n th {\n font-size: 0.8em;\n font-weight: 700;\n text-transform: uppercase;\n letter-spacing: 0.08em;\n opacity: 0.7;\n }\n\n tr.is-revoked {\n opacity: 0.6;\n }\n\n a {\n word-break: break-all;\n }\n\n th:nth-child(1),\n td:nth-child(1) {\n width: 31%;\n }\n\n th:nth-child(2),\n td:nth-child(2) {\n width: 24%;\n }\n\n th:nth-child(3),\n td:nth-child(3) {\n width: 15%;\n }\n\n th:nth-child(4),\n td:nth-child(4) {\n width: 16%;\n }\n\n th:nth-child(5),\n td:nth-child(5) {\n width: 14%;\n text-align: right;\n }\n\n tr:last-child td {\n border-bottom: none;\n }\n }\n\n .scope-chips {\n display: flex;\n flex-wrap: wrap;\n gap: 0.4em;\n }\n\n .scope-chip {\n display: inline-block;\n padding: 0.2em 0.7em;\n border-radius: 999px;\n background: light-dark(\n oklch(92% 0.05 var(--primary-hue)),\n oklch(35% 0.08 var(--primary-hue))\n );\n font-size: 0.85em;\n white-space: nowrap;\n }\n\n .badge,\n .token-list button.revoke {\n display: inline-flex;\n align-items: center;\n gap: 0.4em;\n padding: 0.3em 0.8em;\n border-radius: 999px;\n font-size: 0.85em;\n font-weight: 600;\n line-height: 1.5;\n white-space: nowrap;\n }\n\n .badge {\n background: transparent;\n border: 1px solid currentcolor;\n }\n\n .badge-active {\n color: light-dark(oklch(35% 0.16 145deg), oklch(75% 0.16 145deg));\n\n .dot {\n width: 6px;\n height: 6px;\n border-radius: 50%;\n background: currentcolor;\n }\n }\n\n .badge-revoked {\n color: var(--clr-text);\n opacity: 0.7;\n }\n\n .token-list button.revoke {\n background: light-dark(oklch(92% 0.15 25deg), oklch(30% 0.12 25deg));\n color: light-dark(oklch(30% 0.15 25deg), oklch(92% 0.12 25deg));\n border: 1px solid transparent;\n cursor: pointer;\n transition: background-color 150ms ease;\n }\n\n .token-list button.revoke:hover {\n background: light-dark(oklch(80% 0.2 25deg), oklch(45% 0.18 25deg));\n }\n}\n","@layer components {\n body > header {\n display: grid;\n grid-template-columns: auto 1fr auto;\n grid-template-rows: auto auto;\n align-items: baseline;\n gap: 1rem;\n\n a {\n text-decoration: none;\n }\n\n h1 {\n margin: 0;\n text-wrap: nowrap;\n }\n\n nav {\n display: flex;\n flex-direction: row;\n gap: .5rem;\n\n @media screen and (width <= 1100px) {\n grid-row: 2;\n grid-column: 1 / span 3;\n flex-wrap: wrap;\n }\n }\n }\n}\n","@layer base {\n :root {\n color-scheme: light dark;\n\n --primary-hue: 307;\n --clr-text: light-dark(\n oklch(5% 0.1 var(--primary-hue)),\n oklch(100% 0.1 var(--primary-hue))\n );\n --clr-background: light-dark(\n oklch(100% 0.1 var(--primary-hue)),\n oklch(15% 0.15 var(--primary-hue))\n );\n --clr-border: light-dark(\n oklch(90% 0.2 var(--primary-hue)),\n oklch(25% 0.1 var(--primary-hue))\n );\n\n /* Adding snow fall colour whilst we are using it */\n --clr-snow-fall: light-dark(\n oklch(25% 0.15 var(--primary-hue)),\n oklch(85% 0.2 var(--primary-hue))\n );\n }\n\n body {\n background-color: var(--clr-background);\n color: var(--clr-text);\n }\n}\n","@layer reset {\n /* Sourced from https://piccalil.li/blog/a-more-modern-css-reset/ */\n\n /* Box sizing rules */\n *,\n *::before,\n *::after {\n box-sizing: border-box;\n }\n\n /* Prevent font size inflation */\n html {\n /* stylelint-disable property-no-vendor-prefix */\n -moz-text-size-adjust: none;\n -webkit-text-size-adjust: none;\n /* stylelint-enable property-no-vendor-prefix */\n text-size-adjust: none;\n }\n\n /* Remove default margin in favour of better control in authored CSS */\n body, h1, h2, h3, h4, p,\n figure, blockquote, dl, dd {\n margin-block-end: 0;\n }\n\n /* Remove list styles on ul, ol elements with a list role, which suggests default styling will be removed */\n ul[role='list'],\n ol[role='list'] {\n list-style: none;\n }\n\n /* Set core body defaults */\n body {\n line-height: 1.5;\n }\n\n /* Set shorter line heights on headings and interactive elements */\n h1, h2, h3, h4,\n button, input, label {\n line-height: 1.1;\n }\n\n /* Balance text wrapping on headings */\n h1, h2,\n h3, h4 {\n text-wrap: balance;\n }\n\n /* A elements that don't have a class get default styles */\n a:not([class]) {\n text-decoration-skip-ink: auto;\n color: currentcolor;\n }\n\n /* Make images easier to work with */\n img,\n picture {\n max-width: 100%;\n height: auto;\n display: block;\n }\n\n /* Inherit fonts for inputs and buttons */\n input, button,\n textarea, select {\n font-family: inherit;\n font-size: inherit;\n }\n\n /* Make sure textareas without a rows attribute are not tiny */\n textarea:not([rows]) {\n min-height: 10em;\n }\n\n /* Anything that has been anchored to should have extra scroll margin */\n :target {\n scroll-margin-block: 5ex;\n }\n\n /* Disable view transition animations for users who don’t want them */\n @media (prefers-reduced-motion) {\n ::view-transition-group(*),\n ::view-transition-old(*),\n ::view-transition-new(*) {\n animation: none !important;\n }\n }\n}\n","@layer components {\n .note {\n display: flex;\n flex-direction: column;\n border: 1px solid var(--clr-border);\n border-radius: var(--border-radius);\n padding: 1ex 2ex;\n\n .e-content {\n > p:first-child {\n margin-block-start: 0;\n }\n\n .u-photo {\n width: 100%;\n height: auto;\n }\n }\n\n .syndication-links {\n display: flex;\n flex-direction: row;\n gap: 1ex;\n\n svg {\n border-radius: 4px;\n width: auto;\n height: 1lh;\n }\n }\n }\n\n .reply-to {\n font-size: 85%;\n margin-inline: 2ex;\n padding-inline: 1ex;\n border: 1px solid var(--clr-border);\n border-bottom: none;\n border-top-left-radius: var(--border-radius);\n border-top-right-radius: var(--border-radius);\n }\n}\n"],"names":[]}
\ No newline at end of file
diff --git a/public/assets/css/app.css.zst b/public/assets/css/app.css.zst
index 2fda6303..d7479672 100644
Binary files a/public/assets/css/app.css.zst and b/public/assets/css/app.css.zst differ
diff --git a/public/assets/js/app.js.br b/public/assets/js/app.js.br
index 7567f865..40b0dd0d 100644
Binary files a/public/assets/js/app.js.br and b/public/assets/js/app.js.br differ
diff --git a/public/assets/js/app.js.zst b/public/assets/js/app.js.zst
index f9a71170..e9a5163a 100644
Binary files a/public/assets/js/app.js.zst and b/public/assets/js/app.js.zst differ
diff --git a/resources/css/admin-tokens.css b/resources/css/admin-tokens.css
index 7bf0ad02..a0db29e5 100644
--- a/resources/css/admin-tokens.css
+++ b/resources/css/admin-tokens.css
@@ -134,30 +134,4 @@
.token-list button.revoke:hover {
background: light-dark(oklch(80% 0.2 25deg), oklch(45% 0.18 25deg));
}
-
- .token-reveal {
- margin-block-end: 1em;
- padding: 1em 1.2em;
- border: 1px solid var(--clr-border);
- border-radius: 16px;
- background: light-dark(
- oklch(96% 0.08 145deg),
- oklch(28% 0.08 145deg)
- );
-
- input {
- width: 100%;
- font-family: monospace;
- padding: 0.5em 0.7em;
- border-radius: 8px;
- border: 1px solid var(--clr-border);
- }
- }
-
- .scope-checkboxes {
- display: flex;
- flex-wrap: wrap;
- align-items: center;
- gap: 1em;
- }
}
diff --git a/resources/views/about.blade.php b/resources/views/about.blade.php
deleted file mode 100644
index e73c933a..00000000
--- a/resources/views/about.blade.php
+++ /dev/null
@@ -1,9 +0,0 @@
-@extends('master')
-@section('title')About « @stop
-
-@section('content')
- About
-
- {!! $about !!}
-
-@stop
diff --git a/resources/views/admin/about/show.blade.php b/resources/views/admin/about/show.blade.php
deleted file mode 100644
index 59c6a433..00000000
--- a/resources/views/admin/about/show.blade.php
+++ /dev/null
@@ -1,19 +0,0 @@
-@extends('master')
-
-@section('title')Edit About « Admin CP « @stop
-
-@section('content')
- Edit About
-
-@stop
diff --git a/resources/views/admin/tokens/create.blade.php b/resources/views/admin/tokens/create.blade.php
deleted file mode 100644
index d4847d1e..00000000
--- a/resources/views/admin/tokens/create.blade.php
+++ /dev/null
@@ -1,52 +0,0 @@
-@extends('master')
-
-@section('title')New Token « Admin CP « @stop
-
-@section('content')
- Generate a new token
- Use this for clients that can't complete the IndieAuth authorization flow (e.g. they don't support PKCE) and instead let you paste in a token directly.
-
-
- {{ csrf_field() }}
-
-
- Client
-
-
-
-
- Scope
-
-
- create
-
-
-
- update
-
-
-
-
- Generate token
-
-
-@stop
diff --git a/resources/views/admin/tokens/index.blade.php b/resources/views/admin/tokens/index.blade.php
index 33806cdd..c9443e29 100644
--- a/resources/views/admin/tokens/index.blade.php
+++ b/resources/views/admin/tokens/index.blade.php
@@ -4,15 +4,6 @@
@section('content')
Micropub Tokens
- Generate new token
-
- @if(session('new_token'))
-
-
Here's your new token. Copy it now — it won't be shown again.
-
-
- @endif
-
@if($tokens->isEmpty())
No tokens have been issued.
@else
diff --git a/resources/views/admin/welcome.blade.php b/resources/views/admin/welcome.blade.php
index 132da7cc..03112665 100644
--- a/resources/views/admin/welcome.blade.php
+++ b/resources/views/admin/welcome.blade.php
@@ -57,11 +57,6 @@
Edit your bio .
- About
-
- Edit your about page .
-
-
Passkeys
Manager your passkeys .
diff --git a/resources/views/master.blade.php b/resources/views/master.blade.php
index b33d1fa9..543f4255 100644
--- a/resources/views/master.blade.php
+++ b/resources/views/master.blade.php
@@ -36,7 +36,6 @@
Likes
Contacts
Projects
- About
diff --git a/routes/web.php b/routes/web.php
index a9b46407..3b6fa7d2 100644
--- a/routes/web.php
+++ b/routes/web.php
@@ -1,7 +1,5 @@
name('login');
Route::post('login', [AuthController::class, 'login']);
@@ -157,8 +152,6 @@ Route::middleware(MyAuthMiddleware::class)->prefix('admin')->group(function () {
// Micropub Tokens
Route::prefix('tokens')->group(function () {
Route::get('/', [TokensController::class, 'index']);
- Route::get('/create', [TokensController::class, 'create']);
- Route::post('/', [TokensController::class, 'store']);
Route::put('/{token}/revoke', [TokensController::class, 'revoke']);
});
@@ -174,12 +167,6 @@ Route::middleware(MyAuthMiddleware::class)->prefix('admin')->group(function () {
Route::put('/', [SettingsController::class, 'update']);
});
- // About
- Route::prefix('about')->group(function () {
- Route::get('/', [AboutController::class, 'show'])->name('admin.about.show');
- Route::put('/', [AboutController::class, 'update']);
- });
-
// Passkeys
Route::prefix('passkeys')->group(function () {
Route::get('/', [PasskeysController::class, 'index']);
diff --git a/tests/Feature/AboutPageTest.php b/tests/Feature/AboutPageTest.php
deleted file mode 100644
index 61455069..00000000
--- a/tests/Feature/AboutPageTest.php
+++ /dev/null
@@ -1,26 +0,0 @@
-create([
- 'content' => 'This is the about page content.',
- ]);
-
- $this->get('/about')
- ->assertSee('This is the about page content.');
- }
-}
diff --git a/tests/Feature/Admin/AboutTest.php b/tests/Feature/Admin/AboutTest.php
deleted file mode 100644
index 141a548b..00000000
--- a/tests/Feature/Admin/AboutTest.php
+++ /dev/null
@@ -1,68 +0,0 @@
-make();
-
- $response = $this->actingAs($user)
- ->get('/admin/about');
- $response->assertSeeText('Edit About');
- }
-
- #[Test]
- public function admin_can_create_about(): void
- {
- $user = User::factory()->make();
-
- $this->actingAs($user)
- ->post('/admin/about', [
- '_method' => 'PUT',
- 'content' => 'About content',
- ]);
- $this->assertDatabaseHas('about', ['content' => 'About content']);
- }
-
- #[Test]
- public function admin_can_load_existing_about(): void
- {
- $user = User::factory()->make();
- $about = About::factory()->create([
- 'content' => 'This is my about page. It uses HTML .',
- ]);
-
- $response = $this->actingAs($user)
- ->get('/admin/about');
- $response->assertSeeText('This is my about page. It uses HTML .');
- }
-
- #[Test]
- public function admin_can_edit_about(): void
- {
- $user = User::factory()->make();
- $about = About::factory()->create();
-
- $this->actingAs($user)
- ->post('/admin/about', [
- '_method' => 'PUT',
- 'content' => 'This about page has been edited',
- ]);
- $this->assertDatabaseHas('about', [
- 'content' => 'This about page has been edited',
- ]);
- }
-}
diff --git a/tests/Feature/Admin/TokensTest.php b/tests/Feature/Admin/TokensTest.php
index d6695a15..0c415296 100644
--- a/tests/Feature/Admin/TokensTest.php
+++ b/tests/Feature/Admin/TokensTest.php
@@ -37,73 +37,6 @@ class TokensTest extends TestCase
$response->assertSeeText($token->client_id);
}
- #[Test]
- public function create_requires_authentication(): void
- {
- $response = $this->get('/admin/tokens/create');
- $response->assertRedirect();
- }
-
- #[Test]
- public function create_shows_form(): void
- {
- $user = User::factory()->make();
-
- $response = $this->actingAs($user)->get('/admin/tokens/create');
-
- $response->assertOk();
- $response->assertSee('name="client_id"', false);
- }
-
- #[Test]
- public function store_requires_authentication(): void
- {
- $response = $this->post('/admin/tokens', [
- 'client_id' => 'https://ia.net/writer',
- 'scope' => ['create'],
- ]);
-
- $response->assertRedirect();
- $this->assertDatabaseCount('micropub_tokens', 0);
- }
-
- #[Test]
- public function store_creates_a_new_token_and_redirects_with_it_flashed(): void
- {
- $user = User::factory()->make();
-
- $response = $this->actingAs($user)->post('/admin/tokens', [
- 'client_id' => 'https://ia.net/writer',
- 'scope' => ['create', 'update'],
- ]);
-
- $response->assertRedirect('/admin/tokens');
- $response->assertSessionHas('new_token');
-
- $this->assertDatabaseHas('micropub_tokens', [
- 'client_id' => 'https://ia.net/writer',
- 'scope' => 'create update',
- 'me' => config('app.url'),
- ]);
-
- $token = $response->getSession()->get('new_token');
- $this->assertNotNull(MicropubToken::findActive($token));
- }
-
- #[Test]
- public function store_requires_at_least_one_scope(): void
- {
- $user = User::factory()->make();
-
- $response = $this->actingAs($user)->post('/admin/tokens', [
- 'client_id' => 'https://ia.net/writer',
- 'scope' => [],
- ]);
-
- $response->assertSessionHasErrors('scope');
- $this->assertDatabaseCount('micropub_tokens', 0);
- }
-
#[Test]
public function revoke_requires_authentication(): void
{
diff --git a/tests/Feature/MicropubControllerTest.php b/tests/Feature/MicropubControllerTest.php
index 3fd8b515..efcfb6ce 100644
--- a/tests/Feature/MicropubControllerTest.php
+++ b/tests/Feature/MicropubControllerTest.php
@@ -4,17 +4,14 @@ declare(strict_types=1);
namespace Tests\Feature;
-use App\Exceptions\MicropubHandlerException;
use App\Jobs\SendWebMentions;
use App\Jobs\SyndicateNoteToBluesky;
use App\Jobs\SyndicateNoteToMastodon;
-use App\Models\Article;
use App\Models\Media;
use App\Models\Note;
use App\Models\Place;
use App\Models\SyndicationTarget;
use Faker\Factory;
-use Illuminate\Contracts\Debug\ExceptionHandler;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Queue;
@@ -460,11 +457,6 @@ class MicropubControllerTest extends TestCase
#[Test]
public function micropub_client_api_request_for_unsupported_post_type_returns_error(): void
{
- $this->mock(ExceptionHandler::class)
- ->shouldReceive('report')
- ->once()
- ->with(\Mockery::type(MicropubHandlerException::class));
-
$response = $this->postJson(
'/api/post',
[
@@ -879,8 +871,6 @@ class MicropubControllerTest extends TestCase
'main' => $content,
'published' => true,
]);
- $response->assertHeader('Location');
- $this->assertStringStartsWith(config('app.url').'/blog/', $response->headers->get('Location'));
}
#[Test]
@@ -912,64 +902,4 @@ class MicropubControllerTest extends TestCase
'published' => false,
]);
}
-
- #[Test]
- public function micropub_client_api_request_updates_an_existing_draft_article_with_the_same_name(): void
- {
- $draft = Article::create([
- 'title' => 'WireGuard',
- 'main' => 'Early draft content',
- 'published' => false,
- ]);
-
- $response = $this->postJson(
- '/api/post',
- [
- 'type' => ['h-entry'],
- 'properties' => [
- 'name' => ['WireGuard'],
- 'content' => ['Finished content'],
- ],
- ],
- ['HTTP_Authorization' => 'Bearer '.$this->getToken()]
- );
-
- $response
- ->assertJson(['response' => 'created'])
- ->assertStatus(201);
- $this->assertSame(1, Article::where('title', 'WireGuard')->count());
- $this->assertDatabaseHas('articles', [
- 'id' => $draft->id,
- 'title' => 'WireGuard',
- 'main' => 'Finished content',
- 'published' => true,
- ]);
- }
-
- #[Test]
- public function micropub_client_api_request_errors_when_an_article_with_the_same_name_is_already_published(): void
- {
- Article::create([
- 'title' => 'WireGuard',
- 'main' => 'Published content',
- 'published' => true,
- ]);
-
- $response = $this->postJson(
- '/api/post',
- [
- 'type' => ['h-entry'],
- 'properties' => [
- 'name' => ['WireGuard'],
- 'content' => ['Some other content'],
- ],
- ],
- ['HTTP_Authorization' => 'Bearer '.$this->getToken()]
- );
-
- $response
- ->assertJson(['error' => 'invalid_request'])
- ->assertStatus(400);
- $this->assertSame(1, Article::where('title', 'WireGuard')->count());
- }
}
diff --git a/tests/Unit/ArticlesTest.php b/tests/Unit/ArticlesTest.php
index afcc0828..fda1abf4 100644
--- a/tests/Unit/ArticlesTest.php
+++ b/tests/Unit/ArticlesTest.php
@@ -63,28 +63,6 @@ class ArticlesTest extends TestCase
);
}
- #[Test]
- public function uri_is_the_absolute_form_of_the_link(): void
- {
- $article = Article::create([
- 'title' => 'Test',
- 'main' => 'Test',
- ]);
-
- $this->assertEquals(config('app.url').$article->link, $article->uri);
- }
-
- #[Test]
- public function slug_is_suffixed_when_a_trashed_article_already_used_it(): void
- {
- $original = Article::create(['title' => 'My Title', 'main' => 'Content']);
- $original->delete();
-
- $newArticle = Article::create(['title' => 'My Title', 'main' => 'Other content']);
-
- $this->assertEquals('my-title-2', $newArticle->titleurl);
- }
-
#[Test]
public function date_scope_returns_expected_articles(): void
{