@hraness/sponge 0.5.1 → 0.5.2

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.
@@ -6,6 +6,7 @@ import { sha256 } from '../util/hash.js';
6
6
  import { bundleBytes, bundleText } from '../app/bundle-files.js';
7
7
  import { selectedMetadata } from '../metadata/article.js';
8
8
  import { localizeMarkdown } from '../app/markdown.js';
9
+ import { pdfHeadingTitle } from './title.js';
9
10
  import { SpongeError } from '../errors.js';
10
11
  const classify = classifyErrors([
11
12
  'integrity',
@@ -61,7 +62,7 @@ export function ingestPdf(store, token, options) {
61
62
  throw new SpongeError('cancelled');
62
63
  }
63
64
  const selectedTitle = selectedMetadata(capture.metadata, 'title')?.value;
64
- const title = typeof selectedTitle === 'string' ? selectedTitle : undefined;
65
+ const title = typeof selectedTitle === 'string' ? selectedTitle : pdfHeadingTitle(original);
65
66
  const status = localized.missingImages ? 'partial' : capture.status;
66
67
  // Representation algorithm participates in identity: later extraction changes cannot reuse an old payload.
67
68
  const bundleHash = sha256(JSON.stringify(['pdf-local-markdown-v1', capture.manifestSha256]));
@@ -0,0 +1,5 @@
1
+ /**
2
+ * The first heading Wordcell detected on page 1, for a PDF whose metadata has no title.
3
+ * Wordcell writes body headings at level 2 or deeper and escapes Markdown punctuation in them.
4
+ */
5
+ export declare function pdfHeadingTitle(markdown: string): string | undefined;
@@ -0,0 +1,28 @@
1
+ // Section names that open many papers but never name one.
2
+ const generic = /^(?:abstract|contents|table of contents|introduction|preface|foreword|acknowledge?ments?|summary|executive summary|references)$/i;
3
+ /**
4
+ * The first heading Wordcell detected on page 1, for a PDF whose metadata has no title.
5
+ * Wordcell writes body headings at level 2 or deeper and escapes Markdown punctuation in them.
6
+ */
7
+ export function pdfHeadingTitle(markdown) {
8
+ const start = markdown.search(/^<!-- pdf-page: 1 -->$/m);
9
+ if (start < 0)
10
+ return undefined;
11
+ const page = markdown.slice(start).split(/^<!-- pdf-page: [1-9][0-9]* -->$/m)[1] ?? '';
12
+ const headings = [...page.matchAll(/^#{2,6} (.+)$/gm)].map(([, heading]) => heading
13
+ .replace(/\\([\\`*_[\]{}<>#|])/g, '$1')
14
+ .replace(/\s+/g, ' ')
15
+ .trim());
16
+ const lowercase = (text = '') => /^\p{Ll}/u.test(text);
17
+ for (const [index, title] of headings.entries()) {
18
+ // Text Wordcell read from an image, and prose wrapped across headings such as a copyright notice.
19
+ if (/^Text visible in (?:.+ )?image$/.test(title) || lowercase(title) || lowercase(headings[index + 1]))
20
+ continue;
21
+ if (title.length < 3 || title.length > 300)
22
+ continue;
23
+ // Body sections have begun, so the title was never detected as a heading.
24
+ if (generic.test(title) || /^(?:[0-9]+(?:\.[0-9]+)*\.?|[IVX]+\.)\s/.test(title))
25
+ return undefined;
26
+ return title;
27
+ }
28
+ }
@@ -15,7 +15,7 @@ const patterns = {
15
15
  model: /^[a-f0-9]{64}$/,
16
16
  worker: uuid,
17
17
  };
18
- const helperNames = ['sponge-speech', 'sponge-embedding'];
18
+ const helperNames = ['sponge-speech', 'sponge-embedding', 'sponge-media'];
19
19
  const trash = /^\.gc-[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
20
20
  // A worker writes owner.json just after creating its directory; a young unreadable directory may be mid-acquisition.
21
21
  const youngMs = 10 * 60 * 1000;
@@ -33,16 +33,16 @@
33
33
  }
34
34
  ],
35
35
  "build": {
36
- "asrSourceSha256": "6d060d06c7efdc2a9a5537b4d2e108673afc0db596367e9ff876af44bc60d942",
36
+ "asrSourceSha256": "c76a1f77f5daf67188383617d9f790c80571b5021bd529a8238447b1bde1d078",
37
37
  "clangVersion": "Apple clang version 21.0.0 (clang-2100.1.1.101)",
38
- "embeddingSourceSha256": "04e07c5f696d5d1d639b004c200da0d0044e473a8b6fdc6ef2be696a97fdf552",
39
- "mediaSourceSha256": "b35bba5fb8d9f88d67a0aa6fc3e282bd9dbed9077d994fc4de4428eb3df6381e",
38
+ "embeddingSourceSha256": "51ea5f439a1d50b32a5e5c0e93b515d0b5cff996309687df12de911e19c99042",
39
+ "mediaSourceSha256": "b93b70ac8de1df0954d92512e93943061f6612c063102504d335036aad362935",
40
40
  "rustcVersion": "rustc 1.97.1 (8bab26f4f 2026-07-14)",
41
41
  "swiftVersion": "Apple Swift version 6.3.3 (swiftlang-6.3.3.1.3 clang-2100.1.1.101)"
42
42
  },
43
43
  "minimumMacOS": "26.0",
44
- "packageVersion": "0.5.1",
44
+ "packageVersion": "0.5.2",
45
45
  "platform": "darwin",
46
46
  "schemaVersion": 1,
47
- "sourceCommit": "142391ee43de6dc6dba8ef209c73b177539b4725"
47
+ "sourceCommit": "6364ef0dc20f5cdf4702f17c35e4539395f4b716"
48
48
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hraness/sponge",
3
- "version": "0.5.1",
3
+ "version": "0.5.2",
4
4
  "description": "A local library for captured articles and verified offline assets.",
5
5
  "repository": {
6
6
  "type": "git",