mirror of
https://github.com/khoaliber/khoj.git
synced 2026-03-02 13:18:18 +00:00
Make chats print friendly to share via print to PDF etc. from browser
Add print specific styling to hide side panels and chat input footers. Add heading with khoj logo, conversation title, agent and date.
This commit is contained in:
@@ -124,3 +124,50 @@ div.chatTitleWrapper {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
/* Print-specific styles for chat layout */
|
||||
@media print {
|
||||
/* Chat container adjustments */
|
||||
div.main {
|
||||
height: auto !important;
|
||||
max-height: none !important;
|
||||
width: 100% !important;
|
||||
margin: 0 !important;
|
||||
padding: 0 !important;
|
||||
overflow: visible !important;
|
||||
}
|
||||
|
||||
div.chatBox,
|
||||
div.chatBoxBody,
|
||||
div.chatLayout {
|
||||
height: auto !important;
|
||||
max-height: none !important;
|
||||
width: 100% !important;
|
||||
display: block !important;
|
||||
margin: 0 !important;
|
||||
padding: 0 !important;
|
||||
overflow: visible !important;
|
||||
position: static !important;
|
||||
}
|
||||
|
||||
div.chatBodyFull,
|
||||
div.chatBody {
|
||||
display: block !important;
|
||||
width: 100% !important;
|
||||
height: auto !important;
|
||||
max-height: none !important;
|
||||
grid-template-columns: none !important;
|
||||
overflow: visible !important;
|
||||
position: static !important;
|
||||
}
|
||||
|
||||
div.inputBox {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
/* Make chat content use full width in print */
|
||||
.chatHistory {
|
||||
width: 100% !important;
|
||||
max-width: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -162,7 +162,7 @@ function ChatBodyData(props: ChatBodyDataProps) {
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
className={`${styles.inputBox} p-1 md:px-2 shadow-md bg-background align-middle items-center justify-center dark:bg-neutral-700 dark:border-0 dark:shadow-sm rounded-2xl md:rounded-xl h-fit ${chatHistoryCustomClassName} mr-auto ml-auto mt-auto`}
|
||||
className={`${styles.inputBox} print-hidden p-1 md:px-2 shadow-md bg-background align-middle items-center justify-center dark:bg-neutral-700 dark:border-0 dark:shadow-sm rounded-2xl md:rounded-xl h-fit ${chatHistoryCustomClassName} mr-auto ml-auto mt-auto`}
|
||||
>
|
||||
<ChatInputArea
|
||||
agentColor={agentMetadata?.color}
|
||||
@@ -180,13 +180,15 @@ function ChatBodyData(props: ChatBodyDataProps) {
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<ChatSidebar
|
||||
conversationId={conversationId}
|
||||
isActive={props.isActive}
|
||||
isOpen={props.isChatSideBarOpen}
|
||||
onOpenChange={props.setIsChatSideBarOpen}
|
||||
isMobileWidth={props.isMobileWidth}
|
||||
/>
|
||||
<div className="print-hidden">
|
||||
<ChatSidebar
|
||||
conversationId={conversationId}
|
||||
isActive={props.isActive}
|
||||
isOpen={props.isChatSideBarOpen}
|
||||
onOpenChange={props.setIsChatSideBarOpen}
|
||||
isMobileWidth={props.isMobileWidth}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -458,9 +460,11 @@ export default function Chat() {
|
||||
|
||||
return (
|
||||
<SidebarProvider>
|
||||
<AppSidebar conversationId={conversationId || ""} />
|
||||
<div className="print-hidden">
|
||||
<AppSidebar conversationId={conversationId || ""} />
|
||||
</div>
|
||||
<SidebarInset>
|
||||
<header className="flex h-16 shrink-0 items-center gap-2 border-b px-4">
|
||||
<header className="flex h-16 shrink-0 items-center gap-2 border-b px-4 print-hidden">
|
||||
<SidebarTrigger className="-ml-1" />
|
||||
<Separator orientation="vertical" className="mr-2 h-4" />
|
||||
{conversationId && (
|
||||
@@ -493,7 +497,7 @@ export default function Chat() {
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="h-12 w-12 data-[state=open]:bg-accent"
|
||||
className="h-12 w-12 data-[state=open]:bg-accent print-hidden"
|
||||
onClick={() => setIsChatSideBarOpen(!isChatSideBarOpen)}
|
||||
>
|
||||
<Joystick className="w-6 h-6" />
|
||||
|
||||
@@ -16,3 +16,37 @@ div.trainOfThought {
|
||||
padding: 8px 16px;
|
||||
margin: 12px;
|
||||
}
|
||||
|
||||
/* Print-specific styles for chat history */
|
||||
@media print {
|
||||
div.chatHistory {
|
||||
height: auto !important;
|
||||
max-height: none !important;
|
||||
overflow: visible !important;
|
||||
display: block !important;
|
||||
position: static !important;
|
||||
flex-direction: column !important;
|
||||
}
|
||||
|
||||
div.chatHistory > * {
|
||||
height: auto !important;
|
||||
max-height: none !important;
|
||||
overflow: visible !important;
|
||||
position: static !important;
|
||||
width: 100% !important;
|
||||
max-width: none !important;
|
||||
}
|
||||
|
||||
/* Show agent indicators clearly in print */
|
||||
div.agentIndicator {
|
||||
margin-bottom: 0.5rem !important;
|
||||
}
|
||||
|
||||
/* Train of thought styling for print */
|
||||
div.trainOfThought {
|
||||
border-left: 2px solid #ccc !important;
|
||||
margin: 0.5rem 0 !important;
|
||||
padding: 0.5rem 1rem !important;
|
||||
font-size: 0.9em !important;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ import { AgentData } from "@/app/components/agentCard/agentCard";
|
||||
import React from "react";
|
||||
import { useIsMobileWidth } from "@/app/common/utils";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { KhojLogo } from "../logo/khojLogo";
|
||||
|
||||
interface ChatResponse {
|
||||
status: string;
|
||||
@@ -536,6 +537,27 @@ export default function ChatHistory(props: ChatHistoryProps) {
|
||||
ref={scrollAreaRef}
|
||||
>
|
||||
<div ref={scrollableContentWrapperRef}>
|
||||
{/* Print-only header with conversation info */}
|
||||
<div className="print-only-header">
|
||||
<div className="print-header-content">
|
||||
<div className="print-header-left">
|
||||
<KhojLogo className="print-logo" />
|
||||
</div>
|
||||
<div className="print-header-right">
|
||||
<h1>{data?.slug || "Conversation with Khoj"}</h1>
|
||||
<div className="conversation-meta">
|
||||
<p>
|
||||
<strong>Agent:</strong> {constructAgentName()}
|
||||
</p>
|
||||
<p>
|
||||
<strong>Printed On:</strong> {new Date().toLocaleDateString()}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<hr />
|
||||
</div>
|
||||
|
||||
<div className={`${styles.chatHistory} ${props.customClassName}`}>
|
||||
<div ref={sentinelRef} style={{ height: "1px" }}>
|
||||
{fetchingData && <InlineLoading className="opacity-50" />}
|
||||
|
||||
@@ -198,3 +198,109 @@ div.trainOfThoughtElement ul {
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
|
||||
/* Print-specific styles for chat messages */
|
||||
@media print {
|
||||
div.chatMessageContainer {
|
||||
background: transparent !important;
|
||||
border: 1px solid #ccc !important;
|
||||
border-radius: 8px !important;
|
||||
margin: 0.5rem 0 !important;
|
||||
padding: 0.5rem !important;
|
||||
page-break-inside: avoid;
|
||||
width: 100% !important;
|
||||
max-width: none !important;
|
||||
}
|
||||
|
||||
div.chatMessageWrapper {
|
||||
padding-left: 0.5rem !important;
|
||||
padding-bottom: 0.5rem !important;
|
||||
width: 100% !important;
|
||||
max-width: none !important;
|
||||
}
|
||||
|
||||
div.you {
|
||||
background-color: #f5f5f5 !important;
|
||||
color: #000 !important;
|
||||
width: 100% !important;
|
||||
max-width: none !important;
|
||||
align-self: stretch !important;
|
||||
}
|
||||
|
||||
div.khoj {
|
||||
background-color: transparent !important;
|
||||
color: #000 !important;
|
||||
width: 100% !important;
|
||||
max-width: none !important;
|
||||
align-self: stretch !important;
|
||||
}
|
||||
|
||||
div.youfullHistory,
|
||||
div.khojfullHistory {
|
||||
width: 100% !important;
|
||||
max-width: none !important;
|
||||
}
|
||||
|
||||
div.author {
|
||||
color: #666 !important;
|
||||
font-size: 0.7rem !important;
|
||||
}
|
||||
|
||||
/* Hide interactive elements */
|
||||
div.chatFooter,
|
||||
div.chatButtons,
|
||||
button.codeCopyButton,
|
||||
button.copyButton,
|
||||
button.retryButton,
|
||||
div.feedbackButtons {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
/* Image styling for print */
|
||||
div.imagesContainer {
|
||||
display: block !important;
|
||||
overflow: visible !important;
|
||||
margin-bottom: 0.5rem !important;
|
||||
}
|
||||
|
||||
div.imageWrapper {
|
||||
margin-right: 0 !important;
|
||||
margin-bottom: 0.5rem !important;
|
||||
}
|
||||
|
||||
div.imageWrapper img,
|
||||
div.khoj div.imageWrapper img {
|
||||
width: auto !important;
|
||||
height: auto !important;
|
||||
max-width: 100% !important;
|
||||
max-height: 4in !important;
|
||||
object-fit: contain !important;
|
||||
page-break-inside: avoid;
|
||||
}
|
||||
|
||||
/* Train of thought styling for print */
|
||||
div.trainOfThought {
|
||||
border-left: 2px solid #ccc !important;
|
||||
margin: 0.5rem 0 !important;
|
||||
padding: 0.5rem !important;
|
||||
font-size: 0.9em !important;
|
||||
color: #666 !important;
|
||||
}
|
||||
|
||||
div.trainOfThought strong {
|
||||
color: #000 !important;
|
||||
}
|
||||
|
||||
div.trainOfThought.primary {
|
||||
border-left-color: #000 !important;
|
||||
}
|
||||
|
||||
div.trainOfThought.primary strong {
|
||||
color: #000 !important;
|
||||
}
|
||||
|
||||
div.trainOfThoughtElement {
|
||||
display: block !important;
|
||||
margin-bottom: 0.5rem !important;
|
||||
}
|
||||
}
|
||||
|
||||
366
src/interface/web/app/globals-print.css
Normal file
366
src/interface/web/app/globals-print.css
Normal file
@@ -0,0 +1,366 @@
|
||||
/* Hide print-only elements on screen */
|
||||
.print-only-header {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Print-specific styles for clean PDF export */
|
||||
@media print {
|
||||
/* Show print-only header */
|
||||
.print-only-header {
|
||||
display: block !important;
|
||||
margin-bottom: 2rem !important;
|
||||
padding-bottom: 1rem !important;
|
||||
page-break-after: avoid;
|
||||
}
|
||||
|
||||
.print-header-content {
|
||||
display: flex !important;
|
||||
align-items: flex-start !important;
|
||||
gap: 1.5rem !important;
|
||||
margin-bottom: 1rem !important;
|
||||
}
|
||||
|
||||
.print-header-left {
|
||||
flex-shrink: 0 !important;
|
||||
}
|
||||
|
||||
.print-logo {
|
||||
width: 60px !important;
|
||||
height: 60px !important;
|
||||
fill: #000 !important;
|
||||
}
|
||||
|
||||
.print-header-right {
|
||||
flex: 1 !important;
|
||||
}
|
||||
|
||||
.print-only-header h1 {
|
||||
font-size: 28pt !important;
|
||||
font-weight: bold !important;
|
||||
color: #000 !important;
|
||||
margin: 0 0 0.75rem 0 !important;
|
||||
line-height: 1.2 !important;
|
||||
page-break-after: avoid;
|
||||
}
|
||||
|
||||
.conversation-meta {
|
||||
display: flex !important;
|
||||
flex-direction: column !important;
|
||||
gap: 0.25rem !important;
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
|
||||
.conversation-meta p {
|
||||
margin: 0 !important;
|
||||
font-size: 14pt !important;
|
||||
color: #000 !important;
|
||||
line-height: 1.3 !important;
|
||||
}
|
||||
|
||||
.conversation-meta strong {
|
||||
font-weight: bold !important;
|
||||
color: #000 !important;
|
||||
}
|
||||
|
||||
.print-only-header hr {
|
||||
border: none !important;
|
||||
height: 2px !important;
|
||||
background-color: #000 !important;
|
||||
margin: 1rem 0 0 0 !important;
|
||||
width: 100% !important;
|
||||
}
|
||||
/* Hide non-essential elements */
|
||||
.sidebar,
|
||||
.sidebar-trigger,
|
||||
.sidebar-inset > header,
|
||||
.print-hidden,
|
||||
button,
|
||||
nav,
|
||||
[data-sidebar],
|
||||
[data-sidebar-trigger],
|
||||
.chat-sidebar,
|
||||
.app-sidebar {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
/* Reset page margins and layout */
|
||||
@page {
|
||||
margin: 0.5in;
|
||||
size: A4;
|
||||
}
|
||||
|
||||
/* Main layout adjustments for print */
|
||||
body {
|
||||
font-size: 12pt !important;
|
||||
line-height: 1.4 !important;
|
||||
color: #000 !important;
|
||||
background: white !important;
|
||||
}
|
||||
|
||||
/* Remove background colors and shadows */
|
||||
* {
|
||||
background: transparent !important;
|
||||
box-shadow: none !important;
|
||||
text-shadow: none !important;
|
||||
animation: none !important;
|
||||
transition: none !important;
|
||||
}
|
||||
|
||||
/* Remove any height constraints that could limit content */
|
||||
div,
|
||||
section,
|
||||
main,
|
||||
article,
|
||||
aside {
|
||||
max-height: none !important;
|
||||
}
|
||||
|
||||
/* Specific height fixes for flex containers */
|
||||
[style*="height"],
|
||||
[style*="max-height"] {
|
||||
height: auto !important;
|
||||
max-height: none !important;
|
||||
}
|
||||
|
||||
/* Ensure all content is visible - target all scroll containers */
|
||||
.scroll-area,
|
||||
.scroll-area > div,
|
||||
[data-radix-scroll-area-viewport],
|
||||
[data-radix-scroll-area-scrollbar],
|
||||
[data-radix-scroll-area-content] {
|
||||
height: auto !important;
|
||||
max-height: none !important;
|
||||
overflow: visible !important;
|
||||
position: static !important;
|
||||
}
|
||||
|
||||
/* Chat history styling */
|
||||
.chat-history,
|
||||
.chat-history > *,
|
||||
.chat-body,
|
||||
.chat-body-full {
|
||||
display: block !important;
|
||||
width: 100% !important;
|
||||
height: auto !important;
|
||||
max-height: none !important;
|
||||
overflow: visible !important;
|
||||
position: static !important;
|
||||
}
|
||||
|
||||
/* Ensure chat messages container expands fully */
|
||||
.chat-messages,
|
||||
.chat-messages-container,
|
||||
.messages-container {
|
||||
height: auto !important;
|
||||
max-height: none !important;
|
||||
overflow: visible !important;
|
||||
}
|
||||
|
||||
/* Make chat messages use full width in print */
|
||||
.w-4\/6,
|
||||
.w-2\/3,
|
||||
.w-3\/4,
|
||||
.max-w-2xl,
|
||||
.max-w-3xl,
|
||||
.max-w-4xl,
|
||||
.max-w-5xl {
|
||||
width: 100% !important;
|
||||
max-width: none !important;
|
||||
}
|
||||
|
||||
/* Message styling for print */
|
||||
.chat-message {
|
||||
page-break-inside: avoid;
|
||||
margin-bottom: 1rem !important;
|
||||
padding: 0.5rem !important;
|
||||
border-bottom: 1px solid #ccc !important;
|
||||
width: 100% !important;
|
||||
max-width: none !important;
|
||||
}
|
||||
|
||||
/* Ensure message containers use full width */
|
||||
.chat-message-container,
|
||||
.chat-message-wrapper {
|
||||
width: 100% !important;
|
||||
max-width: none !important;
|
||||
margin-left: 0 !important;
|
||||
margin-right: 0 !important;
|
||||
}
|
||||
|
||||
/* Title styling */
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
page-break-after: avoid;
|
||||
color: #000 !important;
|
||||
font-weight: bold !important;
|
||||
margin-top: 0.5rem !important;
|
||||
margin-bottom: 0.5rem !important;
|
||||
}
|
||||
|
||||
/* Code block styling */
|
||||
pre,
|
||||
code {
|
||||
font-family: "Courier New", monospace !important;
|
||||
font-size: 0.85em !important;
|
||||
white-space: pre-wrap !important;
|
||||
word-wrap: break-word !important;
|
||||
border: 1px solid #ccc !important;
|
||||
padding: 0.5rem !important;
|
||||
margin: 0.5rem 0 !important;
|
||||
background: #f9f9f9 !important;
|
||||
color: #000 !important;
|
||||
}
|
||||
|
||||
/* Link styling */
|
||||
a {
|
||||
color: #000 !important;
|
||||
text-decoration: underline !important;
|
||||
}
|
||||
|
||||
/* Image handling */
|
||||
img {
|
||||
max-width: 100% !important;
|
||||
height: auto !important;
|
||||
page-break-inside: avoid;
|
||||
}
|
||||
|
||||
/* Force black text color */
|
||||
p,
|
||||
div,
|
||||
span,
|
||||
li,
|
||||
td,
|
||||
th {
|
||||
color: #000 !important;
|
||||
}
|
||||
|
||||
/* Lists */
|
||||
ol,
|
||||
ul {
|
||||
margin-left: 1rem !important;
|
||||
margin-bottom: 0.5rem !important;
|
||||
}
|
||||
|
||||
/* Table styling */
|
||||
table {
|
||||
border-collapse: collapse !important;
|
||||
width: 100% !important;
|
||||
margin: 0.5rem 0 !important;
|
||||
}
|
||||
|
||||
table,
|
||||
th,
|
||||
td {
|
||||
border: 1px solid #000 !important;
|
||||
padding: 0.25rem !important;
|
||||
}
|
||||
|
||||
/* Paragraphs */
|
||||
p {
|
||||
margin-bottom: 0.5rem !important;
|
||||
color: #000 !important;
|
||||
}
|
||||
|
||||
/* Ensure proper spacing */
|
||||
.chat-message + .chat-message {
|
||||
margin-top: 1rem !important;
|
||||
}
|
||||
|
||||
/* Hide scroll indicators */
|
||||
::-webkit-scrollbar {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
/* Hide interactive elements */
|
||||
.retry-button,
|
||||
.action-button,
|
||||
.chat-footer,
|
||||
.chat-buttons,
|
||||
.code-copy-button,
|
||||
.copy-button,
|
||||
.feedback-buttons {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
/* Train of thought styling for print */
|
||||
.train-of-thought {
|
||||
border-left: 2px solid #ccc !important;
|
||||
margin: 0.5rem 0 !important;
|
||||
padding: 0.5rem !important;
|
||||
font-size: 0.9em !important;
|
||||
color: #666 !important;
|
||||
}
|
||||
|
||||
.train-of-thought strong {
|
||||
color: #000 !important;
|
||||
}
|
||||
|
||||
.train-of-thought.primary {
|
||||
border-left-color: #000 !important;
|
||||
}
|
||||
|
||||
.train-of-thought-element {
|
||||
display: block !important;
|
||||
margin-bottom: 0.5rem !important;
|
||||
}
|
||||
|
||||
/* Chat message container styling */
|
||||
.chat-message-container {
|
||||
background: transparent !important;
|
||||
border: 1px solid #ccc !important;
|
||||
border-radius: 8px !important;
|
||||
margin: 0.5rem 0 !important;
|
||||
padding: 0.5rem !important;
|
||||
page-break-inside: avoid;
|
||||
}
|
||||
|
||||
.chat-message-wrapper {
|
||||
padding-left: 0.5rem !important;
|
||||
padding-bottom: 0.5rem !important;
|
||||
}
|
||||
|
||||
.you {
|
||||
background-color: #f5f5f5 !important;
|
||||
color: #000 !important;
|
||||
}
|
||||
|
||||
.khoj {
|
||||
background-color: transparent !important;
|
||||
color: #000 !important;
|
||||
}
|
||||
|
||||
.author {
|
||||
color: #666 !important;
|
||||
font-size: 0.7rem !important;
|
||||
}
|
||||
|
||||
/* Image containers */
|
||||
.images-container {
|
||||
display: block !important;
|
||||
overflow: visible !important;
|
||||
margin-bottom: 0.5rem !important;
|
||||
}
|
||||
|
||||
.image-wrapper {
|
||||
margin-right: 0 !important;
|
||||
margin-bottom: 0.5rem !important;
|
||||
}
|
||||
|
||||
.image-wrapper img {
|
||||
width: auto !important;
|
||||
height: auto !important;
|
||||
max-width: 100% !important;
|
||||
max-height: 4in !important;
|
||||
object-fit: contain !important;
|
||||
page-break-inside: avoid;
|
||||
}
|
||||
|
||||
/* Agent indicators */
|
||||
.agent-indicator {
|
||||
margin-bottom: 0.5rem !important;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
import type { Metadata } from "next";
|
||||
import { noto_sans, noto_sans_arabic } from "@/app/fonts";
|
||||
import "./globals.css";
|
||||
import "./globals-print.css";
|
||||
import { ContentSecurityPolicy } from "./common/layoutHelper";
|
||||
import { ThemeProvider } from "./components/providers/themeProvider";
|
||||
|
||||
|
||||
Reference in New Issue
Block a user