Skip to content
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
20 changes: 20 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,26 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
`core/analyze`, CLI `detectFormat`, `package.json` exports, and a new
`GotalkNow` namespace.
- Conversion utility: `scripts/conversion/convert-gtbz-to-obz.js`.
- **GoTalk NOW `.gotalk-book` share exports.** Real-world books shared from the
app arrive as ZIPs with everything nested under a `<BookName>.gotalk-book/`
folder (often `.zip`-suffixed, sometimes with macOS `__MACOSX/` resource-fork
entries). The processor now locates the plists in either layout and
`processTexts` round-trips the nested structure in place.
- **GoTalk NOW format routing.** `MyBook.gotalk-book.zip` / `.gotalk-book`
names route to the GoTalk processor in `getProcessor` (Node + browser;
`.gotalk-book` added to supported extensions), and the CLI sniffs bare
`.zip` archives for GoTalk plists.
- **GoTalk NOW image resolution.** Bundled button images are matched by
basename (case-insensitive, with extension guessing) against the archive —
`Location`/`QuickRecoveryPath` are iOS container paths that never match entry
names. Entries sourced from the app's "GoTalk Image Library" clip-art are
skipped in favour of the user's own image, and a pre-rendered
`<page>-<button>-<buttonCount>.png` snapshot is the final fallback. Symbol
libraries are detected generically (`metacom`/`pcs`/`symbolstix`/`widgit`).
- **GoTalk NOW semantics.** `JumpTo` sentinels (`0`, `-1`, empty) no longer
produce phantom navigation targets; sparse pages without `ButtonCount` are
sized by highest button index + 1; non-square counts derive a near-square
rectangle (cols = ⌈√n⌉, rows = ⌈n/cols⌉) instead of a forced square.

### Fixed

Expand Down
23 changes: 23 additions & 0 deletions src/cli/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
} from '../utilities/analytics/history';
import { ComparisonAnalyzer, MetricsCalculator, dumpVocabulary } from '../utilities/analytics';
import type { VocabularyDump } from '../utilities/analytics';
import { getZipAdapter } from '../utils/zip';
import { CellScanningOrder, ScanningSelectionMethod } from '../types/aac';
import { defaultFileAdapter, extname } from '../utils/io';
import { readFileSync } from 'node:fs';
Expand Down Expand Up @@ -37,6 +38,28 @@ async function detectFormat(filePath: string): Promise<string> {
if (filePath.endsWith('.gtbz')) {
return 'gotalknow';
}
// GoTalk NOW share exports keep their book suffix before the archive
// extension ("MyBook.gotalk-book.zip") — route on the book suffix.
if (/\.gotalk-book(\.|$)/i.test(filePath)) {
return 'gotalknow';
}

// Bare .zip: sniff the archive for GoTalk NOW plists (root or nested
// <BookName>.gotalk-book/ folder). Other zip-based formats (.obz, .spb,
// .gridset) always arrive with their own extension.
if (filePath.toLowerCase().endsWith('.zip')) {
try {
const zip = await getZipAdapter(filePath, defaultFileAdapter);
const files = zip.listFiles();
const hasGoTalkPlists = files.some(
(f: string) =>
!f.startsWith('__MACOSX/') && /(^|\/)PageData\.plist$/.test(f.replace(/\\/g, '/'))
);
if (hasGoTalkPlists) return 'gotalknow';
} catch {
/* not a readable zip — fall through to extension-based detection */
}
}

// Otherwise use file extension
return extname(filePath).slice(1);
Expand Down
1 change: 1 addition & 0 deletions src/core/analyze.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ export function getProcessor(format: string, options?: ProcessorOptions): BasePr
case 'gotalknow':
case 'gotalk':
case 'gtbz': // GoTalk NOW file extension
case 'gotalk-book': // GoTalk NOW share export (e.g. "MyBook.gotalk-book.zip")
return new GotalkNowProcessor(options);
default:
throw new Error('Unknown format: ' + format);
Expand Down
8 changes: 8 additions & 0 deletions src/index.browser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,13 @@ export function getProcessor(
? filePathOrExtension.substring(filePathOrExtension.lastIndexOf('.'))
: filePathOrExtension;

// GoTalk NOW share exports keep their book suffix before the archive
// extension (e.g. "MyBook.gotalk-book.zip"), so match on the name, not the
// final extension.
if (/\.gotalk-book(\.|$)/i.test(filePathOrExtension)) {
return new GotalkNowProcessor(options);
}

switch (extension.toLowerCase()) {
case '.dot':
return new DotProcessor(options);
Expand Down Expand Up @@ -108,6 +115,7 @@ export function getSupportedExtensions(): string[] {
'.plist',
'.grd',
'.gtbz',
'.gotalk-book',
];
}

Expand Down
8 changes: 8 additions & 0 deletions src/index.node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,13 @@ export function getProcessor(
? filePathOrExtension.substring(filePathOrExtension.lastIndexOf('.'))
: filePathOrExtension;

// GoTalk NOW share exports keep their book suffix before the archive
// extension (e.g. "MyBook.gotalk-book.zip"), so match on the name, not the
// final extension.
if (/\.gotalk-book(\.|$)/i.test(filePathOrExtension)) {
return new GotalkNowProcessor(options);
}

switch (extension.toLowerCase()) {
case '.dot':
return new DotProcessor(options);
Expand Down Expand Up @@ -137,6 +144,7 @@ export function getSupportedExtensions(): string[] {
'.plist',
'.grd',
'.gtbz',
'.gotalk-book',
];
}

Expand Down
Loading
Loading