diff --git a/modules/openai/chat-page.php b/modules/openai/chat-page.php
index 50a94e1..f7f5b28 100644
--- a/modules/openai/chat-page.php
+++ b/modules/openai/chat-page.php
@@ -22,6 +22,12 @@ function personalos_map_notebook_to_para_item( $notebook ) {
);
}
+/**
+ * Get messages from a post and parse them into UIMessage format
+ *
+ * @param int $post_id The post ID to retrieve messages from.
+ * @return array Parsed messages.
+ */
/**
* Get messages from a post and parse them into UIMessage format
*
@@ -38,18 +44,18 @@ function personalos_get_messages_from_post( $post_id ) {
$messages = array();
foreach ( $blocks as $block ) {
- if ( $block['blockName'] === 'pos/ai-message' ) {
+ if ( 'pos/ai-message' === $block['blockName'] ) {
$role = $block['attrs']['role'] ?? 'user';
- // Handle different content structures if necessary, for now assume string content in attrs or innerContent
- // Gutenberg blocks usually store content in innerContent for HTML, but pos/ai-message might be different.
- // Looking at save_backscroll implementation:
- // $content_blocks[] = get_comment_delimited_block_content( ... 'content' => $content ... )
- // This usually implies attribute storage or innerHTML if it's a dynamic block saving.
- // However, save_backscroll uses get_comment_delimited_block_content which suggests attributes serialization.
-
- $content = $block['attrs']['content'] ?? '';
$id = $block['attrs']['id'] ?? 'generated_' . uniqid();
+ // Content is stored in innerHTML as ...
+ $content = '';
+ if ( ! empty( $block['innerHTML'] ) ) {
+ if ( preg_match( '/(.*?)<\/span>/s', $block['innerHTML'], $matches ) ) {
+ $content = html_entity_decode( $matches[1], ENT_QUOTES, 'UTF-8' );
+ }
+ }
+
$messages[] = array(
'id' => $id,
'role' => $role,
diff --git a/modules/openai/class-openai-module.php b/modules/openai/class-openai-module.php
index 785b08c..032a010 100644
--- a/modules/openai/class-openai-module.php
+++ b/modules/openai/class-openai-module.php
@@ -1944,15 +1944,16 @@ public function save_backscroll( array $backscroll, array $search_args, bool $ap
}
}
- // Create message block
+ // Create message block with content in innerHTML (not attributes)
+ // This preserves newlines naturally without escaping hacks
+ $inner_html = '' . esc_html( $content ) . '';
$content_blocks[] = get_comment_delimited_block_content(
'pos/ai-message',
array(
- 'role' => $role,
- 'content' => $content,
- 'id' => $message_id,
+ 'role' => $role,
+ 'id' => $message_id,
),
- ''
+ $inner_html
);
}
}
@@ -1992,6 +1993,9 @@ public function save_backscroll( array $backscroll, array $search_args, bool $ap
$post_data['post_content'] = implode( "\n\n", $content_blocks );
}
+ // Note: wp_slash is handled by preserve_ai_message_newlines filter
+ // to protect escaped newlines from wp_unslash
+
// Only update if there is content to update or we are not just appending empty content
// But here we might want to update just to ensure touch?
if ( isset( $post_data['post_content'] ) || isset( $post_data['post_title'] ) || ! $append ) {
@@ -2001,6 +2005,7 @@ public function save_backscroll( array $backscroll, array $search_args, bool $ap
}
} else {
// Create new
+ // Note: wp_slash is handled by preserve_ai_message_newlines filter
$post_data['post_content'] = implode( "\n\n", $content_blocks );
// Ensure defaults for new post
if ( empty( $post_data['post_title'] ) ) {
diff --git a/src-chatbot/app/globals.css b/src-chatbot/app/globals.css
index 3409b98..7487002 100644
--- a/src-chatbot/app/globals.css
+++ b/src-chatbot/app/globals.css
@@ -99,8 +99,32 @@
@apply border-border;
}
+ html, body {
+ overflow-x: hidden;
+ width: 100%;
+ max-width: 100vw;
+ }
+
body {
@apply bg-background text-foreground;
+ /* PWA safe area handling for iOS notch and home indicator */
+ padding-top: env(safe-area-inset-top);
+ padding-bottom: env(safe-area-inset-bottom);
+ padding-left: env(safe-area-inset-left);
+ padding-right: env(safe-area-inset-right);
+ min-height: 100vh;
+ min-height: 100dvh;
+ }
+
+ /* Ensure proper text wrapping everywhere */
+ p, li, a, span, div {
+ overflow-wrap: break-word;
+ word-wrap: break-word;
+ word-break: break-word;
+ }
+
+ a {
+ word-break: break-all;
}
}
diff --git a/src-chatbot/app/layout.tsx b/src-chatbot/app/layout.tsx
index 1ea4bcb..0b9cd27 100644
--- a/src-chatbot/app/layout.tsx
+++ b/src-chatbot/app/layout.tsx
@@ -1,6 +1,7 @@
import type { Metadata } from 'next';
import { Geist, Geist_Mono } from 'next/font/google';
import ClientOnly from '@/components/client-only';
+import { PWARegister } from '@/components/pwa-register';
// import { GeistSans } from 'geist/font/sans'; // Removed due to resolution issues
import './globals.css';
@@ -13,7 +14,7 @@ export const metadata: Metadata = {
applicationName: 'PersonalOS',
appleWebApp: {
capable: true,
- statusBarStyle: 'default',
+ statusBarStyle: 'black-translucent',
title: 'Personal Chat',
// startupImage: [
// '/images/apple-touch-startup-image-768x1004.png',
@@ -32,7 +33,7 @@ export const metadata: Metadata = {
{ url: '/wp-content/plugins/personalos/build/chatbot/images/apple-icon.png', sizes: '180x180', type: 'image/png' },
],
},
-// manifest: '/wp-content/plugins/personalos/build/chatbot/manifest.json',
+ manifest: '/wp-content/plugins/personalos/build/chatbot/manifest.json',
};
export const viewport = {
@@ -90,7 +91,7 @@ export default async function RootLayout({
>
-
+
@@ -102,6 +103,7 @@ export default async function RootLayout({
/>
+
{children}