@bitbaum/chatkit 0.19.0 → 0.20.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.
- package/README.md +45 -3
- package/bin/chatkit-copy-pdf-assets.mjs +38 -0
- package/dist/attachments.d.ts +21 -7
- package/dist/attachments.d.ts.map +1 -1
- package/dist/attachments.js +14 -6
- package/dist/attachments.js.map +1 -1
- package/dist/pdf.d.ts +96 -0
- package/dist/pdf.d.ts.map +1 -0
- package/dist/pdf.js +212 -0
- package/dist/pdf.js.map +1 -0
- package/dist/react/attach.d.ts +1 -1
- package/dist/react/attach.d.ts.map +1 -1
- package/dist/react/attach.js +1 -1
- package/dist/react/attach.js.map +1 -1
- package/package.json +13 -4
package/README.md
CHANGED
|
@@ -613,9 +613,12 @@ Photos, Files — each its own file input. One input with a mixed `accept`
|
|
|
613
613
|
apps (Camera, Camera, Recorder, "Photos & Videos"), where the screenshot you
|
|
614
614
|
came to send is three levels down and the file browser is not offered at all.
|
|
615
615
|
`ATTACH_SOURCE_INPUT` is the SSOT: Camera captures, Photos opens the system
|
|
616
|
-
photo picker, Files
|
|
617
|
-
|
|
618
|
-
|
|
616
|
+
photo picker, Files asks for documents only (`FILES_ACCEPT`: PDF and text)
|
|
617
|
+
and opens the real document browser. It used to ask for nothing, which Brave
|
|
618
|
+
on Android answered with the capture chooser and no Files at all: an empty
|
|
619
|
+
`accept` means "anything", and anything includes the camera. A picker can
|
|
620
|
+
still hand over other files, so `isTextFile` and `@bitbaum/chatkit/pdf`
|
|
621
|
+
decide afterwards what can be read. With a mouse the
|
|
619
622
|
paperclip opens the file dialog directly — on a desktop all three are the
|
|
620
623
|
same dialog. A box that is not chatkit's composer renders `<AttachMenu
|
|
621
624
|
onFiles={…} />` rather than a bare `<input type="file">`. An app with more to
|
|
@@ -644,6 +647,45 @@ import { AttachMenu, shrinkImage } from "@bitbaum/chatkit/attach";
|
|
|
644
647
|
maxBytes)` is the in-browser downscale the composer uses. `acceptFamilies` and
|
|
645
648
|
`ATTACH_SOURCE_INPUT` are the rule itself.
|
|
646
649
|
|
|
650
|
+
### PDFs — `@bitbaum/chatkit/pdf`
|
|
651
|
+
|
|
652
|
+
No model on a free chain reads a PDF file. So a PDF someone attaches is read
|
|
653
|
+
in the browser and handed on the way the model already understands:
|
|
654
|
+
|
|
655
|
+
```ts
|
|
656
|
+
import { isPdfFile, readPdf, sheetName, PDF_MAX_BYTES } from "@bitbaum/chatkit/pdf";
|
|
657
|
+
|
|
658
|
+
if (isPdfFile(file) && file.size <= PDF_MAX_BYTES) {
|
|
659
|
+
const r = await readPdf(file, { maxSheets: picturesLeft, assetsUrl: "/pdfjs/" });
|
|
660
|
+
if (r.kind === "text") attachText(file.name, r.text); // "--- page 3 ---" markers kept
|
|
661
|
+
else
|
|
662
|
+
r.sheets.forEach((s) => attachImage(sheetName(file.name, s.firstPage, s.lastPage), s.dataUrl));
|
|
663
|
+
// r.pagesShown < r.pageCount → tell the person which pages went
|
|
664
|
+
}
|
|
665
|
+
```
|
|
666
|
+
|
|
667
|
+
- **A PDF with a text layer** (Word, Docs, a typed report) comes back as text.
|
|
668
|
+
- **A PDF without one** (slides exported as pictures, a scan) comes back as
|
|
669
|
+
pictures of its pages, **four to a sheet** at 784px a page — slide text stays
|
|
670
|
+
legible, and a 20-page deck is 5 pictures. Send them to a model that sees.
|
|
671
|
+
- pdf.js loads only on the first PDF (its own entry point and a dynamic
|
|
672
|
+
import), and the legacy build is used so older iPhones read PDFs too.
|
|
673
|
+
|
|
674
|
+
**One step per app**: pdf.js fetches its worker and image decoders by URL at
|
|
675
|
+
runtime, which no bundler follows. Copy them as the first step of `dev` and
|
|
676
|
+
`build` (pnpm does not run `prebuild` hooks by default):
|
|
677
|
+
|
|
678
|
+
```json
|
|
679
|
+
"build": "chatkit-copy-pdf-assets public/pdfjs && next build"
|
|
680
|
+
```
|
|
681
|
+
|
|
682
|
+
and pass `assetsUrl: "/pdfjs/"`. Keep that folder out of git. Served
|
|
683
|
+
from the app's own origin they pass a strict CSP, and they include the
|
|
684
|
+
decoders without which pages whose pictures are JPEG 2000 — macOS's "reduce
|
|
685
|
+
file size" export — draw blank. `test/browser-pdf.mjs` checks both
|
|
686
|
+
kinds in Chromium; `node test/browser-pdf.mjs your.pdf` prints what any PDF
|
|
687
|
+
turns into.
|
|
688
|
+
|
|
647
689
|
### The check every app runs in CI
|
|
648
690
|
|
|
649
691
|
```sh
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Copies pdf.js's runtime files where an app serves static files, so
|
|
3
|
+
// `readPdf(file, { assetsUrl })` from @bitbaum/chatkit/pdf can fetch them.
|
|
4
|
+
//
|
|
5
|
+
// chatkit-copy-pdf-assets public/pdfjs → readPdf(file, { assetsUrl: "/pdfjs/" })
|
|
6
|
+
//
|
|
7
|
+
// pdf.js loads its worker and its image decoders (JPEG 2000, JBIG2) by URL at
|
|
8
|
+
// runtime, which no bundler follows. Without them a page whose pictures are
|
|
9
|
+
// JPEG 2000 — macOS's "reduce file size" PDF export — draws blank (found
|
|
10
|
+
// 2026-10-27 on an investor deck). Run it as the first step of the app's dev
|
|
11
|
+
// and build scripts (pnpm skips `prebuild` hooks by default) so the files
|
|
12
|
+
// always match the installed pdf.js; the folder stays out of git. Idempotent.
|
|
13
|
+
import { cpSync, existsSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
|
|
14
|
+
import { createRequire } from "node:module";
|
|
15
|
+
import { dirname, join, resolve } from "node:path";
|
|
16
|
+
import { PDF_ASSET_DIRS, PDF_WORKER_FILE } from "../dist/pdf.js";
|
|
17
|
+
|
|
18
|
+
const dest = resolve(process.argv[2] ?? "public/pdfjs");
|
|
19
|
+
const require = createRequire(import.meta.url);
|
|
20
|
+
const pkgJson = require.resolve("pdfjs-dist/package.json");
|
|
21
|
+
const root = dirname(pkgJson);
|
|
22
|
+
const { version } = require(pkgJson);
|
|
23
|
+
|
|
24
|
+
rmSync(dest, { recursive: true, force: true });
|
|
25
|
+
mkdirSync(dest, { recursive: true });
|
|
26
|
+
cpSync(join(root, "legacy", "build", PDF_WORKER_FILE), join(dest, PDF_WORKER_FILE));
|
|
27
|
+
for (const dir of PDF_ASSET_DIRS) {
|
|
28
|
+
const from = join(root, dir);
|
|
29
|
+
if (!existsSync(from)) {
|
|
30
|
+
console.error(`chatkit-copy-pdf-assets: pdfjs-dist ${version} has no ${dir}/`);
|
|
31
|
+
process.exit(1);
|
|
32
|
+
}
|
|
33
|
+
// quickjs-eval is pdf.js's sandbox for a PDF's own JavaScript, which a chat
|
|
34
|
+
// never runs. Leaving it out keeps a document's scripts from having anywhere to go.
|
|
35
|
+
cpSync(from, join(dest, dir), { recursive: true, filter: (p) => !p.includes("quickjs") });
|
|
36
|
+
}
|
|
37
|
+
writeFileSync(join(dest, "VERSION"), `pdfjs-dist ${version}\n`);
|
|
38
|
+
console.log(`chatkit-copy-pdf-assets: pdfjs-dist ${version} → ${dest}`);
|
package/dist/attachments.d.ts
CHANGED
|
@@ -62,11 +62,11 @@ export declare function fitWithin(width: number, height: number, maxEdge: number
|
|
|
62
62
|
export declare function dataUrlBytes(dataUrl: string): number;
|
|
63
63
|
export declare function isImageMime(mime: string): boolean;
|
|
64
64
|
/**
|
|
65
|
-
* Whether a file is text the model can read inline. The Files picker
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
*
|
|
65
|
+
* Whether a file is text the model can read inline. The Files picker asks for
|
|
66
|
+
* documents (FILES_ACCEPT) but a picker may still hand over anything, so the
|
|
67
|
+
* decision about what can actually be sent stays here, after the pick, where
|
|
68
|
+
* a PDF gets a sentence instead of being read as binary noise and pasted into
|
|
69
|
+
* a prompt.
|
|
70
70
|
*/
|
|
71
71
|
export declare function isTextFile(name: string, mime: string): boolean;
|
|
72
72
|
/**
|
|
@@ -80,10 +80,24 @@ export declare function isTextFile(name: string, mime: string): boolean;
|
|
|
80
80
|
* - camera: `capture` opens the camera straight away.
|
|
81
81
|
* - photos: `image/*` alone opens the system photo picker (Screenshots is its
|
|
82
82
|
* first album), not a chooser.
|
|
83
|
-
* - files:
|
|
84
|
-
* Drive, any folder.
|
|
83
|
+
* - files: `FILES_ACCEPT`, documents only (PDF and text), opens the real
|
|
84
|
+
* document browser — Downloads, Drive, any folder. It used to be no `accept`
|
|
85
|
+
* at all, which Chrome answered with that browser but Brave on Android
|
|
86
|
+
* (2026-10-27, OrangeCat) answered with the capture chooser above, Files
|
|
87
|
+
* missing: an empty `accept` means "anything", and "anything" includes the
|
|
88
|
+
* camera and the recorder. Naming only document types takes every capture
|
|
89
|
+
* app off the table on both. The type check still happens after the pick,
|
|
90
|
+
* in `isTextFile` and the app's own reader (`@bitbaum/chatkit/pdf`).
|
|
85
91
|
*/
|
|
86
92
|
export type AttachSource = "camera" | "photos" | "files";
|
|
93
|
+
/**
|
|
94
|
+
* What the Files source asks the phone for: PDFs and text, one "document"
|
|
95
|
+
* family. MIME types first (what Android and iOS map to their pickers), then
|
|
96
|
+
* the extensions a desktop file dialog filters by. Only types every platform
|
|
97
|
+
* knows: an extension a phone cannot map can widen the request back to
|
|
98
|
+
* "anything" — the capture chooser this exists to avoid.
|
|
99
|
+
*/
|
|
100
|
+
export declare const FILES_ACCEPT = "application/pdf,text/*,application/json,.pdf,.txt,.md,.markdown,.csv,.tsv,.json";
|
|
87
101
|
export declare const ATTACH_SOURCE_INPUT: Record<AttachSource, {
|
|
88
102
|
accept?: string;
|
|
89
103
|
capture?: "environment";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"attachments.d.ts","sourceRoot":"","sources":["../src/attachments.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,MAAM,cAAc,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAC7E,MAAM,MAAM,eAAe,GAAG;IAC5B,IAAI,EAAE,OAAO,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AACF,MAAM,MAAM,UAAU,GAAG,cAAc,GAAG,eAAe,CAAC;AAE1D,uDAAuD;AACvD,MAAM,MAAM,gBAAgB,GAAG,UAAU,GAAG;IAAE,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAEpE,MAAM,MAAM,gBAAgB,GAAG;IAC7B,yBAAyB;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,8DAA8D;IAC9D,YAAY,EAAE,MAAM,CAAC;IACrB,mEAAmE;IACnE,aAAa,EAAE,MAAM,CAAC;IACtB;;;;;;;;OAQG;IACH,YAAY,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,eAAO,MAAM,yBAAyB,EAAE,gBAKvC,CAAC;AAEF;yBACyB;AACzB,MAAM,MAAM,eAAe,GAAG;IAC5B,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;IACpC,aAAa,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;IACvD,YAAY,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,KAAK,MAAM,CAAC;IACjE,UAAU,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;IACrC,OAAO,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,CAAC;CAClC,CAAC;AAEF,eAAO,MAAM,wBAAwB,EAAE,eAMtC,CAAC;AAEF;;;GAGG;AACH,wBAAgB,SAAS,CACvB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,GACd;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,OAAO,CAAA;CAAE,CASpD;AAED,+EAA+E;AAC/E,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAEpD;AAID,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEjD;AAQD;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAI9D;AAED
|
|
1
|
+
{"version":3,"file":"attachments.d.ts","sourceRoot":"","sources":["../src/attachments.ts"],"names":[],"mappings":"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"}
|
package/dist/attachments.js
CHANGED
|
@@ -45,11 +45,11 @@ export function isImageMime(mime) {
|
|
|
45
45
|
const TEXT_EXTENSIONS = /\.(txt|md|markdown|json|jsonl|csv|tsv|log|ya?ml|toml|ini|xml|html?|css|scss|js|jsx|mjs|cjs|ts|tsx|py|rb|go|rs|java|kt|swift|c|h|cpp|hpp|cs|php|sh|bash|zsh|sql|env|diff|patch|srt|vtt)$/i;
|
|
46
46
|
const TEXT_MIME = /^(text\/|application\/(json|xml|x-yaml|yaml|javascript|x-sh|sql))/i;
|
|
47
47
|
/**
|
|
48
|
-
* Whether a file is text the model can read inline. The Files picker
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
48
|
+
* Whether a file is text the model can read inline. The Files picker asks for
|
|
49
|
+
* documents (FILES_ACCEPT) but a picker may still hand over anything, so the
|
|
50
|
+
* decision about what can actually be sent stays here, after the pick, where
|
|
51
|
+
* a PDF gets a sentence instead of being read as binary noise and pasted into
|
|
52
|
+
* a prompt.
|
|
53
53
|
*/
|
|
54
54
|
export function isTextFile(name, mime) {
|
|
55
55
|
if (TEXT_MIME.test(mime))
|
|
@@ -58,10 +58,18 @@ export function isTextFile(name, mime) {
|
|
|
58
58
|
return false;
|
|
59
59
|
return TEXT_EXTENSIONS.test(name);
|
|
60
60
|
}
|
|
61
|
+
/**
|
|
62
|
+
* What the Files source asks the phone for: PDFs and text, one "document"
|
|
63
|
+
* family. MIME types first (what Android and iOS map to their pickers), then
|
|
64
|
+
* the extensions a desktop file dialog filters by. Only types every platform
|
|
65
|
+
* knows: an extension a phone cannot map can widen the request back to
|
|
66
|
+
* "anything" — the capture chooser this exists to avoid.
|
|
67
|
+
*/
|
|
68
|
+
export const FILES_ACCEPT = "application/pdf,text/*,application/json,.pdf,.txt,.md,.markdown,.csv,.tsv,.json";
|
|
61
69
|
export const ATTACH_SOURCE_INPUT = {
|
|
62
70
|
camera: { accept: "image/*", capture: "environment" },
|
|
63
71
|
photos: { accept: "image/*" },
|
|
64
|
-
files: {},
|
|
72
|
+
files: { accept: FILES_ACCEPT },
|
|
65
73
|
};
|
|
66
74
|
const FAMILY_BY_EXTENSION = {};
|
|
67
75
|
for (const ext of [
|
package/dist/attachments.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"attachments.js","sourceRoot":"","sources":["../src/attachments.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAiCH,MAAM,CAAC,MAAM,yBAAyB,GAAqB;IACzD,QAAQ,EAAE,CAAC;IACX,YAAY,EAAE,OAAO;IACrB,aAAa,EAAE,SAAS;IACxB,YAAY,EAAE,CAAC;CAChB,CAAC;AAYF,MAAM,CAAC,MAAM,wBAAwB,GAAoB;IACvD,SAAS,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,GAAG,IAAI,0DAA0D;IACtF,aAAa,EAAE,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,GAAG,IAAI,sBAAsB,KAAK,OAAO;IACzE,YAAY,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,IAAI,sBAAsB,CAAC,gBAAgB;IACzE,UAAU,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,kBAAkB,IAAI,GAAG;IAC/C,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,SAAS,GAAG,SAAS;CACxC,CAAC;AAEF;;;GAGG;AACH,MAAM,UAAU,SAAS,CACvB,KAAa,EACb,MAAc,EACd,OAAe;IAEf,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;IACxC,IAAI,OAAO,IAAI,CAAC,IAAI,OAAO,IAAI,OAAO;QAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;IAChF,MAAM,KAAK,GAAG,OAAO,GAAG,OAAO,CAAC;IAChC,OAAO;QACL,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC,CAAC;QAC7C,MAAM,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,KAAK,CAAC,CAAC;QAC/C,MAAM,EAAE,IAAI;KACb,CAAC;AACJ,CAAC;AAED,+EAA+E;AAC/E,MAAM,UAAU,YAAY,CAAC,OAAe;IAC1C,OAAO,IAAI,CAAC,KAAK,CAAC,kBAAkB,CAAC,OAAO,CAAC,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;AAC/D,CAAC;AAED,MAAM,UAAU,GAAG,mCAAmC,CAAC;AAEvD,MAAM,UAAU,WAAW,CAAC,IAAY;IACtC,OAAO,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC/B,CAAC;AAED;4EAC4E;AAC5E,MAAM,eAAe,GACnB,0LAA0L,CAAC;AAC7L,MAAM,SAAS,GAAG,oEAAoE,CAAC;AAEvF;;;;;;GAMG;AACH,MAAM,UAAU,UAAU,CAAC,IAAY,EAAE,IAAY;IACnD,IAAI,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IACtC,IAAI,IAAI,IAAI,IAAI,KAAK,0BAA0B;QAAE,OAAO,KAAK,CAAC;IAC9D,OAAO,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACpC,CAAC;
|
|
1
|
+
{"version":3,"file":"attachments.js","sourceRoot":"","sources":["../src/attachments.ts"],"names":[],"mappings":"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"}
|
package/dist/pdf.d.ts
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @bitbaum/chatkit/pdf — a PDF a person attaches, read in the browser and
|
|
3
|
+
* handed on the way a chat model already understands: as text, or as
|
|
4
|
+
* pictures of the pages.
|
|
5
|
+
*
|
|
6
|
+
* import { isPdfFile, readPdf } from "@bitbaum/chatkit/pdf";
|
|
7
|
+
*
|
|
8
|
+
* No model on a free chain reads a PDF file, and most paid ones bill for the
|
|
9
|
+
* privilege. So the PDF never leaves the device as a PDF:
|
|
10
|
+
*
|
|
11
|
+
* - A PDF with a text layer (exported from Word, Docs, a typed report) comes
|
|
12
|
+
* back as `{ kind: "text" }` with page markers kept, so the model can say
|
|
13
|
+
* "on page 4". Attach it like a .txt file.
|
|
14
|
+
* - A PDF without one (a deck exported as slide images, a scan) comes back as
|
|
15
|
+
* `{ kind: "pages" }`: the pages rendered and packed four to a sheet.
|
|
16
|
+
* Attach the sheets like photos, to a model that can see. Four to a sheet
|
|
17
|
+
* keeps each page ~780px wide — slide text stays legible — and turns a
|
|
18
|
+
* 20-page deck into 5 pictures instead of 20.
|
|
19
|
+
*
|
|
20
|
+
* Found 2026-10-27 in OrangeCat: an investor deck (17 slide images, 13.7 MB,
|
|
21
|
+
* no text layer) could not be shared with the Cat at all.
|
|
22
|
+
*
|
|
23
|
+
* Its own entry point, so a chat that never sees a PDF never loads pdf.js;
|
|
24
|
+
* `readPdf` imports it on the first PDF. The pure parts are exported for
|
|
25
|
+
* tests and for apps that word their own notices.
|
|
26
|
+
*/
|
|
27
|
+
/** Pages per sheet, as a grid. */
|
|
28
|
+
export declare const PDF_SHEET_COLUMNS = 2;
|
|
29
|
+
export declare const PDF_SHEET_ROWS = 2;
|
|
30
|
+
export declare const PDF_PAGES_PER_SHEET: number;
|
|
31
|
+
/** Width of one page on a sheet; a sheet is two of these side by side. */
|
|
32
|
+
export declare const PDF_PAGE_WIDTH_PX = 784;
|
|
33
|
+
/** A PDF this large is refused before pdf.js opens it. */
|
|
34
|
+
export declare const PDF_MAX_BYTES: number;
|
|
35
|
+
/**
|
|
36
|
+
* Below this many non-space characters per page, on average, the text layer
|
|
37
|
+
* is treated as absent (a slide deck's page numbers, a scanner's stamp) and
|
|
38
|
+
* the pages are looked at instead.
|
|
39
|
+
*/
|
|
40
|
+
export declare const PDF_MIN_TEXT_CHARS_PER_PAGE = 60;
|
|
41
|
+
export type PdfReading = {
|
|
42
|
+
kind: "text";
|
|
43
|
+
text: string;
|
|
44
|
+
pageCount: number;
|
|
45
|
+
} | {
|
|
46
|
+
kind: "pages";
|
|
47
|
+
sheets: PdfSheet[];
|
|
48
|
+
pageCount: number;
|
|
49
|
+
pagesShown: number;
|
|
50
|
+
};
|
|
51
|
+
export interface PdfSheet {
|
|
52
|
+
/** 1-based, inclusive. */
|
|
53
|
+
firstPage: number;
|
|
54
|
+
lastPage: number;
|
|
55
|
+
dataUrl: string;
|
|
56
|
+
}
|
|
57
|
+
export declare function isPdfFile(file: {
|
|
58
|
+
name: string;
|
|
59
|
+
type: string;
|
|
60
|
+
}): boolean;
|
|
61
|
+
/** True when the extracted text is the document, not a few stray labels. */
|
|
62
|
+
export declare function isTextRich(pageTexts: string[]): boolean;
|
|
63
|
+
/** The pages as one text, each under a marker the Cat can cite. */
|
|
64
|
+
export declare function joinPageTexts(pageTexts: string[]): string;
|
|
65
|
+
/**
|
|
66
|
+
* Which pages go on which sheet, for at most `maxSheets` sheets. Pages past
|
|
67
|
+
* what fits are left out, and the caller says so.
|
|
68
|
+
*/
|
|
69
|
+
export declare function planSheets(pageCount: number, maxSheets: number): number[][];
|
|
70
|
+
/** "deck.pdf · pages 5–8", or "· page 9" for a sheet of one. */
|
|
71
|
+
export declare function sheetName(fileName: string, firstPage: number, lastPage: number): string;
|
|
72
|
+
/**
|
|
73
|
+
* Where pdf.js's own runtime files live — the worker, the image decoders
|
|
74
|
+
* (JPEG 2000, JBIG2), CMaps, standard fonts, ICC profiles. They are fetched by
|
|
75
|
+
* URL at runtime, which a bundler cannot follow, so an app copies them into its
|
|
76
|
+
* static folder with `chatkit-copy-pdf-assets public/pdfjs` and passes
|
|
77
|
+
* `assetsUrl: "/pdfjs/"`. Served from the app's own origin, they also pass a
|
|
78
|
+
* strict Content-Security-Policy. Missing decoders are why a page whose
|
|
79
|
+
* pictures are JPEG 2000 — macOS's "reduce file size" export, the deck this
|
|
80
|
+
* was written for — draws blank.
|
|
81
|
+
*/
|
|
82
|
+
export declare const PDF_ASSET_DIRS: readonly ["wasm", "cmaps", "standard_fonts", "iccs"];
|
|
83
|
+
export declare const PDF_WORKER_FILE = "pdf.worker.min.mjs";
|
|
84
|
+
export interface ReadPdfOptions {
|
|
85
|
+
/** How many pictures the message still has room for. A text PDF needs none. */
|
|
86
|
+
maxSheets: number;
|
|
87
|
+
/** Base URL of the files `chatkit-copy-pdf-assets` copied, e.g. "/pdfjs/". */
|
|
88
|
+
assetsUrl: string;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Read a picked PDF. Throws on a file pdf.js cannot open (encrypted, corrupt)
|
|
92
|
+
* — the app words that for the person. `PDF_MAX_BYTES` is checked by the app
|
|
93
|
+
* before calling, so it can say so in its own words.
|
|
94
|
+
*/
|
|
95
|
+
export declare function readPdf(file: Blob | Uint8Array, opts: ReadPdfOptions): Promise<PdfReading>;
|
|
96
|
+
//# sourceMappingURL=pdf.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pdf.d.ts","sourceRoot":"","sources":["../src/pdf.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,kCAAkC;AAClC,eAAO,MAAM,iBAAiB,IAAI,CAAC;AACnC,eAAO,MAAM,cAAc,IAAI,CAAC;AAChC,eAAO,MAAM,mBAAmB,QAAqC,CAAC;AAEtE,0EAA0E;AAC1E,eAAO,MAAM,iBAAiB,MAAM,CAAC;AAErC,0DAA0D;AAC1D,eAAO,MAAM,aAAa,QAAmB,CAAC;AAE9C;;;;GAIG;AACH,eAAO,MAAM,2BAA2B,KAAK,CAAC;AAI9C,MAAM,MAAM,UAAU,GAClB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GACjD;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,QAAQ,EAAE,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAAC;AAEjF,MAAM,WAAW,QAAQ;IACvB,0BAA0B;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAEvE;AAED,4EAA4E;AAC5E,wBAAgB,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,OAAO,CAMvD;AAED,mEAAmE;AACnE,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,MAAM,CAKzD;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE,EAAE,CAW3E;AAED,gEAAgE;AAChE,wBAAgB,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAIvF;AAWD;;;;;;;;;GASG;AACH,eAAO,MAAM,cAAc,sDAAuD,CAAC;AACnF,eAAO,MAAM,eAAe,uBAAuB,CAAC;AAwFpD,MAAM,WAAW,cAAc;IAC7B,+EAA+E;IAC/E,SAAS,EAAE,MAAM,CAAC;IAClB,8EAA8E;IAC9E,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;;GAIG;AACH,wBAAsB,OAAO,CAAC,IAAI,EAAE,IAAI,GAAG,UAAU,EAAE,IAAI,EAAE,cAAc,GAAG,OAAO,CAAC,UAAU,CAAC,CA6BhG"}
|
package/dist/pdf.js
ADDED
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @bitbaum/chatkit/pdf — a PDF a person attaches, read in the browser and
|
|
3
|
+
* handed on the way a chat model already understands: as text, or as
|
|
4
|
+
* pictures of the pages.
|
|
5
|
+
*
|
|
6
|
+
* import { isPdfFile, readPdf } from "@bitbaum/chatkit/pdf";
|
|
7
|
+
*
|
|
8
|
+
* No model on a free chain reads a PDF file, and most paid ones bill for the
|
|
9
|
+
* privilege. So the PDF never leaves the device as a PDF:
|
|
10
|
+
*
|
|
11
|
+
* - A PDF with a text layer (exported from Word, Docs, a typed report) comes
|
|
12
|
+
* back as `{ kind: "text" }` with page markers kept, so the model can say
|
|
13
|
+
* "on page 4". Attach it like a .txt file.
|
|
14
|
+
* - A PDF without one (a deck exported as slide images, a scan) comes back as
|
|
15
|
+
* `{ kind: "pages" }`: the pages rendered and packed four to a sheet.
|
|
16
|
+
* Attach the sheets like photos, to a model that can see. Four to a sheet
|
|
17
|
+
* keeps each page ~780px wide — slide text stays legible — and turns a
|
|
18
|
+
* 20-page deck into 5 pictures instead of 20.
|
|
19
|
+
*
|
|
20
|
+
* Found 2026-10-27 in OrangeCat: an investor deck (17 slide images, 13.7 MB,
|
|
21
|
+
* no text layer) could not be shared with the Cat at all.
|
|
22
|
+
*
|
|
23
|
+
* Its own entry point, so a chat that never sees a PDF never loads pdf.js;
|
|
24
|
+
* `readPdf` imports it on the first PDF. The pure parts are exported for
|
|
25
|
+
* tests and for apps that word their own notices.
|
|
26
|
+
*/
|
|
27
|
+
/** Pages per sheet, as a grid. */
|
|
28
|
+
export const PDF_SHEET_COLUMNS = 2;
|
|
29
|
+
export const PDF_SHEET_ROWS = 2;
|
|
30
|
+
export const PDF_PAGES_PER_SHEET = PDF_SHEET_COLUMNS * PDF_SHEET_ROWS;
|
|
31
|
+
/** Width of one page on a sheet; a sheet is two of these side by side. */
|
|
32
|
+
export const PDF_PAGE_WIDTH_PX = 784;
|
|
33
|
+
/** A PDF this large is refused before pdf.js opens it. */
|
|
34
|
+
export const PDF_MAX_BYTES = 40 * 1024 * 1024;
|
|
35
|
+
/**
|
|
36
|
+
* Below this many non-space characters per page, on average, the text layer
|
|
37
|
+
* is treated as absent (a slide deck's page numbers, a scanner's stamp) and
|
|
38
|
+
* the pages are looked at instead.
|
|
39
|
+
*/
|
|
40
|
+
export const PDF_MIN_TEXT_CHARS_PER_PAGE = 60;
|
|
41
|
+
const SHEET_JPEG_QUALITY = 0.82;
|
|
42
|
+
export function isPdfFile(file) {
|
|
43
|
+
return file.type === "application/pdf" || file.name.toLowerCase().endsWith(".pdf");
|
|
44
|
+
}
|
|
45
|
+
/** True when the extracted text is the document, not a few stray labels. */
|
|
46
|
+
export function isTextRich(pageTexts) {
|
|
47
|
+
if (pageTexts.length === 0) {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
const chars = pageTexts.reduce((n, t) => n + t.replace(/\s+/g, "").length, 0);
|
|
51
|
+
return chars / pageTexts.length >= PDF_MIN_TEXT_CHARS_PER_PAGE;
|
|
52
|
+
}
|
|
53
|
+
/** The pages as one text, each under a marker the Cat can cite. */
|
|
54
|
+
export function joinPageTexts(pageTexts) {
|
|
55
|
+
return pageTexts
|
|
56
|
+
.map((t, i) => `--- page ${i + 1} ---\n${t.trim()}`)
|
|
57
|
+
.join("\n\n")
|
|
58
|
+
.trim();
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Which pages go on which sheet, for at most `maxSheets` sheets. Pages past
|
|
62
|
+
* what fits are left out, and the caller says so.
|
|
63
|
+
*/
|
|
64
|
+
export function planSheets(pageCount, maxSheets) {
|
|
65
|
+
const shown = Math.min(pageCount, Math.max(0, maxSheets) * PDF_PAGES_PER_SHEET);
|
|
66
|
+
const sheets = [];
|
|
67
|
+
for (let first = 1; first <= shown; first += PDF_PAGES_PER_SHEET) {
|
|
68
|
+
const pages = [];
|
|
69
|
+
for (let p = first; p < first + PDF_PAGES_PER_SHEET && p <= shown; p++) {
|
|
70
|
+
pages.push(p);
|
|
71
|
+
}
|
|
72
|
+
sheets.push(pages);
|
|
73
|
+
}
|
|
74
|
+
return sheets;
|
|
75
|
+
}
|
|
76
|
+
/** "deck.pdf · pages 5–8", or "· page 9" for a sheet of one. */
|
|
77
|
+
export function sheetName(fileName, firstPage, lastPage) {
|
|
78
|
+
return firstPage === lastPage
|
|
79
|
+
? `${fileName} · page ${firstPage}`
|
|
80
|
+
: `${fileName} · pages ${firstPage}–${lastPage}`;
|
|
81
|
+
}
|
|
82
|
+
let pdfjsPromise = null;
|
|
83
|
+
/**
|
|
84
|
+
* Where pdf.js's own runtime files live — the worker, the image decoders
|
|
85
|
+
* (JPEG 2000, JBIG2), CMaps, standard fonts, ICC profiles. They are fetched by
|
|
86
|
+
* URL at runtime, which a bundler cannot follow, so an app copies them into its
|
|
87
|
+
* static folder with `chatkit-copy-pdf-assets public/pdfjs` and passes
|
|
88
|
+
* `assetsUrl: "/pdfjs/"`. Served from the app's own origin, they also pass a
|
|
89
|
+
* strict Content-Security-Policy. Missing decoders are why a page whose
|
|
90
|
+
* pictures are JPEG 2000 — macOS's "reduce file size" export, the deck this
|
|
91
|
+
* was written for — draws blank.
|
|
92
|
+
*/
|
|
93
|
+
export const PDF_ASSET_DIRS = ["wasm", "cmaps", "standard_fonts", "iccs"];
|
|
94
|
+
export const PDF_WORKER_FILE = "pdf.worker.min.mjs";
|
|
95
|
+
function withSlash(url) {
|
|
96
|
+
return url.endsWith("/") ? url : `${url}/`;
|
|
97
|
+
}
|
|
98
|
+
/** pdf.js, loaded on the first PDF — never part of the chat's first paint. */
|
|
99
|
+
function loadPdfJs(assetsUrl) {
|
|
100
|
+
if (!pdfjsPromise) {
|
|
101
|
+
pdfjsPromise = import("pdfjs-dist/legacy/build/pdf.mjs").then((pdfjs) => {
|
|
102
|
+
pdfjs.GlobalWorkerOptions.workerSrc = `${withSlash(assetsUrl)}${PDF_WORKER_FILE}`;
|
|
103
|
+
return pdfjs;
|
|
104
|
+
});
|
|
105
|
+
// A failed load (offline, blocked chunk) must not poison the next try.
|
|
106
|
+
pdfjsPromise.catch(() => {
|
|
107
|
+
pdfjsPromise = null;
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
return pdfjsPromise;
|
|
111
|
+
}
|
|
112
|
+
async function pageTextsOf(doc) {
|
|
113
|
+
const texts = [];
|
|
114
|
+
for (let n = 1; n <= doc.numPages; n++) {
|
|
115
|
+
const page = await doc.getPage(n);
|
|
116
|
+
const content = await page.getTextContent();
|
|
117
|
+
let text = "";
|
|
118
|
+
for (const item of content.items) {
|
|
119
|
+
if ("str" in item) {
|
|
120
|
+
text += item.str + (item.hasEOL ? "\n" : " ");
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
texts.push(text.replace(/[ \t]+\n/g, "\n").replace(/[ \t]{2,}/g, " "));
|
|
124
|
+
page.cleanup();
|
|
125
|
+
}
|
|
126
|
+
return texts;
|
|
127
|
+
}
|
|
128
|
+
async function renderSheet(doc, pages) {
|
|
129
|
+
// Rendering needs a canvas; outside a browser the pages are simply not drawn.
|
|
130
|
+
if (typeof document === "undefined")
|
|
131
|
+
return null;
|
|
132
|
+
const rendered = [];
|
|
133
|
+
for (const n of pages) {
|
|
134
|
+
const page = await doc.getPage(n);
|
|
135
|
+
const base = page.getViewport({ scale: 1 });
|
|
136
|
+
const viewport = page.getViewport({ scale: PDF_PAGE_WIDTH_PX / base.width });
|
|
137
|
+
const canvas = document.createElement("canvas");
|
|
138
|
+
canvas.width = Math.ceil(viewport.width);
|
|
139
|
+
canvas.height = Math.ceil(viewport.height);
|
|
140
|
+
const ctx = canvas.getContext("2d");
|
|
141
|
+
if (!ctx) {
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
144
|
+
// White, not transparent: a JPEG turns transparency black.
|
|
145
|
+
ctx.fillStyle = "#fff";
|
|
146
|
+
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
147
|
+
await page.render({ canvas, canvasContext: ctx, viewport }).promise;
|
|
148
|
+
rendered.push(canvas);
|
|
149
|
+
page.cleanup();
|
|
150
|
+
}
|
|
151
|
+
if (rendered.length === 0) {
|
|
152
|
+
return null;
|
|
153
|
+
}
|
|
154
|
+
const cols = Math.min(PDF_SHEET_COLUMNS, rendered.length);
|
|
155
|
+
const rows = Math.ceil(rendered.length / PDF_SHEET_COLUMNS);
|
|
156
|
+
const cellW = PDF_PAGE_WIDTH_PX;
|
|
157
|
+
const cellH = Math.max(...rendered.map((c) => c.height));
|
|
158
|
+
const gap = 8;
|
|
159
|
+
const sheet = document.createElement("canvas");
|
|
160
|
+
sheet.width = cols * cellW + (cols - 1) * gap;
|
|
161
|
+
sheet.height = rows * cellH + (rows - 1) * gap;
|
|
162
|
+
const ctx = sheet.getContext("2d");
|
|
163
|
+
if (!ctx) {
|
|
164
|
+
return null;
|
|
165
|
+
}
|
|
166
|
+
// The gaps read as page edges; grey so a white page still has one.
|
|
167
|
+
ctx.fillStyle = "#9ca3af";
|
|
168
|
+
ctx.fillRect(0, 0, sheet.width, sheet.height);
|
|
169
|
+
rendered.forEach((c, i) => {
|
|
170
|
+
const x = (i % PDF_SHEET_COLUMNS) * (cellW + gap);
|
|
171
|
+
const y = Math.floor(i / PDF_SHEET_COLUMNS) * (cellH + gap);
|
|
172
|
+
ctx.drawImage(c, x, y);
|
|
173
|
+
});
|
|
174
|
+
return sheet.toDataURL("image/jpeg", SHEET_JPEG_QUALITY);
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Read a picked PDF. Throws on a file pdf.js cannot open (encrypted, corrupt)
|
|
178
|
+
* — the app words that for the person. `PDF_MAX_BYTES` is checked by the app
|
|
179
|
+
* before calling, so it can say so in its own words.
|
|
180
|
+
*/
|
|
181
|
+
export async function readPdf(file, opts) {
|
|
182
|
+
const pdfjs = await loadPdfJs(opts.assetsUrl);
|
|
183
|
+
const data = file instanceof Uint8Array ? file : new Uint8Array(await file.arrayBuffer());
|
|
184
|
+
const base = withSlash(opts.assetsUrl);
|
|
185
|
+
const task = pdfjs.getDocument({
|
|
186
|
+
data,
|
|
187
|
+
wasmUrl: `${base}wasm/`,
|
|
188
|
+
cMapUrl: `${base}cmaps/`,
|
|
189
|
+
standardFontDataUrl: `${base}standard_fonts/`,
|
|
190
|
+
iccUrl: `${base}iccs/`,
|
|
191
|
+
});
|
|
192
|
+
const doc = await task.promise;
|
|
193
|
+
try {
|
|
194
|
+
const pageTexts = await pageTextsOf(doc);
|
|
195
|
+
if (isTextRich(pageTexts)) {
|
|
196
|
+
return { kind: "text", text: joinPageTexts(pageTexts), pageCount: doc.numPages };
|
|
197
|
+
}
|
|
198
|
+
const sheets = [];
|
|
199
|
+
for (const pages of planSheets(doc.numPages, opts.maxSheets)) {
|
|
200
|
+
const dataUrl = await renderSheet(doc, pages);
|
|
201
|
+
if (dataUrl) {
|
|
202
|
+
sheets.push({ firstPage: pages[0], lastPage: pages[pages.length - 1], dataUrl });
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
const pagesShown = sheets.length > 0 ? sheets[sheets.length - 1].lastPage : 0;
|
|
206
|
+
return { kind: "pages", sheets, pageCount: doc.numPages, pagesShown };
|
|
207
|
+
}
|
|
208
|
+
finally {
|
|
209
|
+
void task.destroy();
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
//# sourceMappingURL=pdf.js.map
|
package/dist/pdf.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pdf.js","sourceRoot":"","sources":["../src/pdf.ts"],"names":[],"mappings":"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"}
|
package/dist/react/attach.d.ts
CHANGED
|
@@ -12,6 +12,6 @@
|
|
|
12
12
|
*/
|
|
13
13
|
export { AttachMenu, DEFAULT_ATTACH_MENU_LABELS } from "./AttachMenu.js";
|
|
14
14
|
export type { AttachExtra, AttachMenuLabels } from "./AttachMenu.js";
|
|
15
|
-
export { ATTACH_SOURCE_INPUT, acceptFamilies, isImageMime, isTextFile, type AcceptFamily, type AttachSource, } from "../attachments.js";
|
|
15
|
+
export { ATTACH_SOURCE_INPUT, FILES_ACCEPT, acceptFamilies, isImageMime, isTextFile, type AcceptFamily, type AttachSource, } from "../attachments.js";
|
|
16
16
|
export { shrinkImage } from "../image.js";
|
|
17
17
|
//# sourceMappingURL=attach.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"attach.d.ts","sourceRoot":"","sources":["../../src/react/attach.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EAAE,UAAU,EAAE,0BAA0B,EAAE,MAAM,iBAAiB,CAAC;AACzE,YAAY,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACrE,OAAO,EACL,mBAAmB,EACnB,cAAc,EACd,WAAW,EACX,UAAU,EACV,KAAK,YAAY,EACjB,KAAK,YAAY,GAClB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC"}
|
|
1
|
+
{"version":3,"file":"attach.d.ts","sourceRoot":"","sources":["../../src/react/attach.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EAAE,UAAU,EAAE,0BAA0B,EAAE,MAAM,iBAAiB,CAAC;AACzE,YAAY,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACrE,OAAO,EACL,mBAAmB,EACnB,YAAY,EACZ,cAAc,EACd,WAAW,EACX,UAAU,EACV,KAAK,YAAY,EACjB,KAAK,YAAY,GAClB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC"}
|
package/dist/react/attach.js
CHANGED
|
@@ -11,6 +11,6 @@
|
|
|
11
11
|
* `chatkit-check-file-inputs`.
|
|
12
12
|
*/
|
|
13
13
|
export { AttachMenu, DEFAULT_ATTACH_MENU_LABELS } from "./AttachMenu.js";
|
|
14
|
-
export { ATTACH_SOURCE_INPUT, acceptFamilies, isImageMime, isTextFile, } from "../attachments.js";
|
|
14
|
+
export { ATTACH_SOURCE_INPUT, FILES_ACCEPT, acceptFamilies, isImageMime, isTextFile, } from "../attachments.js";
|
|
15
15
|
export { shrinkImage } from "../image.js";
|
|
16
16
|
//# sourceMappingURL=attach.js.map
|
package/dist/react/attach.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"attach.js","sourceRoot":"","sources":["../../src/react/attach.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EAAE,UAAU,EAAE,0BAA0B,EAAE,MAAM,iBAAiB,CAAC;AAEzE,OAAO,EACL,mBAAmB,EACnB,cAAc,EACd,WAAW,EACX,UAAU,GAGX,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC"}
|
|
1
|
+
{"version":3,"file":"attach.js","sourceRoot":"","sources":["../../src/react/attach.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EAAE,UAAU,EAAE,0BAA0B,EAAE,MAAM,iBAAiB,CAAC;AAEzE,OAAO,EACL,mBAAmB,EACnB,YAAY,EACZ,cAAc,EACd,WAAW,EACX,UAAU,GAGX,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bitbaum/chatkit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.20.1",
|
|
4
4
|
"description": "One chat for every product: a composer with a microphone that always works, a thread that does not fight the reader, messages with copy and retry, and LLM markdown — the fleet's chat standard as a package.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Cato",
|
|
@@ -20,7 +20,8 @@
|
|
|
20
20
|
"speech-to-text",
|
|
21
21
|
"markdown",
|
|
22
22
|
"react",
|
|
23
|
-
"ai"
|
|
23
|
+
"ai",
|
|
24
|
+
"pdf"
|
|
24
25
|
],
|
|
25
26
|
"type": "module",
|
|
26
27
|
"sideEffects": [
|
|
@@ -40,7 +41,8 @@
|
|
|
40
41
|
"styles.css"
|
|
41
42
|
],
|
|
42
43
|
"bin": {
|
|
43
|
-
"chatkit-check-file-inputs": "./bin/chatkit-check-file-inputs.mjs"
|
|
44
|
+
"chatkit-check-file-inputs": "./bin/chatkit-check-file-inputs.mjs",
|
|
45
|
+
"chatkit-copy-pdf-assets": "./bin/chatkit-copy-pdf-assets.mjs"
|
|
44
46
|
},
|
|
45
47
|
"exports": {
|
|
46
48
|
".": {
|
|
@@ -59,6 +61,10 @@
|
|
|
59
61
|
"types": "./dist/react/attach.d.ts",
|
|
60
62
|
"default": "./dist/react/attach.js"
|
|
61
63
|
},
|
|
64
|
+
"./pdf": {
|
|
65
|
+
"types": "./dist/pdf.d.ts",
|
|
66
|
+
"default": "./dist/pdf.js"
|
|
67
|
+
},
|
|
62
68
|
"./styles.css": "./styles.css"
|
|
63
69
|
},
|
|
64
70
|
"scripts": {
|
|
@@ -71,7 +77,7 @@
|
|
|
71
77
|
"format": "prettier --write .",
|
|
72
78
|
"format:check": "prettier --check .",
|
|
73
79
|
"demo": "esbuild demo/demo.tsx --bundle --jsx=automatic --format=iife --define:process.env.NODE_ENV='\"production\"' --outfile=demo/dist/demo.js --log-level=warning",
|
|
74
|
-
"test:browser": "pnpm run demo && node test/browser.mjs && node test/browser-scroll.mjs && node test/browser-app.mjs"
|
|
80
|
+
"test:browser": "pnpm run demo && node test/browser.mjs && node test/browser-scroll.mjs && node test/browser-app.mjs && node test/browser-pdf.mjs"
|
|
75
81
|
},
|
|
76
82
|
"devDependencies": {
|
|
77
83
|
"@bitbaum/design-tokens": "^1.2.0",
|
|
@@ -91,5 +97,8 @@
|
|
|
91
97
|
},
|
|
92
98
|
"peerDependencies": {
|
|
93
99
|
"react": ">=18"
|
|
100
|
+
},
|
|
101
|
+
"dependencies": {
|
|
102
|
+
"pdfjs-dist": "^6.4.299"
|
|
94
103
|
}
|
|
95
104
|
}
|