@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.
- package/lib/design/designs.ts +123 -39
- package/lib/document-materials/extract.ts +189 -6
- package/lib/material-intake.ts +494 -0
- package/lib/runtime/index.ts +46 -4
- package/package.json +1 -1
- package/plugins/revela/.mcp.json +1 -1
- package/plugins/revela/hooks/hooks.json +10 -0
- package/plugins/revela/hooks/revela_material_notice.ts +58 -0
- package/plugins/revela/mcp/revela-server.ts +105 -0
- package/plugins/revela/skills/revela-design/SKILL.md +7 -5
- package/plugins/revela/skills/revela-init/SKILL.md +18 -8
- package/plugins/revela/skills/revela-make-deck/SKILL.md +10 -9
package/lib/design/designs.ts
CHANGED
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* DesignManager — manage revela visual design templates.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
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
|
-
|
|
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
|
|
162
|
-
|
|
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
|
-
|
|
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
|
|
192
|
-
if (!
|
|
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 =
|
|
206
|
-
|
|
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
|
-
|
|
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 `
|
|
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 `
|
|
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
|
|
616
|
-
if (!
|
|
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
|
|
649
|
-
if (!
|
|
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
|
|
676
|
-
if (!
|
|
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
|
-
|
|
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
|
|
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:
|
|
751
|
-
text_path:
|
|
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:
|
|
832
|
-
text_path:
|
|
1012
|
+
manifest_path: relativeManifestPath,
|
|
1013
|
+
text_path: relativeTextPath,
|
|
1014
|
+
read_view_path: readViewPath,
|
|
833
1015
|
images,
|
|
834
|
-
skipped_assets:
|
|
1016
|
+
skipped_assets: skippedAssets,
|
|
835
1017
|
slides,
|
|
836
|
-
tables
|
|
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 ?? [],
|