Skip to content

Highlighting tests #38

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jun 26, 2025
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
6 changes: 6 additions & 0 deletions examples/vite_basic/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@ const App = () => {
handleFileChange,
} = useFileLoader(AVAILABLE_FILES[0]);

console.log("test page ", testPage);

if (testPage) {
testPage.children = [undefined];
}

// Get backrefs for the currently selected file
const currentBackrefs = selectedFile?.backrefs || [];

Expand Down
108 changes: 12 additions & 96 deletions typescript/src/renderer/JsonDocRenderer.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,10 @@
import "./styles/index.css";
import React, { useEffect } from "react";
import React from "react";

import { Page } from "@/models/generated";
import { loadPage } from "@/serialization/loader";

import { BlockRenderer } from "./components/BlockRenderer";
import { PageDelimiter } from "./components/PageDelimiter";
import { JsonViewPanel } from "./components/dev/JsonViewPanel";
import { RendererProvider } from "./context/RendererContext";
import { HighlightNavigation } from "./components/HighlightNavigation";
import { useHighlights } from "./hooks/useHighlights";
import { RendererContainer } from "./components/RendererContainer";
import { Backref } from "./utils/highlightUtils";
import { GlobalErrorBoundary } from "./components/ErrorBoundary";

Expand Down Expand Up @@ -40,101 +35,22 @@ export const JsonDocRenderer = ({
backrefs = [],
onError,
}: JsonDocRendererProps) => {
// Use the modular hooks for highlight management
const { highlightCount, currentActiveIndex, navigateToHighlight } =
useHighlights({
backrefs,
});

useEffect(() => {
try {
//TODO: this is not throwing for invalid page object (one that doesn't follow schema)
loadPage(page);
} catch (_) {
// console.log("error ", error);
}
}, [page]);

// return null;
const renderedContent = (
<div className="json-doc-page">
{/* Page icon */}
{page.icon && (
<div className="json-doc-page-icon">
{page.icon.type === "emoji" && page.icon.emoji}
</div>
)}
{/* Page title */}
{page.properties?.title && (
<h1 className="json-doc-page-title" data-page-id={page.id}>
{page.properties.title.title?.[0]?.plain_text || "Untitled"}
</h1>
)}
{/* Page children blocks */}
{page.children && page.children.length > 0 && (
<div className="json-doc-page-content">
{page.children.map((block: any, index: number) => {
const currentPageNum = block.metadata?.origin?.page_num;
const nextPageNum =
index < page.children.length - 1
? (page.children[index + 1]?.metadata as any)?.origin?.page_num
: null;

// Show delimiter after the last block of each page
const showPageDelimiter =
currentPageNum &&
(nextPageNum !== currentPageNum ||
index === page.children.length - 1);

return (
<React.Fragment key={block.id || index}>
<BlockRenderer
block={block}
depth={0}
components={components}
/>

{showPageDelimiter && !components?.page_delimiter && (
<PageDelimiter pageNumber={currentPageNum} />
)}
{showPageDelimiter && components?.page_delimiter && (
<components.page_delimiter pageNumber={currentPageNum} />
)}
</React.Fragment>
);
})}
</div>
)}
</div>
);

console.log("theme: ", theme);
return (
<div
className={`jsondoc-theme-${theme}`}
data-testid="jsondoc-renderer-root"
>
<GlobalErrorBoundary onError={onError}>
<RendererProvider value={{ devMode, resolveImageUrl }}>
<div
className={`json-doc-renderer${className ? " " + className : ""}`}
>
{viewJson ? (
<div className="flex h-screen">
<JsonViewPanel data={page} />
</div>
) : (
renderedContent
)}
{/* Show highlight navigation when there are highlights */}
{highlightCount > 0 && (
<HighlightNavigation
highlightCount={highlightCount}
onNavigate={navigateToHighlight}
currentIndex={currentActiveIndex}
/>
)}
</div>
</RendererProvider>
<RendererContainer
page={page}
className={className}
components={components}
devMode={devMode}
resolveImageUrl={resolveImageUrl}
viewJson={viewJson}
backrefs={backrefs}
/>
</GlobalErrorBoundary>
</div>
);
Expand Down
2 changes: 1 addition & 1 deletion typescript/src/renderer/components/ErrorBoundary.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ interface GlobalErrorBoundaryProps {
function GlobalErrorFallback({ error, resetErrorBoundary }: FallbackProps) {
console.log("error ", error);
return (
<div className="json-doc-error-boundary">
<div className="json-doc-error-boundary" role="alert">
<div className="json-doc-error-content">
<h2>Document Failed to Load</h2>
<p>Something went wrong while rendering this document.</p>
Expand Down
130 changes: 130 additions & 0 deletions typescript/src/renderer/components/RendererContainer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
import React, { useEffect } from "react";

import { Page } from "@/models/generated";
import { loadPage } from "@/serialization/loader";

import { RendererProvider } from "../context/RendererContext";
import { useHighlights } from "../hooks/useHighlights";
import { Backref } from "../utils/highlightUtils";

import { HighlightNavigation } from "./HighlightNavigation";
import { JsonViewPanel } from "./dev/JsonViewPanel";
import { BlockRenderer } from "./BlockRenderer";
import { PageDelimiter } from "./PageDelimiter";

interface RendererContainerProps {
page: Page;
className?: string;
components?: React.ComponentProps<typeof BlockRenderer>["components"] & {
page_delimiter: React.ComponentType<{
pageNumber: number;
}>;
};
devMode?: boolean;
resolveImageUrl?: (url: string) => Promise<string>;
viewJson?: boolean;
backrefs?: Backref[];
}

export const RendererContainer: React.FC<RendererContainerProps> = ({
page,
className = "",
components,
devMode = false,
resolveImageUrl,
viewJson = false,
backrefs = [],
}) => {
// Use the modular hooks for highlight management
const { highlightCount, currentActiveIndex, navigateToHighlight } =
useHighlights({
backrefs,
});

useEffect(() => {
try {
//TODO: this is not throwing for invalid page object (one that doesn't follow schema)
loadPage(page);
} catch (_) {
// console.log("error ", error);
}
}, [page]);

const renderedContent = (
<div className="json-doc-page">
{/* Page icon */}
{page.icon && (
<div className="json-doc-page-icon">
{page.icon.type === "emoji" && page.icon.emoji}
</div>
)}
{/* Page title */}
{page.properties?.title && (
<h1
className="json-doc-page-title"
data-page-id={page.id}
role="heading"
>
{page.properties.title.title?.[0]?.plain_text || "Untitled"}
</h1>
)}
{/* Page children blocks */}
{page.children && page.children.length > 0 && (
<div className="json-doc-page-content">
{page.children.map((block: any, index: number) => {
const currentPageNum = block.metadata?.origin?.page_num;
const nextPageNum =
index < page.children.length - 1
? (page.children[index + 1]?.metadata as any)?.origin?.page_num
: null;

// Show delimiter after the last block of each page
const showPageDelimiter =
currentPageNum &&
(nextPageNum !== currentPageNum ||
index === page.children.length - 1);

return (
<React.Fragment key={block.id || index}>
<BlockRenderer
block={block}
depth={0}
components={components}
/>

{showPageDelimiter && !components?.page_delimiter && (
<PageDelimiter pageNumber={currentPageNum} />
)}
{showPageDelimiter && components?.page_delimiter && (
<components.page_delimiter pageNumber={currentPageNum} />
)}
</React.Fragment>
);
})}
</div>
)}
</div>
);

return (
<RendererProvider value={{ devMode, resolveImageUrl }}>
<div className={`json-doc-renderer${className ? " " + className : ""}`}>
{viewJson ? (
<div className="flex h-screen">
<JsonViewPanel data={page} />
</div>
) : (
renderedContent
)}
{/* Show highlight navigation when there are highlights */}
{highlightCount > 0 && (
<HighlightNavigation
highlightCount={highlightCount}
onNavigate={navigateToHighlight}
currentIndex={currentActiveIndex}
/>
)}
</div>
</RendererProvider>
);
};
1 change: 1 addition & 0 deletions typescript/src/renderer/utils/highlightUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export function createHighlightSpan(text: string): HTMLSpanElement {
const highlightSpan = document.createElement("span");
highlightSpan.className = "json-doc-highlight";
highlightSpan.textContent = text;
highlightSpan.role = "note";
return highlightSpan;
}

Expand Down
Loading
Loading