Skip to content
This repository was archived by the owner on Jan 13, 2025. It is now read-only.
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
14 changes: 8 additions & 6 deletions packages/akiradocs/src/app/[locale]/[type]/[...slug]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -134,17 +134,19 @@ export default async function ContentPage({ params }: Props) {
</div>
<MainTitle>{t(post.title)}</MainTitle>
<SubTitle>{t(post.description)}</SubTitle>
{post.blocks.map((block) => (
block.content !== post.title && (
<div className="mt-6">
{post.blocks.map((block) => (
block.content !== post.title && (
<BlockRenderer
key={block.id}
block={{
...block,
content: block.content
}}
/>
)
))}
}}
/>
)
))}
</div>
<PageNavigation prev={prev} next={next} locale={locale} />
</div>
</PostContainer>
Expand Down
4 changes: 2 additions & 2 deletions packages/akiradocs/src/components/blocks/ArticleHeaders.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,13 @@ export function ArticleHeaders({ title, setTitle, subtitle, setSubtitle, showPre
value={title}
onChange={(e) => setTitle(e.target.value)}
placeholder="Article title"
className="text-4xl font-bold mb-4 border-none px-0"
className="text-4xl font-bold mb-4 border-none px-0 max-w-4xl break-words"
/>
<Input
value={subtitle}
onChange={(e) => setSubtitle(e.target.value)}
placeholder="Add a subtitle"
className="text-xl mb-4 border-none px-0"
className="text-xl mb-4 border-none px-0 max-w-4xl break-words"
/>
</>
)
Expand Down
224 changes: 195 additions & 29 deletions packages/akiradocs/src/components/blocks/AudioBlock.tsx
Original file line number Diff line number Diff line change
@@ -1,32 +1,198 @@
import React from 'react';
import { cn } from "@/lib/utils";

interface AudioProps {
id?: string;
src: string;
caption?: string;
align?: 'left' | 'center' | 'right';
styles?: {
bold?: boolean;
italic?: boolean;
underline?: boolean;
};
"use client"

import { cn } from '@/lib/utils'
import { Button } from '@/components/ui/button'
import { Music, Pause, Play } from 'lucide-react'
import { useState, useRef, useEffect } from 'react'

interface AudioBlockProps {
content: string
id: string
onUpdate?: (content: string) => void
isEditing?: boolean
metadata?: {
caption?: string
alignment?: 'left' | 'center' | 'right'
styles?: {
bold?: boolean
italic?: boolean
underline?: boolean
}
}
}

export function Audio({ id, src, caption, align = 'left', styles }: AudioProps) {
const alignClass = align === 'center' ? 'mx-auto' : align === 'right' ? 'ml-auto' : '';
return (
<div id={id} className={`mb-6 py-1 ${alignClass}`}>
<audio controls className="w-full bg-background border border-border rounded-md">
<source src={src} type="audio/mpeg" />
<span className="text-muted-foreground">Your browser does not support the audio element.</span>
</audio>
{caption && <p className={cn(
"mt-2 text-sm text-muted-foreground",
styles?.bold && 'font-bold',
styles?.italic && 'italic',
styles?.underline && 'underline'
)}>{caption}</p>}
</div>
);
interface AudioBlockContent {
url: string
caption?: string
alignment?: 'left' | 'center' | 'right'
}

export function AudioBlock({ content, id, onUpdate, isEditing, metadata }: AudioBlockProps) {
const [isHovered, setIsHovered] = useState(false)
const [isPlaying, setIsPlaying] = useState(false)
const audioRef = useRef<HTMLAudioElement>(null)

const handlePlayPause = (e: React.MouseEvent) => {
e.stopPropagation()
if (audioRef.current) {
if (isPlaying) {
audioRef.current.pause()
} else {
audioRef.current.play()
}
setIsPlaying(!isPlaying)
}
}

// Add event listeners to sync the isPlaying state with audio events
useEffect(() => {
const audio = audioRef.current
if (!audio) return

const handlePlay = () => setIsPlaying(true)
const handlePause = () => setIsPlaying(false)
const handleEnded = () => setIsPlaying(false)

audio.addEventListener('play', handlePlay)
audio.addEventListener('pause', handlePause)
audio.addEventListener('ended', handleEnded)

return () => {
audio.removeEventListener('play', handlePlay)
audio.removeEventListener('pause', handlePause)
audio.removeEventListener('ended', handleEnded)
}
}, [])

const parseAudioContent = (content: string): AudioBlockContent => {
try {
return typeof content === 'string' ? JSON.parse(content) : content
} catch {
return {
url: content,
caption: metadata?.caption || '',
alignment: metadata?.alignment || 'center'
}
}
}

const UploadButton = () => (
<div className="text-center">
<input
type="file"
id={`audio-upload-${id}`}
className="hidden"
accept="audio/*"
/>
<div className="flex flex-col items-center gap-2">
<Button
variant="ghost"
className="w-full justify-start h-auto py-6 px-8 hover:bg-accent flex flex-col items-center gap-2"
onClick={(e) => {
e.stopPropagation()
document.getElementById(`audio-upload-${id}`)?.click()
}}
>
<Music className="h-8 w-8 text-muted-foreground" />
<span className="font-medium">Upload Audio</span>
<p className="text-sm text-muted-foreground">
Click to upload or drag and drop
</p>
</Button>
</div>
</div>
)

if (!content && isEditing) {
return (
<div className="flex items-center justify-center border-2 border-dashed border-muted-foreground/25 rounded-lg my-4">
<UploadButton />
</div>
)
}

const audioContent = parseAudioContent(content)
const alignment = audioContent.alignment || metadata?.alignment || 'center'

return (
<figure
className={cn(
"py-1 mb-6 relative group",
"flex flex-col",
alignment === 'left' && "items-start",
alignment === 'center' && "items-center",
alignment === 'right' && "items-end",
)}
onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}
>
<div className="relative w-full max-w-[500px]">
<div className="bg-muted rounded-lg p-4 flex items-center gap-4">
<div className="w-12 h-12 rounded-full bg-primary/10 flex items-center justify-center">
<Music className="h-6 w-6 text-primary" />
</div>
<audio
ref={audioRef}
src={audioContent.url}
controls
className="w-full"
/>
</div>
{isEditing && isHovered && (
<div
className="absolute inset-0 bg-black/50 flex items-center justify-center gap-2 rounded-lg transition-opacity"
onClick={(e) => e.stopPropagation()}
>
<input
type="file"
id={`audio-change-${id}`}
className="hidden"
accept="audio/*"
/>
<Button
variant="secondary"
className="gap-2"
onClick={(e) => {
e.stopPropagation()
document.getElementById(`audio-change-${id}`)?.click()
}}
>
<Music className="h-4 w-4" />
Change Audio
</Button>
<Button
variant="secondary"
className="gap-2"
onClick={handlePlayPause}
>
{isPlaying ? (
<>
<Pause className="h-4 w-4" />
Pause
</>
) : (
<>
<Play className="h-4 w-4" />
Play
</>
)}
</Button>
</div>
)}
</div>
{audioContent.caption && (
<figcaption className={cn(
"mt-2 text-sm text-muted-foreground italic",
alignment === 'left' && "text-left",
alignment === 'center' && "text-center",
alignment === 'right' && "text-right",
metadata?.styles?.bold && "font-bold",
metadata?.styles?.italic && "italic",
metadata?.styles?.underline && "underline"
)}>
{audioContent.caption}
</figcaption>
)}
</figure>
)
}
4 changes: 2 additions & 2 deletions packages/akiradocs/src/components/blocks/BlockquoteBlock.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export function Blockquote({ id, children, align = 'left', styles, isEditing, on

if (isEditing) {
return (
<div className={cn("border-l-4 border-border pl-4 text-muted-foreground", alignClass)}>
<div className={cn("border-l-4 border-border pl-4 py-1 mb-6 text-muted-foreground", alignClass)}>
<textarea
ref={textareaRef}
value={children as string}
Expand All @@ -63,7 +63,7 @@ export function Blockquote({ id, children, align = 'left', styles, isEditing, on
<blockquote
id={id}
className={cn(
"border-l-4 border-border pl-4 text-muted-foreground",
"border-l-4 border-border pl-4 py-1 mb-6 text-muted-foreground",
alignClass,
commonStyles
)}
Expand Down
2 changes: 1 addition & 1 deletion packages/akiradocs/src/components/blocks/CalloutBlock.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export function Callout({ id, type, title, children, align = 'left', styles, isE

return (
<Alert className={cn(
'flex flex-col sm:flex-row items-start gap-4 p-4 my-4 rounded-lg border-2',
'flex flex-col sm:flex-row items-start gap-4 p-4 mb-7 mt-1 rounded-lg border-2',
className,
{
'text-left': align === 'left',
Expand Down
Loading
Loading