feat: add local OneNote and ONEPKG reader

This commit is contained in:
2026-07-22 17:06:03 +02:00
commit f581cbdced
93 changed files with 14949 additions and 0 deletions

View File

@@ -0,0 +1,107 @@
import type { OneNotePageDto } from '../onenote/model/dto.js';
interface PageReaderProps {
page?: OneNotePageDto;
loading: boolean;
error?: string;
}
function formatDate(value: string | undefined): string | null {
if (!value) return null;
const date = new Date(value);
if (Number.isNaN(date.getTime())) return value;
return new Intl.DateTimeFormat(undefined, {
dateStyle: 'medium',
timeStyle: 'short',
}).format(date);
}
export function PageReader({ page, loading, error }: PageReaderProps) {
if (loading) {
return (
<section className="page-reader page-reader--empty" aria-live="polite">
<p>Loading extracted page text</p>
</section>
);
}
if (error) {
return (
<section className="page-reader page-reader--error" role="alert">
<p className="eyebrow">Page unavailable</p>
<h2>Could not read this page</h2>
<p>{error}</p>
</section>
);
}
if (!page) {
return (
<section className="page-reader page-reader--empty">
<p>Select a readable page to inspect its extracted text.</p>
</section>
);
}
const createdAt = formatDate(page.createdAt);
const updatedAt = formatDate(page.updatedAt);
return (
<article className="page-reader" aria-labelledby="selected-page-title">
<header className="page-reader__header">
<p className="eyebrow">Selected page</p>
<h2 id="selected-page-title">{page.title || 'Untitled page'}</h2>
{createdAt || updatedAt ? (
<dl className="page-metadata">
{createdAt ? (
<>
<dt>Created</dt>
<dd>{createdAt}</dd>
</>
) : null}
{updatedAt ? (
<>
<dt>Modified</dt>
<dd>{updatedAt}</dd>
</>
) : null}
</dl>
) : null}
</header>
<div className="page-content">
{page.blocks.length === 0 && page.text.length === 0 ? (
<p className="empty-message">No readable plain text was extracted.</p>
) : page.blocks.length === 0 ? (
<p className="extracted-text">{page.text}</p>
) : (
page.blocks.map((block, index) => {
switch (block.kind) {
case 'text':
return (
<p className="extracted-text" key={index}>
{block.text}
</p>
);
case 'line-break':
return <br key={index} />;
case 'link':
return (
<p className="extracted-text" key={index}>
{block.text}
</p>
);
case 'unsupported':
return (
<aside className="unsupported-block" key={index}>
<strong>{block.sourceKind}</strong>
<span>{block.description}</span>
</aside>
);
}
})
)}
</div>
</article>
);
}