@cyber-dash-tech/revela 0.17.21 → 0.17.23

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.
@@ -1,11 +1,9 @@
1
1
  /**
2
2
  * DesignManager — manage revela visual design templates.
3
3
  *
4
- * Designs are stored in ~/.config/revela/designs/<name>/.
4
+ * User designs are stored in ~/.config/revela/designs/<name>/.
5
+ * Built-in designs are shipped read-only with this package under designs/<name>/.
5
6
  * Each design directory contains DESIGN.md (required) and optionally preview.html.
6
- *
7
- * Built-in designs are shipped with the npm package under designs/ and seeded
8
- * to the config directory on first run.
9
7
  */
10
8
 
11
9
  import {
@@ -152,21 +150,46 @@ export function parseDesignFile(filePath: string): DesignInfo | null {
152
150
  // Public API
153
151
  // ---------------------------------------------------------------------------
154
152
 
155
- /** List installed designs, sorted by name. Internal designs are hidden by default. */
153
+ function designDirHasPackage(dir: string): boolean {
154
+ return existsSync(dir) && statSync(dir).isDirectory() && existsSync(join(dir, "DESIGN.md"))
155
+ }
156
+
157
+ function resolveDesignDir(nameInput?: string): string | null {
158
+ const name = normalizeDesignName(nameInput || activeDesign())
159
+ const userDir = join(DESIGNS_DIR, name)
160
+ if (designDirHasPackage(userDir)) return userDir
161
+
162
+ const bundledDir = join(SEED_DIR, name)
163
+ if (designDirHasPackage(bundledDir)) return bundledDir
164
+
165
+ return null
166
+ }
167
+
168
+ function readDesignsFromDir(root: string): Map<string, DesignInfo> {
169
+ const designs = new Map<string, DesignInfo>()
170
+ if (!existsSync(root)) return designs
171
+
172
+ for (const entry of readdirSync(root).sort()) {
173
+ const dir = join(root, entry)
174
+ if (!designDirHasPackage(dir)) continue
175
+ const info = parseDesignFile(join(dir, "DESIGN.md"))
176
+ if (info) designs.set(entry, info)
177
+ }
178
+ return designs
179
+ }
180
+
181
+ /** List available designs, sorted by name. User designs override bundled designs with the same name. Internal designs are hidden by default. */
156
182
  export function listDesigns(options: ListDesignsOptions = {}): DesignInfo[] {
157
- if (!existsSync(DESIGNS_DIR)) return []
158
- const results: DesignInfo[] = []
159
183
  const includeInternal = options.includeInternal ?? false
184
+ const available = readDesignsFromDir(SEED_DIR)
160
185
 
161
- for (const entry of readdirSync(DESIGNS_DIR).sort()) {
162
- const dir = join(DESIGNS_DIR, entry)
163
- if (!statSync(dir).isDirectory()) continue
164
- const mdPath = join(dir, "DESIGN.md")
165
- if (!existsSync(mdPath)) continue
166
- const info = parseDesignFile(mdPath)
167
- if (info && (includeInternal || !info.internal)) results.push(info)
186
+ for (const [entry, info] of readDesignsFromDir(DESIGNS_DIR)) {
187
+ available.set(entry, info)
168
188
  }
169
- return results
189
+
190
+ return [...available.values()]
191
+ .filter((info) => includeInternal || !info.internal)
192
+ .sort((a, b) => a.name.localeCompare(b.name))
170
193
  }
171
194
 
172
195
  /** Get the name of the currently active design. */
@@ -187,11 +210,12 @@ export function activateDesign(name: string): void {
187
210
 
188
211
  /** Get the skill text body from a design's DESIGN.md. */
189
212
  export function getDesignSkillMd(name?: string): string {
190
- const designName = name || activeDesign()
191
- const mdPath = join(DESIGNS_DIR, designName, "DESIGN.md")
192
- if (!existsSync(mdPath)) {
213
+ const designName = normalizeDesignName(name || activeDesign())
214
+ const designDir = resolveDesignDir(designName)
215
+ if (!designDir) {
193
216
  throw new Error(`Design '${designName}' is not installed`)
194
217
  }
218
+ const mdPath = join(designDir, "DESIGN.md")
195
219
  const info = parseDesignFile(mdPath)
196
220
  if (!info) {
197
221
  throw new Error(`Failed to parse DESIGN.md for '${designName}'`)
@@ -202,9 +226,8 @@ export function getDesignSkillMd(name?: string): string {
202
226
  /** Resolve a design's preview.html path. Throws if the design is not installed. */
203
227
  export function resolveDesignPreview(name?: string): DesignPreviewInfo {
204
228
  const designName = normalizeDesignName(name || activeDesign())
205
- const designDir = join(DESIGNS_DIR, designName)
206
- const mdPath = join(designDir, "DESIGN.md")
207
- if (!existsSync(designDir) || !existsSync(mdPath)) {
229
+ const designDir = resolveDesignDir(designName)
230
+ if (!designDir) {
208
231
  throw new Error(`Design '${designName}' is not installed`)
209
232
  }
210
233
 
@@ -388,12 +411,15 @@ function hasFixedSizeCssRule(html: string, className: "slide-canvas"): boolean {
388
411
  /** Validate a local design package for the minimum Revela design contract. */
389
412
  export function validateDesignPackage(nameInput: string): ValidateDesignPackageResult {
390
413
  let name = nameInput
414
+ let hasValidName = true
391
415
  try {
392
416
  name = normalizeDesignName(nameInput)
393
417
  } catch {
418
+ hasValidName = false
394
419
  // validateDesignPackageAt records the invalid-name error.
395
420
  }
396
- return validateDesignPackageAt(nameInput, join(DESIGNS_DIR, name))
421
+ const dir = hasValidName ? resolveDesignDir(name) || join(DESIGNS_DIR, name) : join(DESIGNS_DIR, name)
422
+ return validateDesignPackageAt(nameInput, dir)
397
423
  }
398
424
 
399
425
  function validateDesignPackageAt(nameInput: string, dir: string): ValidateDesignPackageResult {
@@ -493,6 +519,25 @@ export interface DesignSections {
493
519
  hasMarkers: boolean
494
520
  }
495
521
 
522
+ export interface DesignInventoryLayout {
523
+ name: string
524
+ qa: boolean
525
+ description: string
526
+ }
527
+
528
+ export interface DesignInventoryComponent {
529
+ name: string
530
+ description: string
531
+ }
532
+
533
+ export interface DesignInventory {
534
+ name: string
535
+ sections: string[]
536
+ layouts: DesignInventoryLayout[]
537
+ components: DesignInventoryComponent[]
538
+ hasMarkers: boolean
539
+ }
540
+
496
541
  /**
497
542
  * Parse a DESIGN.md body (no frontmatter) into sections, layouts, and components
498
543
  * using the three-layer HTML comment marker convention:
@@ -566,7 +611,7 @@ export function generateComponentIndex(components: Record<string, string>): stri
566
611
  "|---|---|",
567
612
  ...rows,
568
613
  "",
569
- "_Use `revela-designs` tool with `action: \"read\"` and `component: \"<name>\"` to get full CSS/HTML for any component._",
614
+ "_Use `revela_design_read_component` with `component: \"<name>\"` to get full CSS/HTML for any component._",
570
615
  ].join("\n")
571
616
  }
572
617
 
@@ -598,10 +643,47 @@ export function generateLayoutIndex(layouts: Record<string, LayoutInfo>): string
598
643
  "|---|---|---|",
599
644
  ...rows,
600
645
  "",
601
- "_Use `revela-designs` tool with `action: \"read\"` and `layout: \"<name>\"` to get full HTML/CSS for any layout._",
646
+ "_Use `revela_design_read_layout` with `layout: \"<name>\"` to get full HTML/CSS for any layout._",
602
647
  ].join("\n")
603
648
  }
604
649
 
650
+ export function getDesignInventory(designName?: string): DesignInventory {
651
+ const name = normalizeDesignName(designName || activeDesign())
652
+ const designDir = resolveDesignDir(name)
653
+ if (!designDir) {
654
+ throw new Error(`Design '${name}' is not installed`)
655
+ }
656
+ const mdPath = join(designDir, "DESIGN.md")
657
+ const text = readFileSync(mdPath, "utf-8")
658
+ const { body } = parseFrontmatter(text)
659
+ const { sections, layouts, components, hasMarkers } = parseDesignSections(body)
660
+
661
+ return {
662
+ name,
663
+ sections: Object.keys(sections),
664
+ layouts: Object.entries(layouts).map(([layoutName, layout]) => ({
665
+ name: layoutName,
666
+ qa: layout.qa,
667
+ description: designBlockDescription(layout.content),
668
+ })),
669
+ components: Object.entries(components).map(([componentName, content]) => ({
670
+ name: componentName,
671
+ description: designBlockDescription(content),
672
+ })),
673
+ hasMarkers,
674
+ }
675
+ }
676
+
677
+ function designBlockDescription(body: string): string {
678
+ const firstLine = body
679
+ .split("\n")
680
+ .map((line) => line.trim())
681
+ .find((line) => line && !line.startsWith("<!--") && !line.startsWith("```"))
682
+ return firstLine
683
+ ? firstLine.replace(/^#+\s*/, "").replace(/\(.*?\)/, "").trim()
684
+ : ""
685
+ }
686
+
605
687
  /**
606
688
  * Get the raw text of one or more named layouts from a DESIGN.md.
607
689
  * @param layoutNames - Comma-separated layout names or an array.
@@ -611,11 +693,12 @@ export function getDesignLayout(
611
693
  layoutNames: string | string[],
612
694
  designName?: string,
613
695
  ): string {
614
- const name = designName || activeDesign()
615
- const mdPath = join(DESIGNS_DIR, name, "DESIGN.md")
616
- if (!existsSync(mdPath)) {
696
+ const name = normalizeDesignName(designName || activeDesign())
697
+ const designDir = resolveDesignDir(name)
698
+ if (!designDir) {
617
699
  throw new Error(`Design '${name}' is not installed`)
618
700
  }
701
+ const mdPath = join(designDir, "DESIGN.md")
619
702
  const text = readFileSync(mdPath, "utf-8")
620
703
  const { body } = parseFrontmatter(text)
621
704
  const { layouts, hasMarkers } = parseDesignSections(body)
@@ -644,11 +727,12 @@ export function getDesignLayout(
644
727
  * Throws if the design is not installed or the section doesn't exist.
645
728
  */
646
729
  export function getDesignSection(sectionName: string, designName?: string): string {
647
- const name = designName || activeDesign()
648
- const mdPath = join(DESIGNS_DIR, name, "DESIGN.md")
649
- if (!existsSync(mdPath)) {
730
+ const name = normalizeDesignName(designName || activeDesign())
731
+ const designDir = resolveDesignDir(name)
732
+ if (!designDir) {
650
733
  throw new Error(`Design '${name}' is not installed`)
651
734
  }
735
+ const mdPath = join(designDir, "DESIGN.md")
652
736
  const text = readFileSync(mdPath, "utf-8")
653
737
  const { body } = parseFrontmatter(text)
654
738
  const { sections, hasMarkers } = parseDesignSections(body)
@@ -671,11 +755,12 @@ export function getDesignComponent(
671
755
  componentNames: string | string[],
672
756
  designName?: string,
673
757
  ): string {
674
- const name = designName || activeDesign()
675
- const mdPath = join(DESIGNS_DIR, name, "DESIGN.md")
676
- if (!existsSync(mdPath)) {
758
+ const name = normalizeDesignName(designName || activeDesign())
759
+ const designDir = resolveDesignDir(name)
760
+ if (!designDir) {
677
761
  throw new Error(`Design '${name}' is not installed`)
678
762
  }
763
+ const mdPath = join(designDir, "DESIGN.md")
679
764
  const text = readFileSync(mdPath, "utf-8")
680
765
  const { body } = parseFrontmatter(text)
681
766
  const { components, hasMarkers } = parseDesignSections(body)
@@ -742,8 +827,7 @@ export async function installDesign(
742
827
  // ---------------------------------------------------------------------------
743
828
 
744
829
  function designExists(name: string): boolean {
745
- const dir = join(DESIGNS_DIR, name)
746
- return existsSync(dir) && existsSync(join(dir, "DESIGN.md"))
830
+ return resolveDesignDir(name) !== null
747
831
  }
748
832
 
749
833
  function installFromPath(srcPath: string, name?: string): string {
@@ -814,13 +898,13 @@ export interface DesignClassVocabulary {
814
898
  * Falls back to UNIVERSAL_CLASSES-only when the design has no markers.
815
899
  */
816
900
  export function extractDesignClasses(designName?: string): DesignClassVocabulary {
817
- const name = designName || activeDesign()
818
- const mdPath = join(DESIGNS_DIR, name, "DESIGN.md")
819
-
820
- if (!existsSync(mdPath)) {
901
+ const name = normalizeDesignName(designName || activeDesign())
902
+ const designDir = resolveDesignDir(name)
903
+ if (!designDir) {
821
904
  return { classes: new Set(UNIVERSAL_CLASSES), prefixExemptions: DEFAULT_PREFIX_EXEMPTIONS }
822
905
  }
823
906
 
907
+ const mdPath = join(designDir, "DESIGN.md")
824
908
  const raw = readFileSync(mdPath, "utf-8")
825
909
  const { body } = parseFrontmatter(raw)
826
910
  const { sections, layouts, components, hasMarkers } = parseDesignSections(body)
@@ -58,6 +58,7 @@ export type DocumentMaterialsResult = {
58
58
  cache_dir?: string
59
59
  manifest_path?: string
60
60
  text_path?: string
61
+ read_view_path?: string
61
62
  images?: DocumentMaterial[]
62
63
  skipped_assets?: SkippedAsset[]
63
64
  slides?: PptxSlide[]
@@ -74,6 +75,7 @@ type CachedManifest = {
74
75
  cache_dir: string
75
76
  manifest_path: string
76
77
  text_path: string
78
+ read_view_path?: string
77
79
  images: DocumentMaterial[]
78
80
  skipped_assets: SkippedAsset[]
79
81
  slides: PptxSlide[]
@@ -157,6 +159,145 @@ function materialPath(cacheDir: string, workspaceDir: string, ...segments: strin
157
159
  return workspaceRelative(join(cacheDir, ...segments), workspaceDir)
158
160
  }
159
161
 
162
+ function buildReadView(input: {
163
+ source: string
164
+ type: SupportedType
165
+ fingerprint: string
166
+ text: string
167
+ manifestPath: string
168
+ textPath: string
169
+ images: DocumentMaterial[]
170
+ skippedAssets: SkippedAsset[]
171
+ tables: DocumentMaterial[]
172
+ slides: PptxSlide[] | undefined
173
+ }): string {
174
+ const lines = [
175
+ `# Extracted Material: ${basename(input.source)}`,
176
+ "",
177
+ "## Source",
178
+ "",
179
+ `- sourcePath: ${input.source}`,
180
+ `- type: ${input.type}`,
181
+ `- fingerprint: ${input.fingerprint}`,
182
+ `- manifestPath: ${input.manifestPath}`,
183
+ `- textPath: ${input.textPath}`,
184
+ "",
185
+ "## Text",
186
+ "",
187
+ input.text.trim() || "No text extracted.",
188
+ "",
189
+ "## Extracted Images",
190
+ "",
191
+ ]
192
+
193
+ if (input.images.length === 0) lines.push("- None")
194
+ else {
195
+ for (const image of input.images) {
196
+ const parts = [
197
+ image.page_or_slide ? `page_or_slide: ${image.page_or_slide}` : null,
198
+ `source_ref: ${image.source_ref}`,
199
+ image.note ? `note: ${image.note}` : null,
200
+ ].filter(Boolean).join("; ")
201
+ lines.push(`- ${image.path}${parts ? ` (${parts})` : ""}`)
202
+ }
203
+ }
204
+
205
+ if (input.skippedAssets.length > 0) {
206
+ lines.push("", "## Skipped Or Unmapped Assets", "")
207
+ for (const asset of input.skippedAssets) {
208
+ const parts = [
209
+ asset.page_or_slide ? `page_or_slide: ${asset.page_or_slide}` : null,
210
+ `reason: ${asset.reason}`,
211
+ asset.kind ? `kind: ${asset.kind}` : null,
212
+ ].filter(Boolean).join("; ")
213
+ lines.push(`- ${asset.source_ref}${parts ? ` (${parts})` : ""}`)
214
+ }
215
+ }
216
+
217
+ if (input.tables.length > 0) {
218
+ lines.push("", "## Extracted Tables", "")
219
+ for (const table of input.tables) lines.push(`- ${table.path} (${table.note ?? table.source_ref})`)
220
+ }
221
+
222
+ if (input.slides?.length) {
223
+ lines.push("", "## Slide Structure", "")
224
+ for (const slide of input.slides) {
225
+ const textCount = slide.elements.filter((element) => element.kind === "text").length
226
+ const imageCount = slide.elements.filter((element) => element.kind === "image").length
227
+ const shapeCount = slide.elements.filter((element) => element.kind === "shape").length
228
+ lines.push(`- ${slide.slide}: ${textCount} text, ${imageCount} image, ${shapeCount} shape`)
229
+ }
230
+ }
231
+
232
+ lines.push(
233
+ "",
234
+ "## Intake Rules",
235
+ "",
236
+ "- Treat this extracted material as source context until a material review records what was considered.",
237
+ "- Do not treat extracted images as interpreted evidence unless an explicit image review or user-provided meaning exists.",
238
+ "- Canonical evidence still requires source trace, quote/snippet, support scope, unsupported scope, caveat, strength, and relations in `revela-narrative/`.",
239
+ )
240
+
241
+ return lines.join("\n")
242
+ }
243
+
244
+ function writeReadView(input: {
245
+ cacheDir: string
246
+ workspaceDir: string
247
+ source: string
248
+ type: SupportedType
249
+ fingerprint: string
250
+ text: string
251
+ manifestPath: string
252
+ textPath: string
253
+ images: DocumentMaterial[]
254
+ skippedAssets: SkippedAsset[]
255
+ tables: DocumentMaterial[]
256
+ slides?: PptxSlide[]
257
+ }): string {
258
+ const readViewPath = join(input.cacheDir, "read.md")
259
+ writeFileSync(readViewPath, buildReadView({
260
+ source: input.source,
261
+ type: input.type,
262
+ fingerprint: input.fingerprint,
263
+ text: input.text,
264
+ manifestPath: input.manifestPath,
265
+ textPath: input.textPath,
266
+ images: input.images,
267
+ skippedAssets: input.skippedAssets,
268
+ tables: input.tables,
269
+ slides: input.slides,
270
+ }), "utf-8")
271
+ return workspaceRelative(readViewPath, input.workspaceDir)
272
+ }
273
+
274
+ function ensureCachedReadView(
275
+ manifest: CachedManifest,
276
+ cacheDir: string,
277
+ workspaceDir: string,
278
+ ): string {
279
+ const existing = manifest.read_view_path
280
+ if (existing && existsSync(join(workspaceDir, existing))) return existing
281
+
282
+ const text = existsSync(join(workspaceDir, manifest.text_path))
283
+ ? readFileSync(join(workspaceDir, manifest.text_path), "utf-8").replace(/^\[Extracted from: .*?\]\n\n/, "")
284
+ : ""
285
+ return writeReadView({
286
+ cacheDir,
287
+ workspaceDir,
288
+ source: manifest.source,
289
+ type: manifest.type,
290
+ fingerprint: manifest.fingerprint,
291
+ text,
292
+ manifestPath: manifest.manifest_path,
293
+ textPath: manifest.text_path,
294
+ images: manifest.images,
295
+ skippedAssets: manifest.skipped_assets,
296
+ tables: manifest.tables,
297
+ slides: manifest.slides,
298
+ })
299
+ }
300
+
160
301
  function updateDecksSourceMaterialIndex(
161
302
  workspaceDir: string,
162
303
  filePath: string,
@@ -716,6 +857,7 @@ async function processPdfFile(filePath: string, workspaceDir: string): Promise<D
716
857
 
717
858
  if (existsSync(manifestPath)) {
718
859
  const manifest = JSON.parse(readFileSync(manifestPath, "utf-8")) as CachedManifest
860
+ const readViewPath = ensureCachedReadView(manifest, cacheDir, workspaceDir)
719
861
  return {
720
862
  status: "processed",
721
863
  cache_status: "hit",
@@ -724,6 +866,7 @@ async function processPdfFile(filePath: string, workspaceDir: string): Promise<D
724
866
  cache_dir: manifest.cache_dir,
725
867
  manifest_path: manifest.manifest_path,
726
868
  text_path: manifest.text_path,
869
+ read_view_path: readViewPath,
727
870
  images: manifest.images,
728
871
  skipped_assets: manifest.skipped_assets,
729
872
  slides: manifest.slides,
@@ -740,6 +883,22 @@ async function processPdfFile(filePath: string, workspaceDir: string): Promise<D
740
883
  writeFileSync(textPath, `[Extracted from: ${basename(filePath)}]\n\n${text}`, "utf-8")
741
884
 
742
885
  const images = await extractPdfImages(buf, cacheDir, workspaceDir)
886
+ const relativeManifestPath = workspaceRelative(manifestPath, workspaceDir)
887
+ const relativeTextPath = workspaceRelative(textPath, workspaceDir)
888
+ const readViewPath = writeReadView({
889
+ cacheDir,
890
+ workspaceDir,
891
+ source: relativeSource,
892
+ type: "pdf",
893
+ fingerprint,
894
+ text,
895
+ manifestPath: relativeManifestPath,
896
+ textPath: relativeTextPath,
897
+ images,
898
+ skippedAssets: [],
899
+ tables: [],
900
+ slides: [],
901
+ })
743
902
 
744
903
  const result: DocumentMaterialsResult = {
745
904
  status: "processed",
@@ -747,8 +906,9 @@ async function processPdfFile(filePath: string, workspaceDir: string): Promise<D
747
906
  source: relativeSource,
748
907
  type: "pdf",
749
908
  cache_dir: workspaceRelative(cacheDir, workspaceDir),
750
- manifest_path: workspaceRelative(manifestPath, workspaceDir),
751
- text_path: workspaceRelative(textPath, workspaceDir),
909
+ manifest_path: relativeManifestPath,
910
+ text_path: relativeTextPath,
911
+ read_view_path: readViewPath,
752
912
  images,
753
913
  skipped_assets: [],
754
914
  slides: [],
@@ -762,6 +922,7 @@ async function processPdfFile(filePath: string, workspaceDir: string): Promise<D
762
922
  cache_dir: result.cache_dir!,
763
923
  manifest_path: result.manifest_path!,
764
924
  text_path: result.text_path!,
925
+ read_view_path: result.read_view_path,
765
926
  images: result.images ?? [],
766
927
  skipped_assets: [],
767
928
  slides: [],
@@ -780,6 +941,7 @@ async function processOfficeFile(filePath: string, workspaceDir: string, type: S
780
941
 
781
942
  if (existsSync(manifestPath)) {
782
943
  const manifest = JSON.parse(readFileSync(manifestPath, "utf-8")) as CachedManifest
944
+ const readViewPath = ensureCachedReadView(manifest, cacheDir, workspaceDir)
783
945
  return {
784
946
  status: "processed",
785
947
  cache_status: "hit",
@@ -788,6 +950,7 @@ async function processOfficeFile(filePath: string, workspaceDir: string, type: S
788
950
  cache_dir: manifest.cache_dir,
789
951
  manifest_path: manifest.manifest_path,
790
952
  text_path: manifest.text_path,
953
+ read_view_path: readViewPath,
791
954
  images: manifest.images,
792
955
  skipped_assets: manifest.skipped_assets,
793
956
  slides: manifest.slides,
@@ -821,6 +984,24 @@ async function processOfficeFile(filePath: string, workspaceDir: string, type: S
821
984
  const slides = type === "pptx"
822
985
  ? extractPptxSlides(files, images, pptxAssets!.skipped_assets)
823
986
  : undefined
987
+ const relativeManifestPath = workspaceRelative(manifestPath, workspaceDir)
988
+ const relativeTextPath = workspaceRelative(textPath, workspaceDir)
989
+ const tables = extractTables(type, relativeTextPath)
990
+ const skippedAssets = pptxAssets?.skipped_assets ?? []
991
+ const readViewPath = writeReadView({
992
+ cacheDir,
993
+ workspaceDir,
994
+ source: relativeSource,
995
+ type,
996
+ fingerprint,
997
+ text,
998
+ manifestPath: relativeManifestPath,
999
+ textPath: relativeTextPath,
1000
+ images,
1001
+ skippedAssets,
1002
+ tables,
1003
+ slides,
1004
+ })
824
1005
 
825
1006
  const result: DocumentMaterialsResult = {
826
1007
  status: "processed",
@@ -828,12 +1009,13 @@ async function processOfficeFile(filePath: string, workspaceDir: string, type: S
828
1009
  source: relativeSource,
829
1010
  type,
830
1011
  cache_dir: workspaceRelative(cacheDir, workspaceDir),
831
- manifest_path: workspaceRelative(manifestPath, workspaceDir),
832
- text_path: workspaceRelative(textPath, workspaceDir),
1012
+ manifest_path: relativeManifestPath,
1013
+ text_path: relativeTextPath,
1014
+ read_view_path: readViewPath,
833
1015
  images,
834
- skipped_assets: pptxAssets?.skipped_assets ?? [],
1016
+ skipped_assets: skippedAssets,
835
1017
  slides,
836
- tables: extractTables(type, workspaceRelative(textPath, workspaceDir)),
1018
+ tables,
837
1019
  }
838
1020
 
839
1021
  const manifest: CachedManifest = {
@@ -843,6 +1025,7 @@ async function processOfficeFile(filePath: string, workspaceDir: string, type: S
843
1025
  cache_dir: result.cache_dir!,
844
1026
  manifest_path: result.manifest_path!,
845
1027
  text_path: result.text_path!,
1028
+ read_view_path: result.read_view_path,
846
1029
  images: result.images ?? [],
847
1030
  skipped_assets: result.skipped_assets ?? [],
848
1031
  slides: result.slides ?? [],