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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ Thumbs.db
/CLAUDE.md

# AI tool configurations
/.ai
/.cursor
/.gemini
/.junie
Expand Down
32 changes: 32 additions & 0 deletions app/Livewire/Concerns/HasMarkdownPreview.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php

namespace App\Livewire\Concerns;

use App\Services\MarkdownService;
use Livewire\Attributes\Computed;

trait HasMarkdownPreview
{
public string $editorMode = 'write';

public function setEditorMode(string $mode): void
{
if (! in_array($mode, ['write', 'preview', 'split'])) {
return;
}

$this->editorMode = $mode;
unset($this->previewHtml);
}

#[Computed]
public function previewHtml(): string
{
return app(MarkdownService::class)->render($this->content);
}

protected function clearPreviewCache(): void
{
unset($this->previewHtml);
}
}
3 changes: 3 additions & 0 deletions app/Livewire/Projects/Tabs/Prd.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace App\Livewire\Projects\Tabs;

use App\Enums\DocumentType;
use App\Livewire\Concerns\HasMarkdownPreview;
use App\Livewire\Concerns\HasVersionHistory;
use App\Models\Document;
use App\Models\DocumentVersion;
Expand All @@ -15,6 +16,7 @@
class Prd extends Component
{
use AuthorizesRequests;
use HasMarkdownPreview;
use HasVersionHistory;

public string $projectId;
Expand Down Expand Up @@ -52,6 +54,7 @@ public function loadContent(): void
public function updatedContent(): void
{
$this->isDirty = true;
$this->clearPreviewCache();
}

public function save(): void
Expand Down
3 changes: 3 additions & 0 deletions app/Livewire/Projects/Tabs/Tech.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use App\Actions\GenerateTasksFromTechSpec;
use App\Enums\DocumentType;
use App\Enums\PlanRunStepStatus;
use App\Livewire\Concerns\HasMarkdownPreview;
use App\Livewire\Concerns\HasVersionHistory;
use App\Models\Document;
use App\Models\DocumentVersion;
Expand All @@ -18,6 +19,7 @@
class Tech extends Component
{
use AuthorizesRequests;
use HasMarkdownPreview;
use HasVersionHistory;

public string $projectId;
Expand Down Expand Up @@ -55,6 +57,7 @@ public function loadContent(): void
public function updatedContent(): void
{
$this->isDirty = true;
$this->clearPreviewCache();
}

public function save(): void
Expand Down
3 changes: 2 additions & 1 deletion app/Providers/AppServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use App\Events\TasksChanged;
use App\Listeners\QueueGitHubSync;
use App\Services\MarkdownService;
use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\RateLimiter;
Expand All @@ -16,7 +17,7 @@ class AppServiceProvider extends ServiceProvider
*/
public function register(): void
{
//
$this->app->singleton(MarkdownService::class);
}

/**
Expand Down
37 changes: 37 additions & 0 deletions app/Services/MarkdownService.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php

namespace App\Services;

use League\CommonMark\Environment\Environment;
use League\CommonMark\Extension\CommonMark\CommonMarkCoreExtension;
use League\CommonMark\Extension\DisallowedRawHtml\DisallowedRawHtmlExtension;
use League\CommonMark\Extension\GithubFlavoredMarkdownExtension;
use League\CommonMark\MarkdownConverter;

class MarkdownService
{
private MarkdownConverter $converter;

public function __construct()
{
$environment = new Environment([
'html_input' => 'strip',
'allow_unsafe_links' => false,
]);

$environment->addExtension(new CommonMarkCoreExtension);
$environment->addExtension(new GithubFlavoredMarkdownExtension);
$environment->addExtension(new DisallowedRawHtmlExtension);

$this->converter = new MarkdownConverter($environment);
}

public function render(string $markdown): string
{
if (empty(trim($markdown))) {
return '';
}

return $this->converter->convert($markdown)->getContent();
}
}
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"firebase/php-jwt": "^7.0",
"laravel/framework": "^12.0",
"laravel/tinker": "^2.10.1",
"league/commonmark": "^2.8",
"livewire/livewire": "^3.6.4",
"livewire/volt": "^1.7.0",
"prism-php/prism": "^0.99.7",
Expand Down
2 changes: 1 addition & 1 deletion composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

56 changes: 55 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
"vite": "^7.0.7"
},
"dependencies": {
"@tailwindcss/forms": "^0.5.11"
"@tailwindcss/forms": "^0.5.11",
"@tailwindcss/typography": "^0.5.19",
"highlight.js": "^11.11.1"
}
}
92 changes: 92 additions & 0 deletions resources/css/app.css
Original file line number Diff line number Diff line change
@@ -1,6 +1,98 @@
@import 'tailwindcss';
@plugin '@tailwindcss/forms';
@plugin '@tailwindcss/typography';

/* Highlight.js theme for syntax highlighting */
@import 'highlight.js/styles/github-dark.css';

@source '../views/**/*.blade.php';
@source '../../vendor/laravel/framework/src/Illuminate/Pagination/resources/views/*.blade.php';
@source '../../storage/framework/views/*.php';

/* Markdown Preview Styling */
.markdown-preview {
/* Inline code - pink on light gray */
& :not(pre) > code {
@apply text-pink-600 bg-gray-100 px-1.5 py-0.5 rounded text-sm font-mono;
}

/* Remove the backtick quotes from inline code */
& :not(pre) > code::before,
& :not(pre) > code::after {
content: none;
}

/* Code blocks container */
& pre {
@apply bg-gray-900 rounded-lg overflow-x-auto relative;
}

/* Code block content - let highlight.js handle colors */
& pre code {
@apply bg-transparent p-4 text-sm leading-relaxed block;
}

/* Code block header with language label and copy button */
& .code-block-header {
@apply flex justify-between items-center px-4 py-2
bg-gray-800 border-b border-gray-700
text-xs text-gray-400 rounded-t-lg;
}

& .code-language {
@apply font-medium tracking-wide;
}

& .copy-btn {
@apply px-2.5 py-1 rounded text-gray-400
hover:text-white hover:bg-gray-700
transition-colors duration-150 cursor-pointer
border-0 bg-transparent;
}

& .copy-btn.copied {
@apply text-green-400;
}

/* When header exists, adjust code padding */
& pre:has(.code-block-header) {
@apply rounded-t-none;
}

& pre:has(.code-block-header) code {
@apply rounded-t-none;
}

/* Tables - clean borders */
& table {
@apply w-full border-collapse;
}

& th {
@apply bg-gray-50 font-semibold text-left px-4 py-2 border border-gray-200;
}

& td {
@apply px-4 py-2 border border-gray-200;
}

/* Task lists */
& input[type="checkbox"] {
@apply rounded border-gray-300 text-indigo-600 mr-2;
}

/* Blockquotes */
& blockquote {
@apply border-l-4 border-indigo-500 bg-indigo-50 pl-4 py-2 italic text-gray-700;
}

/* Horizontal rules */
& hr {
@apply border-gray-200 my-8;
}

/* Images */
& img {
@apply rounded-lg shadow-sm;
}
}
Loading
Loading