@p10node/office-engine 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -344,7 +344,7 @@ async function ce(e, t, n = "application/zip") {
344
344
  let n = await ae(oe(t)), { time: r, date: i } = se();
345
345
  l(e, 8, n.crc, n.csize, n.size, r, i, n.chunks);
346
346
  };
347
- for (let t of e.list()) {
347
+ for (let t of e?.list() ?? []) {
348
348
  let n = E(t.name), r = c.get(n);
349
349
  if (r) {
350
350
  c.delete(n), r.data !== null && await u(t.name, r.data);
@@ -0,0 +1,29 @@
1
+ import type { RunSet } from './docx/types';
2
+ /** Separator of a delimited text file: tab, semicolon (European CSV) or comma. */
3
+ export declare function detectSeparator(text: string): string;
4
+ /** RFC 4180 parser (quoted fields, escaped quotes, CRLF / LF, newlines inside quotes). */
5
+ export declare function parseCsv(text: string, sep?: string): string[][];
6
+ /**
7
+ * Builds a one-sheet workbook from rows of text.
8
+ * `decimalComma`: "12,5" is a number (semicolon-separated files from comma-decimal locales).
9
+ */
10
+ export declare function rowsToXlsx(rows: string[][], sheetName?: string, decimalComma?: boolean): Promise<Blob>;
11
+ export interface ImportRun {
12
+ text: string;
13
+ set?: RunSet;
14
+ href?: string;
15
+ }
16
+ export interface ImportPara {
17
+ runs: ImportRun[];
18
+ /** heading level 1-6 */
19
+ heading?: number;
20
+ list?: 'bullet' | 'number';
21
+ }
22
+ /** One paragraph per line. */
23
+ export declare function textToParas(text: string): ImportPara[];
24
+ /** Small CommonMark subset → HTML: headings, paragraphs, lists, quotes, code blocks, rules, tables. */
25
+ export declare function markdownToHtml(md: string): string;
26
+ /** Builds a document (Letter, 1" margins, Calibri 11) from paragraphs of formatted runs. */
27
+ export declare function parasToDocx(paras: ImportPara[]): Promise<Blob>;
28
+ /** One empty title slide, 16:9, Office theme; layouts: title, title and content, blank. */
29
+ export declare function blankPptx(): Promise<Blob>;
@@ -6,4 +6,6 @@ export type PartData = Uint8Array | string | (() => ReadableStream<Uint8Array>);
6
6
  * zero-copy when the source is a Blob); changed/added entries are deflated.
7
7
  * `changes`: path → new data, or null to delete.
8
8
  */
9
- export declare function rebuildZip(zip: ZipArchive, changes: Map<string, PartData | null>, type?: string): Promise<Blob>;
9
+ export declare function rebuildZip(zip: ZipArchive | null, changes: Map<string, PartData | null>, type?: string): Promise<Blob>;
10
+ /** Writes a new archive (all entries deflated). */
11
+ export declare function writeZip(entries: Map<string, PartData>, type?: string): Promise<Blob>;
@@ -10,7 +10,7 @@ export declare function mount(el: HTMLElement, options: MountOptions): MountedVi
10
10
  /**
11
11
  * `<office-viewer src="/file.xlsx" style="height:600px"></office-viewer>`
12
12
  * Rendered inside a shadow root so host page CSS cannot leak in.
13
- * Attributes: src, name, editable, toolbar ("false" to hide), thumbnails ("false" to hide);
13
+ * Attributes: src, name, editable, toolbar / thumbnails / save / download / file-menu / open ("false" to hide);
14
14
  * real-time collaboration: collab (hub WebSocket URL), room, user (display name).
15
15
  * Property: `file` (File/Blob/ArrayBuffer) for local data.
16
16
  */
@@ -1,5 +1,6 @@
1
1
  import './core/default-worker';
2
2
  export { DocViewer, type DocViewerProps } from './react/DocViewer';
3
+ export type { FileMenuItem } from './react/file/FileMenu';
3
4
  export { DocxView, type DocxViewProps } from './react/docx/DocxView';
4
5
  export { XlsxView, type XlsxViewProps } from './react/xlsx/XlsxView';
5
6
  export { PptxView, type PptxViewProps } from './react/pptx/PptxView';
@@ -2,6 +2,7 @@ import { type CSSProperties, type ReactNode } from 'react';
2
2
  import type { DocSource } from '../core/source';
3
3
  import type { DocumentClient, OpenOptions } from '../core/worker/client';
4
4
  import type { Zoom } from './common/ZoomControl';
5
+ import { type FileMenuItem } from './file/FileMenu';
5
6
  import type { CollabOptions } from '../collab';
6
7
  export interface DocViewerProps {
7
8
  /** File, Blob, ArrayBuffer, Uint8Array or URL (HTTP Range requests are used when the server supports them). */
@@ -30,6 +31,23 @@ export interface DocViewerProps {
30
31
  editable?: boolean;
31
32
  /** Show the Edit/View toggle. Default true. */
32
33
  allowEdit?: boolean;
34
+ /** Show the Save/Download button and enable Ctrl+S. Default true. */
35
+ allowSave?: boolean;
36
+ /**
37
+ * Allow downloading files to the user's device (the save fallback without `onSave`, "Download image").
38
+ * With `onSave`, saving still works when this is false. Default true.
39
+ */
40
+ allowDownload?: boolean;
41
+ /** Show the File menu (new, open, save, rename, download as PDF / HTML / text…, details, print) and its shortcuts. Default true. */
42
+ fileMenu?: boolean;
43
+ /** File → New / Open: replace the document with a blank one or a local file (Office, CSV, text, Markdown, HTML). Default true. */
44
+ allowOpen?: boolean;
45
+ /** Extra File menu entries (Share, Version history…), shown before Close. */
46
+ fileMenuItems?: FileMenuItem[];
47
+ /** File → Rename: called with the new name (extension included). Rename is shown when `fileMenu` is on. */
48
+ onRename?: (name: string) => void;
49
+ /** Adds File → Close. */
50
+ onClose?: () => void;
33
51
  /** Receives the saved file instead of downloading it. */
34
52
  onSave?: (file: Blob, name: string) => void | Promise<void>;
35
53
  /** Called when the document gets / loses unsaved changes. */
@@ -20,6 +20,14 @@ export declare function PromptDialog({ spec, onClose }: {
20
20
  spec: PromptSpec;
21
21
  onClose: () => void;
22
22
  }): import("react").JSX.Element;
23
+ /** Yes / no question (no window.confirm: works in sandboxed / embedded pages). */
24
+ export declare function ConfirmDialog(props: {
25
+ title: string;
26
+ message: ReactNode;
27
+ ok: string;
28
+ onOk: () => void;
29
+ onClose: () => void;
30
+ }): import("react").JSX.Element;
23
31
  /** Short-lived message at the bottom of the viewer. */
24
32
  export declare function Toast({ text, onDone }: {
25
33
  text: string;
@@ -1,5 +1,17 @@
1
1
  import { type ReactNode } from 'react';
2
2
  import type { DocumentClient } from '../../core/worker/client';
3
+ import type { Printable } from '../file/print';
4
+ /** What the active view offers to the File menu. */
5
+ export interface FileOps {
6
+ /** all pages, for PDF / print and HTML */
7
+ printable(): Promise<Printable>;
8
+ /** text formats: 'txt' / 'md' (documents, presentations), 'csv' / 'tsv' (current sheet) */
9
+ text(format: string): Promise<string>;
10
+ /** File → Document details: [label, value] rows */
11
+ stats(): [string, string][];
12
+ /** File → Import (spreadsheets): rows into a new sheet */
13
+ importTable?(rows: string[][], name: string): Promise<void>;
14
+ }
3
15
  export interface ViewerApi {
4
16
  /** editor bar mount point (null when the viewer toolbar is hidden) */
5
17
  bar: HTMLElement | null;
@@ -8,10 +20,18 @@ export interface ViewerApi {
8
20
  setDirty: (dirty: boolean) => void;
9
21
  /** build + hand off the edited file */
10
22
  save: () => Promise<void>;
23
+ /** whether files may be downloaded to the user's device */
24
+ canDownload: boolean;
11
25
  client: DocumentClient | null;
26
+ /** set by the mounted view (see useFileOps) */
27
+ fileOps: {
28
+ current: FileOps | null;
29
+ };
12
30
  }
13
31
  export declare const ViewerContext: import("react").Context<ViewerApi>;
14
32
  export declare const useViewer: () => ViewerApi;
33
+ /** Publishes the view's file operations (latest closure each render) to the File menu. */
34
+ export declare function useFileOps(ops: FileOps): void;
15
35
  /** Renders the editor bar into the viewer's toolbar row. */
16
36
  export declare function EditorBar({ children }: {
17
37
  children: ReactNode;
@@ -4,7 +4,8 @@ export interface ImageActions {
4
4
  original: () => void;
5
5
  replace: () => void;
6
6
  alt: () => void;
7
- download: () => void;
7
+ /** undefined when downloads are disabled */
8
+ download?: () => void;
8
9
  }
9
10
  export declare function DocxToolbar(props: {
10
11
  editor: DocxEditor;
@@ -26,6 +26,8 @@ export interface DropOption<T> {
26
26
  disabled?: boolean;
27
27
  /** separator above this option */
28
28
  sep?: boolean;
29
+ /** group heading above this option */
30
+ head?: string;
29
31
  }
30
32
  /**
31
33
  * Styled dropdown (listbox). Never takes focus from the editor: mouse down is prevented and,
@@ -46,6 +48,8 @@ export declare function Dropdown<T extends string | number>(props: {
46
48
  disabled?: boolean;
47
49
  /** popup aligned to the trigger's right edge */
48
50
  alignRight?: boolean;
51
+ /** extra class of the trigger button */
52
+ className?: string;
49
53
  }): import("react").JSX.Element;
50
54
  /** Value picker (fonts, styles, formats…). */
51
55
  export declare function Select<T extends string | number>(props: {
@@ -0,0 +1,7 @@
1
+ import type { DocumentClient } from '../../core/worker/client';
2
+ export declare function DetailsDialog(props: {
3
+ client: DocumentClient;
4
+ file: [string, string][];
5
+ stats: [string, string][];
6
+ onClose: () => void;
7
+ }): import("react").JSX.Element;
@@ -0,0 +1,48 @@
1
+ import type { DocKind } from '../../core/opc';
2
+ import type { FileOps } from '../common/Slots';
3
+ import { type DropOption } from '../edit/Toolbar';
4
+ export declare const KIND_LABEL: Record<DocKind, string>;
5
+ /** Host-provided File menu entry (Share, Version history…). */
6
+ export interface FileMenuItem {
7
+ label: string;
8
+ /** built-in icon name */
9
+ icon?: string;
10
+ /** shortcut / note on the right */
11
+ hint?: string;
12
+ disabled?: boolean;
13
+ onClick: () => void;
14
+ }
15
+ export interface FileActionsOptions {
16
+ kind: DocKind | undefined;
17
+ /** file name without extension */
18
+ base: string;
19
+ /** New / Open (replace the document) */
20
+ canOpen: boolean;
21
+ /** Import into the document (spreadsheets) */
22
+ canImport: boolean;
23
+ canSave: boolean;
24
+ canDownload: boolean;
25
+ ops: {
26
+ current: FileOps | null;
27
+ };
28
+ create: (kind: DocKind) => void;
29
+ open: (file: File) => void;
30
+ save: () => Promise<void>;
31
+ /** the document in its own format */
32
+ downloadOriginal: () => Promise<void>;
33
+ rename?: () => void;
34
+ details?: () => void;
35
+ close?: () => void;
36
+ extra: FileMenuItem[];
37
+ notify: (message: string) => void;
38
+ }
39
+ export interface FileActions {
40
+ options: DropOption<string>[];
41
+ busy: boolean;
42
+ run: (action: string) => void;
43
+ }
44
+ /** File menu entries and their actions (also used by the Ctrl+O / Ctrl+P shortcuts). */
45
+ export declare function useFileActions(o: FileActionsOptions): FileActions;
46
+ export declare function FileMenu({ actions }: {
47
+ actions: FileActions;
48
+ }): import("react").JSX.Element | null;
@@ -0,0 +1,10 @@
1
+ /** `accept` of the File → Open picker */
2
+ export declare const OPEN_ACCEPT: string;
3
+ export interface Imported {
4
+ src: Blob;
5
+ name: string;
6
+ /** built from another format: not saved as an Office file yet */
7
+ converted: boolean;
8
+ }
9
+ /** Office files open as they are; CSV / TSV become a workbook, text / Markdown / HTML a document. */
10
+ export declare function importFile(file: File): Promise<Imported>;
@@ -0,0 +1,16 @@
1
+ import type { ReactNode } from 'react';
2
+ /** Content laid out for paper: PDF / print and standalone HTML. */
3
+ export interface Printable {
4
+ /** the pages (`.de-print-page` elements) or flowing content */
5
+ node: ReactNode;
6
+ /** CSS `@page` size: '612pt 792pt', 'landscape', 'auto' */
7
+ size: string;
8
+ /** CSS `@page` margin */
9
+ margin: string;
10
+ /** shown to the user (e.g. output was truncated) */
11
+ notice?: string;
12
+ }
13
+ /** Opens the browser print dialog for the content (choose "Save as PDF" there). */
14
+ export declare function printDocument(p: Printable, title: string): Promise<void>;
15
+ /** Standalone web page of the content (pictures embedded). */
16
+ export declare function exportHtml(p: Printable, title: string): Promise<Blob>;
@@ -0,0 +1,10 @@
1
+ import type { Block } from '../../core/docx/types';
2
+ import type { SlideModel } from '../../core/pptx/types';
3
+ /** Plain text: one line per paragraph, list markers kept, table cells separated by tabs. */
4
+ export declare function docxToText(blocks: Block[]): string;
5
+ /** Markdown: headings (outline levels), lists, bold / italic / strike, links, tables. */
6
+ export declare function docxToMarkdown(blocks: Block[]): string;
7
+ /** Text of each slide (shapes, tables, speaker notes). */
8
+ export declare function slidesToText(slides: SlideModel[]): string;
9
+ /** RFC 4180 output (CRLF; fields with separators, quotes or line breaks are quoted). */
10
+ export declare function toCsv(rows: string[][], sep?: string): string;
@@ -1,4 +1,4 @@
1
- import type { Range } from '../../core/xlsx/types';
1
+ import type { Merge, Range } from '../../core/xlsx/types';
2
2
  import type { DocumentClient } from '../../core/worker/client';
3
3
  export interface XlsxViewProps {
4
4
  client: DocumentClient;
@@ -14,3 +14,5 @@ interface Selection {
14
14
  fc: number;
15
15
  }
16
16
  export declare const rectOf: (s: Selection) => Range;
17
+ /** grows a range until it holds every merged cell it touches (like Excel / Sheets selections) */
18
+ export declare function withMerges(r: Range, merges: Merge[]): Range;
@@ -0,0 +1,17 @@
1
+ import type { DocumentClient } from '../../core/worker/client';
2
+ import type { Merge, WorkbookMeta } from '../../core/xlsx/types';
3
+ import type { Printable } from '../file/print';
4
+ import type { Axis } from './axis';
5
+ export interface SheetLayout {
6
+ rows: number;
7
+ cols: number;
8
+ rowAxis: Axis;
9
+ colAxis: Axis;
10
+ merges: Merge[];
11
+ gridLines: boolean;
12
+ done: boolean;
13
+ }
14
+ /** Shown values of the sheet as CSV / TSV (UTF-8 BOM for CSV so Excel reads accents). */
15
+ export declare function sheetText(client: DocumentClient, sheet: number, l: SheetLayout, sep: string): Promise<string>;
16
+ /** The sheet as a table (used range, hidden rows / columns left out), fitted to the paper width. */
17
+ export declare function sheetPrintable(client: DocumentClient, sheet: number, l: SheetLayout, meta: WorkbookMeta): Promise<Printable>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@p10node/office-engine",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Browser engine to view DOCX, XLSX, PPTX progressively (worker-based, range-loading, virtualized React views)",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -48,6 +48,7 @@
48
48
  "build:playground": "vite build --config vite.playground.config.ts",
49
49
  "typecheck": "tsc --noEmit",
50
50
  "test": "bun test",
51
+ "test:e2e": "bun test ./e2e --timeout 90000",
51
52
  "fixtures": "bun scripts/gen-fixtures.ts",
52
53
  "build:worker": "vite build --config vite.worker.config.ts",
53
54
  "collab": "bun scripts/collab-server.ts",