good initial page state
This commit is contained in:
parent
2cd689b468
commit
7e71cb947b
@ -250,13 +250,9 @@ fn get_prim_by_path(
|
||||
}
|
||||
|
||||
fn get_prim_model_by_path_with_file(path: &str, file: &CosFile) -> Result<PrimitiveModel, String> {
|
||||
let (step, prim, detail_path) = get_prim_by_path_with_file(path, file)?;
|
||||
let (step, prim, trace) = get_prim_by_path_with_file(path, file)?;
|
||||
|
||||
Ok(PrimitiveModel::from_primitive_with_children(
|
||||
step.get_key(),
|
||||
&prim,
|
||||
detail_path,
|
||||
))
|
||||
Ok(PrimitiveModel::from_primitive_with_children(&prim, trace))
|
||||
}
|
||||
|
||||
fn get_prim_by_path_with_file(
|
||||
@ -286,7 +282,7 @@ fn get_prim_by_steps_with_file(
|
||||
current_prim = resolve_step(¤t_prim, &step)?;
|
||||
if let Primitive::Reference(xref) = current_prim {
|
||||
last_jump = xref.id.to_string();
|
||||
parent = resolve_pref(xref.clone(), file)?;
|
||||
parent = resolve_p_ref(xref.clone(), file)?;
|
||||
current_prim = &parent;
|
||||
}
|
||||
trace.push(PathTrace::new(step.get_key(), last_jump.clone()));
|
||||
@ -296,9 +292,9 @@ fn get_prim_by_steps_with_file(
|
||||
|
||||
fn resolve_parent(step: Step, file: &CosFile) -> Result<(Primitive, PathTrace), String> {
|
||||
let parent = match step {
|
||||
Step::Page(page_num) => return retrieve_page(page_num, file),
|
||||
Step::Number(obj_num) => resolve_xref(obj_num, file)?,
|
||||
Step::Trailer => retrieve_trailer(file),
|
||||
Step::Page(page_num) => retrieve_page(page_num, file)?,
|
||||
_ => return Err(String::from(format!("{:?} is not a valid path!", step))),
|
||||
};
|
||||
Ok((parent, PathTrace::new(step.get_key(), step.get_key())))
|
||||
@ -323,12 +319,11 @@ fn get_prim_tree_by_path_with_file(
|
||||
file: &CosFile,
|
||||
) -> Result<PrimitiveModel, String> {
|
||||
let step = node.step()?;
|
||||
let (mut parent, trace) = resolve_parent(step.clone(), file)?;
|
||||
let (parent, trace) = resolve_parent(step.clone(), file)?;
|
||||
|
||||
let path = vec![trace];
|
||||
let mut parent_model = PrimitiveModel::from_primitive_with_children(&parent, path);
|
||||
|
||||
let mut parent_model =
|
||||
PrimitiveModel::from_primitive_with_children(step.get_key(), &parent, path);
|
||||
for child in node.children.iter() {
|
||||
expand(child, &mut parent_model, &parent, file)?;
|
||||
}
|
||||
@ -420,7 +415,6 @@ fn resolve_step<'a>(current_prim: &'a Primitive, step: &Step) -> Result<&'a Prim
|
||||
)))
|
||||
}
|
||||
},
|
||||
Step::Data => return Err("Not implemented!".to_string()),
|
||||
_ => return Err(format!("Invalid Step: {}", step.get_key())),
|
||||
})
|
||||
}
|
||||
@ -430,9 +424,13 @@ fn retrieve_trailer(file: &CosFile) -> Primitive {
|
||||
file.trailer.to_primitive(&mut updater).unwrap()
|
||||
}
|
||||
|
||||
fn retrieve_page(page_num: u32, file: &CosFile) -> Result<Primitive, String> {
|
||||
fn retrieve_page(page_num: u32, file: &CosFile) -> Result<(Primitive, PathTrace), String> {
|
||||
let page_rc = t!(file.get_page(page_num - 1));
|
||||
resolve_pref(page_rc.get_ref().get_inner(), file)
|
||||
let p_ref = page_rc.get_ref().get_inner();
|
||||
Ok((
|
||||
resolve_p_ref(p_ref, file)?,
|
||||
PathTrace::new(format!("Page{}", page_num), p_ref.id.to_string()),
|
||||
))
|
||||
}
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub enum Step {
|
||||
@ -498,10 +496,10 @@ impl Step {
|
||||
|
||||
fn resolve_xref(id: u64, file: &CosFile) -> Result<Primitive, String> {
|
||||
let plain_ref = PlainRef { id, gen: 0 };
|
||||
resolve_pref(plain_ref, file)
|
||||
resolve_p_ref(plain_ref, file)
|
||||
}
|
||||
|
||||
fn resolve_pref(plain_ref: PlainRef, file: &CosFile) -> Result<Primitive, String> {
|
||||
fn resolve_p_ref(plain_ref: PlainRef, file: &CosFile) -> Result<Primitive, String> {
|
||||
file.resolver()
|
||||
.resolve(plain_ref)
|
||||
.map_err(|e| e.to_string())
|
||||
@ -595,12 +593,12 @@ impl PrimitiveModel {
|
||||
result
|
||||
}
|
||||
|
||||
fn from_primitive_with_children(
|
||||
key: String,
|
||||
primitive: &Primitive,
|
||||
path: Vec<PathTrace>,
|
||||
) -> PrimitiveModel {
|
||||
let mut model = PrimitiveModel::from_primitive(key, primitive, path.clone());
|
||||
fn from_primitive_with_children(primitive: &Primitive, path: Vec<PathTrace>) -> PrimitiveModel {
|
||||
let mut model = PrimitiveModel::from_primitive(
|
||||
path.last().unwrap().key.clone(),
|
||||
primitive,
|
||||
path.clone(),
|
||||
);
|
||||
model.add_children(primitive, path);
|
||||
model
|
||||
}
|
||||
|
||||
@ -23,7 +23,7 @@
|
||||
let treeShowing: boolean = $state(true);
|
||||
let pagesShowing: boolean = $state(false);
|
||||
let fileViewHeight: number = $derived(
|
||||
Math.max(innerHeight - footerHeight - titleBarHeight, 0),
|
||||
Math.max(innerHeight - footerHeight - tabBarHeight - titleBarHeight, 0),
|
||||
);
|
||||
|
||||
let fStates: Map<string, FileViewState> = new Map<string, FileViewState>();
|
||||
@ -109,7 +109,7 @@
|
||||
<div style="height: {titleBarHeight}px">
|
||||
<TitleBar></TitleBar>
|
||||
</div>
|
||||
<div style="height: {fileViewHeight}px">
|
||||
<div class="fileview_container" style="height: {fileViewHeight + 30}px">
|
||||
<Splitpanes theme="forge-movable" dblClickSplitter={false}>
|
||||
<Pane size={2.5} minSize={1.5} maxSize={4}>
|
||||
<ToolbarLeft bind:tree={treeShowing} bind:pages={pagesShowing}
|
||||
|
||||
@ -7,8 +7,8 @@
|
||||
|
||||
let { fState, height }: { fState: FileViewState; height: number } =
|
||||
$props();
|
||||
let h = $derived(height - 34);
|
||||
let path = $derived(fState.prim?.getLastJump().toString());
|
||||
let h = $derived(height);
|
||||
let path = $derived(fState.prim?.getFirstJump()?.toString());
|
||||
let id = $derived(fState.file.id);
|
||||
let contents: ContentModel | undefined = $state(undefined);
|
||||
let editorContainer: HTMLElement;
|
||||
@ -41,6 +41,7 @@
|
||||
|
||||
invoke<ContentModel>("get_contents", { id, path })
|
||||
.then((result) => {
|
||||
console.log("Contents loaded", result);
|
||||
contents = result;
|
||||
if (contents && editor) {
|
||||
const text = contents.parts
|
||||
@ -49,13 +50,6 @@
|
||||
"\n\n%-------------------% EOF %-------------------%\n\n",
|
||||
);
|
||||
editor.setValue(text);
|
||||
const model = editor.getModel();
|
||||
if (model) {
|
||||
monaco.editor.setModelLanguage(
|
||||
model,
|
||||
"pdf-content-stream",
|
||||
);
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch((err) => console.error(err));
|
||||
|
||||
@ -4,9 +4,12 @@
|
||||
import PrimitiveView from "./PrimitiveView.svelte";
|
||||
import TreeView from "./TreeView.svelte";
|
||||
import type FileViewState from "../models/FileViewState.svelte";
|
||||
import { onMount } from "svelte";
|
||||
import { createEventDispatcher, onMount } from "svelte";
|
||||
import PageList from "./PageList.svelte";
|
||||
import ContentsView from "./ContentsView.svelte";
|
||||
import TreeViewNode from "../models/TreeViewNode.svelte";
|
||||
import TreeViewState from "../models/TreeViewState.svelte";
|
||||
import type { PathSelectedEvent } from "../events/PathSelectedEvent";
|
||||
|
||||
let {
|
||||
treeShowing,
|
||||
@ -49,37 +52,33 @@
|
||||
window.removeEventListener("mousedown", handleMouseButton);
|
||||
};
|
||||
});
|
||||
|
||||
function pathSelectedHandler(event: PathSelectedEvent) {
|
||||
console.log("Path selected", event.detail.path);
|
||||
fState.selectPath(event.detail.path);
|
||||
}
|
||||
</script>
|
||||
|
||||
<div bind:clientWidth={width} class="file-view-container">
|
||||
<div style="width: {splitPanesWidth}px">
|
||||
<div
|
||||
class="flex flex-row"
|
||||
style="width: {splitPanesWidth}px; height: {height}px"
|
||||
>
|
||||
<Splitpanes theme="forge-movable" pushOtherPanes={false}>
|
||||
<Pane
|
||||
size={treeShowing || pagesShowing ? 15 : 0}
|
||||
minSize={treeShowing || pagesShowing ? 1 : 0}
|
||||
minSize={treeShowing || pagesShowing ? 2 : 0}
|
||||
maxSize={treeShowing || pagesShowing ? 100 : 0}
|
||||
>
|
||||
<Splitpanes
|
||||
theme="forge-movable"
|
||||
horizontal
|
||||
pushOtherPanes={false}
|
||||
style="height: {height}px"
|
||||
>
|
||||
<Pane
|
||||
size={treeShowing ? (pagesShowing ? 50 : 100) : 0}
|
||||
minSize={treeShowing ? 2 : 0}
|
||||
maxSize={treeShowing ? 100 : 0}
|
||||
>
|
||||
<TreeView {fState}></TreeView>
|
||||
</Pane>
|
||||
<Pane
|
||||
size={pagesShowing ? (treeShowing ? 50 : 100) : 0}
|
||||
minSize={pagesShowing ? 2 : 0}
|
||||
maxSize={pagesShowing ? 100 : 0}
|
||||
>
|
||||
<PageList {fState}></PageList>
|
||||
</Pane>
|
||||
</Splitpanes>
|
||||
{#if pagesShowing}
|
||||
<PageList {fState} h={height}></PageList>
|
||||
{:else if treeShowing}
|
||||
<TreeView
|
||||
bind:pathSelectedHandler
|
||||
file_id={fState.file.id}
|
||||
root={TreeViewNode.TRAILER}
|
||||
></TreeView>
|
||||
{/if}
|
||||
</Pane>
|
||||
|
||||
<Pane minSize={1}>
|
||||
@ -98,7 +97,7 @@
|
||||
</div>
|
||||
{#if xrefTableShowing}
|
||||
<div class="xref-modal" class:visible={xrefTableShowing}>
|
||||
<XRefTable {fState}></XRefTable>
|
||||
<XRefTable {fState} {height}></XRefTable>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
@ -110,21 +109,13 @@
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.xref-modal-container {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
width: 0;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.xref-modal {
|
||||
position: absolute;
|
||||
border-left: 1px solid var(--border-color);
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
width: 281px;
|
||||
width: 280px;
|
||||
background: var(--background-color);
|
||||
box-shadow: -2px 0 5px rgba(0, 0, 0, 0.1);
|
||||
z-index: 1000;
|
||||
|
||||
@ -24,8 +24,6 @@
|
||||
fState && fState.prim ? toElements(fState.prim.trace) : undefined,
|
||||
);
|
||||
|
||||
$inspect(fState?.path);
|
||||
$inspect(fState?.prim?.trace);
|
||||
function toElements(path: Trace[]): Path[] {
|
||||
if (path.length == 0) {
|
||||
return [new Path("Trailer", 0)];
|
||||
@ -52,38 +50,30 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<div
|
||||
class="bg-forge-prim border border-forge-bound"
|
||||
style="height: {footerHeight}px"
|
||||
>
|
||||
<div class="flex flex-row items-center" style="height: {footerHeight}px">
|
||||
{#if elements}
|
||||
{#each elements as path}
|
||||
<button
|
||||
class="flex flex-row items-center ml-2"
|
||||
onclick={() => selectPath(path)}
|
||||
>
|
||||
{#if path.jump}
|
||||
<div
|
||||
class="flex items-center mr-1 ml-1"
|
||||
style="height: {footerHeight}px"
|
||||
>
|
||||
<PrimitiveIcon ptype={"Reference"}></PrimitiveIcon>
|
||||
</div>
|
||||
{:else}
|
||||
<div
|
||||
class="flex items-center"
|
||||
style="height: {footerHeight}px"
|
||||
>
|
||||
<CaretRightOutline class="text-forge-sec" />
|
||||
</div>
|
||||
{/if}
|
||||
<p class="text-xs ml-1">{path.value}</p>
|
||||
</button>
|
||||
{/each}
|
||||
{/if}
|
||||
</div>
|
||||
<div class="footer" style="height: {footerHeight}px">
|
||||
{#if elements}
|
||||
{#each elements as path}
|
||||
<button
|
||||
class="flex flex-row items-center ml-2"
|
||||
onclick={() => selectPath(path)}
|
||||
>
|
||||
{#if path.jump}
|
||||
<PrimitiveIcon ptype={"Reference"}></PrimitiveIcon>
|
||||
{:else}
|
||||
<CaretRightOutline class="text-forge-sec" />
|
||||
{/if}
|
||||
<p class="text-xs ml-1">{path.value}</p>
|
||||
</button>
|
||||
{/each}
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style lang="postcss">
|
||||
.footer {
|
||||
@apply bg-forge-prim border border-forge-bound flex flex-row items-center;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -3,8 +3,7 @@
|
||||
import PrimitiveIcon from "./PrimitiveIcon.svelte";
|
||||
import type PageModel from "../models/PageModel";
|
||||
|
||||
let { fState }: { fState: FileViewState } = $props();
|
||||
let h: number = $state(100);
|
||||
let { fState, h }: { fState: FileViewState; h: number } = $props();
|
||||
let selected: PageModel | undefined = $state(undefined);
|
||||
|
||||
function handlePageSelect(page: PageModel) {
|
||||
@ -13,43 +12,37 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<div bind:clientHeight={h} class="full-container">
|
||||
<div class="overflow-x-auto">
|
||||
<div class="w-[251px]">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<td class="page-cell t-header border-forge-prim"
|
||||
>Page</td
|
||||
>
|
||||
<td class="ref-cell t-header border-forge-sec">Ref</td>
|
||||
<div class="overflow-x-auto">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<td class="page-cell t-header border-forge-prim">Page</td>
|
||||
<td class="ref-cell t-header border-forge-sec">Ref</td>
|
||||
</tr>
|
||||
</thead>
|
||||
</table>
|
||||
<div class="overflow-y-auto" style="height: {h - 25}px">
|
||||
<table>
|
||||
<tbody>
|
||||
{#each fState.file.pages as page}
|
||||
<tr
|
||||
class:selected={page === selected}
|
||||
class="hover:bg-forge-sec"
|
||||
ondblclick={() => handlePageSelect(page)}
|
||||
>
|
||||
<td class="page-cell t-data">
|
||||
<div class="key-field">
|
||||
<PrimitiveIcon ptype={"Reference"} />
|
||||
<p class="text-left">
|
||||
{page.key}
|
||||
</p>
|
||||
</div>
|
||||
</td>
|
||||
<td class="ref-cell t-data">{page.obj_num}</td>
|
||||
</tr>
|
||||
</thead>
|
||||
</table>
|
||||
<div class="overflow-y-auto" style="height: {h - 55}px">
|
||||
<table>
|
||||
<tbody>
|
||||
{#each fState.file.pages as page}
|
||||
<tr
|
||||
class:selected={page === selected}
|
||||
class="hover:bg-forge-sec"
|
||||
ondblclick={() => handlePageSelect(page)}
|
||||
>
|
||||
<td class="page-cell t-data">
|
||||
<div class="key-field">
|
||||
<PrimitiveIcon ptype={"Reference"} />
|
||||
<p class="text-left">
|
||||
{page.key}
|
||||
</p>
|
||||
</div>
|
||||
</td>
|
||||
<td class="ref-cell t-data">{page.id}</td>
|
||||
</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -71,7 +64,7 @@
|
||||
}
|
||||
|
||||
.t-data {
|
||||
@apply border border-forge-sec text-sm text-forge-text text-left;
|
||||
@apply border border-forge-sec text-xs text-forge-text text-left;
|
||||
text-align: left;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
@ -6,11 +6,10 @@
|
||||
|
||||
let { fState, height }: { fState: FileViewState; height: number } =
|
||||
$props();
|
||||
let bodyHeight = $derived(height - 54);
|
||||
let selected: Primitive | undefined = $state(undefined);
|
||||
let bodyHeight = $derived(height - 24);
|
||||
let prim = $derived(fState.prim);
|
||||
let showContents = $state(false);
|
||||
|
||||
$inspect(fState.highlightedPrim);
|
||||
function handlePrimSelect(prim: Primitive) {
|
||||
if (prim.isContainer()) {
|
||||
if (!prim) {
|
||||
@ -44,9 +43,10 @@
|
||||
<tbody>
|
||||
{#each prim.children as entry}
|
||||
<tr
|
||||
class:selected={entry === selected}
|
||||
class:selected={entry.key ===
|
||||
fState.highlightedPrim?.key}
|
||||
class="hover:bg-forge-sec"
|
||||
onclick={() => (selected = entry)}
|
||||
onclick={() => (fState.highlightedPrim = entry)}
|
||||
ondblclick={() => handlePrimSelect(entry)}
|
||||
>
|
||||
<td class="page-cell t-data">
|
||||
|
||||
@ -1,33 +1,48 @@
|
||||
<script lang="ts">
|
||||
import {BookOpenSolid, CodeBranchSolid} from "flowbite-svelte-icons";
|
||||
|
||||
let {tree = $bindable(false), pages = $bindable(false)}: { tree: boolean, pages: boolean } = $props();
|
||||
import { BookOpenSolid, CodeBranchSolid } from "flowbite-svelte-icons";
|
||||
|
||||
let {
|
||||
tree = $bindable(false),
|
||||
pages = $bindable(false),
|
||||
}: { tree: boolean; pages: boolean } = $props();
|
||||
|
||||
function toggleTree() {
|
||||
tree = !tree;
|
||||
if (tree) {
|
||||
pages = false;
|
||||
}
|
||||
}
|
||||
|
||||
function togglePages() {
|
||||
pages = !pages;
|
||||
if (pages) {
|
||||
tree = false;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="grid grid-cols-1">
|
||||
<button class={ tree ? "tool-button active" : "tool-button" } onclick={toggleTree} >
|
||||
<button
|
||||
class={tree ? "tool-button active" : "tool-button"}
|
||||
onclick={toggleTree}
|
||||
>
|
||||
<div class="justify-center flex m-0">
|
||||
<CodeBranchSolid class="rotate-180 scale-x-[-1]"/>
|
||||
<CodeBranchSolid class="rotate-180 scale-x-[-1]" />
|
||||
</div>
|
||||
<b class="button-title">Tree</b>
|
||||
</button>
|
||||
<button id="#page" class={ pages ? "tool-button active" : "tool-button" } onclick={togglePages}>
|
||||
<div class="justify-center flex ">
|
||||
<BookOpenSolid/>
|
||||
<button
|
||||
id="#page"
|
||||
class={pages ? "tool-button active" : "tool-button"}
|
||||
onclick={togglePages}
|
||||
>
|
||||
<div class="justify-center flex">
|
||||
<BookOpenSolid />
|
||||
</div>
|
||||
<b class="button-title">Page</b>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
|
||||
<style lang="postcss">
|
||||
.button-title {
|
||||
@apply text-forge-text_sec text-xs;
|
||||
@ -41,4 +56,4 @@
|
||||
.tool-button.active {
|
||||
@apply bg-forge-active focus:bg-forge-acc;
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@ -2,16 +2,21 @@
|
||||
import type Primitive from "../models/Primitive.svelte";
|
||||
import PrimitiveIcon from "./PrimitiveIcon.svelte";
|
||||
import { CaretRightOutline, CaretDownOutline } from "flowbite-svelte-icons";
|
||||
import type FileViewState from "../models/FileViewState.svelte";
|
||||
import { arraysAreEqual } from "../utils.js";
|
||||
import type TreeViewState from "../models/TreeViewState.svelte";
|
||||
import { PathSelectedEvent } from "../events/PathSelectedEvent";
|
||||
import { createEventDispatcher } from "svelte";
|
||||
|
||||
let {
|
||||
prim,
|
||||
path,
|
||||
fState,
|
||||
}: { prim: Primitive | undefined; path: string[]; fState: FileViewState } =
|
||||
$props();
|
||||
let active = $derived(arraysAreEqual(path, fState.path));
|
||||
parent_view,
|
||||
parent_path,
|
||||
treeState,
|
||||
pathSelectedHandler = $bindable(),
|
||||
}: {
|
||||
parent_view: Primitive;
|
||||
parent_path: string[];
|
||||
treeState: TreeViewState;
|
||||
pathSelectedHandler: any;
|
||||
} = $props();
|
||||
|
||||
function copyPathAndAppend(key: string): string[] {
|
||||
const _path = copyPath();
|
||||
@ -22,33 +27,31 @@
|
||||
function copyPath(): string[] {
|
||||
const _path: string[] = [];
|
||||
|
||||
for (let item of path) {
|
||||
for (let item of parent_path) {
|
||||
_path.push(item);
|
||||
}
|
||||
return _path;
|
||||
}
|
||||
|
||||
function selectItem(child: Primitive) {
|
||||
if (child.isContainer()) {
|
||||
let _path = copyPathAndAppend(child.key);
|
||||
fState.expandTree(_path);
|
||||
fState.selectPath(_path);
|
||||
} else {
|
||||
let _path = copyPath();
|
||||
fState.selectPath(_path);
|
||||
}
|
||||
let _path = copyPathAndAppend(child.key);
|
||||
console.log("Selecting from tree", _path);
|
||||
treeState.expandTree(_path);
|
||||
|
||||
const event = new PathSelectedEvent(_path);
|
||||
pathSelectedHandler(event);
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if prim}
|
||||
{#if parent_view}
|
||||
<ul class="active">
|
||||
{#each prim.children as child}
|
||||
{#each parent_view.children as child}
|
||||
<li>
|
||||
<div class="item">
|
||||
{#if child.children.length > 0}
|
||||
<button
|
||||
onclick={() =>
|
||||
fState.collapseTree(
|
||||
treeState.collapseTree(
|
||||
copyPathAndAppend(child.key),
|
||||
)}
|
||||
><span class="caret"><CaretDownOutline /></span
|
||||
@ -57,7 +60,9 @@
|
||||
{:else if child.isContainer()}
|
||||
<button
|
||||
onclick={() =>
|
||||
fState.expandTree(copyPathAndAppend(child.key))}
|
||||
treeState.expandTree(
|
||||
copyPathAndAppend(child.key),
|
||||
)}
|
||||
><span class="caret"><CaretRightOutline /></span
|
||||
></button
|
||||
>
|
||||
@ -85,9 +90,10 @@
|
||||
</div>
|
||||
{#if child.children.length > 0}
|
||||
<svelte:self
|
||||
prim={child}
|
||||
path={copyPathAndAppend(child.key)}
|
||||
{fState}
|
||||
parent_view={child}
|
||||
parent_path={copyPathAndAppend(child.key)}
|
||||
{treeState}
|
||||
bind:pathSelectedHandler
|
||||
></svelte:self>
|
||||
{/if}
|
||||
</li>
|
||||
|
||||
@ -1,30 +1,48 @@
|
||||
<script lang="ts">
|
||||
import TreeNode from "./TreeNode.svelte";
|
||||
import type FileViewState from "../models/FileViewState.svelte";
|
||||
import type Primitive from "../models/Primitive.svelte";
|
||||
import PrimitiveIcon from "./PrimitiveIcon.svelte";
|
||||
import TreeViewState from "../models/TreeViewState.svelte";
|
||||
import type TreeViewNode from "../models/TreeViewNode.svelte";
|
||||
|
||||
let { fState }: { fState: FileViewState } = $props();
|
||||
let prim: Primitive | undefined = $derived(fState.treeView);
|
||||
let {
|
||||
file_id,
|
||||
root,
|
||||
pathSelectedHandler = $bindable(),
|
||||
active = true,
|
||||
}: {
|
||||
file_id: string;
|
||||
root: TreeViewNode;
|
||||
pathSelectedHandler: any;
|
||||
active: boolean;
|
||||
} = $props();
|
||||
let h = $state(100);
|
||||
let treeState = $state(new TreeViewState(file_id, root));
|
||||
</script>
|
||||
|
||||
<div bind:clientHeight={h} class="full-container">
|
||||
<div class="overflow-auto" style="height: {h}px">
|
||||
<ul id="myUL">
|
||||
{#if prim}
|
||||
{#if treeState.view}
|
||||
<li>
|
||||
<div class="item">
|
||||
<PrimitiveIcon ptype={"Dictionary"} />
|
||||
{"Trailer "}
|
||||
</div>
|
||||
<TreeNode {prim} path={prim.getTrace()} {fState}></TreeNode>
|
||||
{#if active}
|
||||
<TreeNode
|
||||
bind:pathSelectedHandler
|
||||
parent_view={treeState.view}
|
||||
parent_path={treeState.view.getPath()}
|
||||
{treeState}
|
||||
></TreeNode>
|
||||
{/if}
|
||||
</li>
|
||||
{/if}
|
||||
</ul>
|
||||
</div>
|
||||
getTrace
|
||||
</div>
|
||||
getPath
|
||||
|
||||
<style lang="postcss">
|
||||
.item {
|
||||
|
||||
@ -3,10 +3,10 @@
|
||||
import type XRefEntry from "../models/XRefEntry";
|
||||
|
||||
const cellH = 25;
|
||||
const headerOffset = 80;
|
||||
let { fState }: { fState: FileViewState } = $props();
|
||||
const headerOffset = 2 * cellH;
|
||||
let { fState, height }: { fState: FileViewState; height: number } =
|
||||
$props();
|
||||
|
||||
let viewHeight: number = $state(100);
|
||||
let fillerHeight: number = $state(0);
|
||||
|
||||
let firstEntry = $state(0);
|
||||
@ -15,9 +15,8 @@
|
||||
fState.xref_entries.slice(firstEntry, lastEntry),
|
||||
);
|
||||
|
||||
let bodyViewHeight: number = $derived(
|
||||
Math.max(viewHeight - headerOffset, 0),
|
||||
);
|
||||
let bodyViewHeight: number = $derived(Math.max(height - headerOffset, 0));
|
||||
$inspect(bodyViewHeight);
|
||||
let selectedPath: number | string | undefined = $derived(
|
||||
fState.getLastJump(),
|
||||
);
|
||||
@ -38,7 +37,7 @@
|
||||
|
||||
if (
|
||||
targetY - startY > 0 &&
|
||||
targetY - startY < Math.max(viewHeight - headerOffset, 0)
|
||||
targetY - startY < Math.max(height - headerOffset, 0)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
@ -74,61 +73,55 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<div bind:clientHeight={viewHeight} class="full-container">
|
||||
<div class="overflow-x-auto">
|
||||
<div class="w-[281px]">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<td class="cell t-header border-forge-prim">Obj Nr</td>
|
||||
<td class="cell t-header border-forge-prim">Gen Nr</td>
|
||||
<td class="cell t-header border-forge-prim">Type</td>
|
||||
<td class="cell t-header border-forge-sec">Offset</td>
|
||||
</tr>
|
||||
<tr
|
||||
class={selectedPath === "/"
|
||||
? "bg-forge-acc"
|
||||
: "hover:bg-forge-sec"}
|
||||
ondblclick={() => fState.selectXref(undefined)}
|
||||
>
|
||||
<td class="cell t-data">Trailer</td>
|
||||
<td class="cell t-data">65535</td>
|
||||
<td class="cell t-data">Dictionary</td>
|
||||
<td class="cell t-data">End of file</td>
|
||||
</tr>
|
||||
</thead>
|
||||
</table>
|
||||
<div
|
||||
class="scrollContainer"
|
||||
style="height: {bodyViewHeight}px"
|
||||
onscroll={handleScroll}
|
||||
bind:this={scrollContainer}
|
||||
>
|
||||
<div
|
||||
class="container"
|
||||
style="height: {totalBodyHeight - headerOffset}px"
|
||||
<div class="overflow-x-auto" style="height: {height}px;">
|
||||
<div class="w-[281px]">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<td class="cell t-header border-forge-prim">Obj Nr</td>
|
||||
<td class="cell t-header border-forge-prim">Gen Nr</td>
|
||||
<td class="cell t-header border-forge-prim">Type</td>
|
||||
<td class="cell t-header border-forge-sec">Offset</td>
|
||||
</tr>
|
||||
<tr
|
||||
class={selectedPath === "/"
|
||||
? "bg-forge-acc"
|
||||
: "hover:bg-forge-sec"}
|
||||
ondblclick={() => fState.selectXref(undefined)}
|
||||
>
|
||||
<table>
|
||||
<tbody>
|
||||
<tr class="filler" style="height: {fillerHeight}px"
|
||||
></tr>
|
||||
{#each entriesToDisplay as entry}
|
||||
<tr
|
||||
class={selectedPath === entry.obj_num
|
||||
? "bg-forge-acc"
|
||||
: "hover:bg-forge-sec"}
|
||||
ondblclick={() => fState.selectXref(entry)}
|
||||
>
|
||||
<td class="cell t-data">{entry.obj_num}</td>
|
||||
<td class="cell t-data">{entry.gen_num}</td>
|
||||
<td class="cell t-data">{entry.obj_type}</td
|
||||
>
|
||||
<td class="cell t-data">{entry.offset}</td>
|
||||
</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<td class="cell t-data">Trailer</td>
|
||||
<td class="cell t-data">65535</td>
|
||||
<td class="cell t-data">Dictionary</td>
|
||||
<td class="cell t-data">End of file</td>
|
||||
</tr>
|
||||
</thead>
|
||||
</table>
|
||||
<div
|
||||
class="scrollContainer"
|
||||
style="height: {bodyViewHeight}px"
|
||||
onscroll={handleScroll}
|
||||
bind:this={scrollContainer}
|
||||
>
|
||||
<div class="container" style="height: {totalBodyHeight}px">
|
||||
<table>
|
||||
<tbody>
|
||||
<tr class="filler" style="height: {fillerHeight}px"
|
||||
></tr>
|
||||
{#each entriesToDisplay as entry}
|
||||
<tr
|
||||
class={selectedPath === entry.obj_num
|
||||
? "bg-forge-acc"
|
||||
: "hover:bg-forge-sec"}
|
||||
ondblclick={() => fState.selectXref(entry)}
|
||||
>
|
||||
<td class="cell t-data">{entry.obj_num}</td>
|
||||
<td class="cell t-data">{entry.gen_num}</td>
|
||||
<td class="cell t-data">{entry.obj_type}</td>
|
||||
<td class="cell t-data">{entry.offset}</td>
|
||||
</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
10
src/events/PathSelectedEvent.ts
Normal file
10
src/events/PathSelectedEvent.ts
Normal file
@ -0,0 +1,10 @@
|
||||
export class PathSelectedEvent extends CustomEvent<{ path: string[] }> {
|
||||
static readonly eventName = 'pathselected';
|
||||
|
||||
constructor(path: string[]) {
|
||||
super(PathSelectedEvent.eventName, {
|
||||
detail: { path },
|
||||
bubbles: true
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -8,10 +8,9 @@ import type XRefTable from "./XRefTable";
|
||||
export default class FileViewState {
|
||||
|
||||
public path: string[] = $state(["Trailer"]);
|
||||
public treeRoot: TreeViewNode = $state(new TreeViewNode("Trailer", [new TreeViewNode("Root", [])]));
|
||||
public file: PdfFile;
|
||||
public prim: Primitive | undefined = $state();
|
||||
public treeView: Primitive | undefined = $state();
|
||||
public highlightedPrim: Primitive | undefined = $state();
|
||||
public xref_entries: XRefEntry[] = $state([]);
|
||||
|
||||
|
||||
@ -19,7 +18,6 @@ export default class FileViewState {
|
||||
|
||||
this.file = file;
|
||||
this.selectPath(this.path);
|
||||
this.loadTreeView();
|
||||
this.loadXrefEntries()
|
||||
}
|
||||
|
||||
@ -42,83 +40,30 @@ export default class FileViewState {
|
||||
public selectPath(newPath: string[]) {
|
||||
invoke<Primitive>("get_prim_by_path", { id: this.file.id, path: this.mergePaths(newPath) })
|
||||
.then(result => {
|
||||
this.prim = new Primitive(result)
|
||||
this.path = newPath
|
||||
let _prim = new Primitive(result)
|
||||
if (_prim.isContainer()) {
|
||||
this.prim = _prim;
|
||||
this.path = newPath
|
||||
return;
|
||||
} else {
|
||||
this.highlightedPrim = _prim;
|
||||
this.selectPath(newPath.slice(0, newPath.length - 1))
|
||||
}
|
||||
})
|
||||
.catch(err => console.error(err));
|
||||
}
|
||||
|
||||
|
||||
public loadTreeView() {
|
||||
invoke<Primitive>("get_prim_tree_by_path", { id: this.file.id, path: this.treeRoot })
|
||||
.then(result => {
|
||||
this.treeView = new Primitive(result);
|
||||
}
|
||||
).catch(err => console.error(err))
|
||||
}
|
||||
|
||||
public getTreeRoot() {
|
||||
return this.treeRoot;
|
||||
return this.treeState;
|
||||
}
|
||||
|
||||
public expandTree(path: string[]) {
|
||||
if (path.length == 0) {
|
||||
console.error("Empty path")
|
||||
return;
|
||||
}
|
||||
let node;
|
||||
if (path[0] === "/") {
|
||||
node = this.treeRoot;
|
||||
} else {
|
||||
console.error("Invalid Path " + path);
|
||||
return;
|
||||
}
|
||||
for (let key of path.slice(1, path.length)) {
|
||||
let _node: TreeViewNode | undefined = node.getChild(key)
|
||||
if (_node) {
|
||||
node = _node;
|
||||
} else {
|
||||
node = node.addChild(key)
|
||||
}
|
||||
}
|
||||
this.loadTreeView();
|
||||
}
|
||||
|
||||
public collapseTree(path: string[]) {
|
||||
if (path.length == 0) {
|
||||
console.error("Empty path")
|
||||
return;
|
||||
}
|
||||
let node;
|
||||
if (path[0] === "/") {
|
||||
node = this.treeRoot;
|
||||
} else {
|
||||
console.error("Invalid Path " + path);
|
||||
return;
|
||||
}
|
||||
if (path.length == 1) {
|
||||
this.treeRoot.clearChildren();
|
||||
return;
|
||||
}
|
||||
for (let key of path.slice(1, path.length - 1)) {
|
||||
if (node) {
|
||||
node = node.getChild(key)
|
||||
}
|
||||
}
|
||||
if (node) {
|
||||
node.removeChild(path[path.length - 1]);
|
||||
}
|
||||
this.loadTreeView()
|
||||
}
|
||||
|
||||
public getMergedPath() {
|
||||
return this.mergePaths(this.path);
|
||||
}
|
||||
|
||||
public displayPath() {
|
||||
|
||||
}
|
||||
|
||||
public popPath() {
|
||||
let path = this.copyPath();
|
||||
if (path.length == 1) {
|
||||
|
||||
@ -27,7 +27,7 @@ export default class Primitive {
|
||||
return this.ptype === "Dictionary" || this.ptype === "Array" || this.ptype === "Reference" || this.ptype === "Stream";
|
||||
}
|
||||
|
||||
public getTrace(): string[] {
|
||||
public getPath(): string[] {
|
||||
return this.trace.map(path => path.key);
|
||||
}
|
||||
|
||||
@ -38,7 +38,14 @@ export default class Primitive {
|
||||
}
|
||||
|
||||
public isPage(): boolean {
|
||||
return this.trace[0].last_jump.startsWith("Page");
|
||||
return this.trace[0].key.startsWith("Page") && !isNaN(+this.trace[0].key.replace("Page", ""));
|
||||
}
|
||||
|
||||
public getPageNumber(): number {
|
||||
if (!this.isPage()) {
|
||||
throw new Error("Primitive is not a page");
|
||||
}
|
||||
return +this.trace[0].key.replace("Page", "");
|
||||
}
|
||||
|
||||
public getFirstJump(): string | number | undefined {
|
||||
|
||||
@ -10,6 +10,9 @@ export default class TreeViewNode {
|
||||
this.children = children;
|
||||
}
|
||||
|
||||
static TRAILER = new TreeViewNode("Trailer", [new TreeViewNode("Root", [])]);
|
||||
|
||||
|
||||
public getChild(key: string) {
|
||||
return this.children.find(child => child.key === key);
|
||||
}
|
||||
|
||||
65
src/models/TreeViewState.svelte.ts
Normal file
65
src/models/TreeViewState.svelte.ts
Normal file
@ -0,0 +1,65 @@
|
||||
import { invoke } from "@tauri-apps/api/core";
|
||||
import Primitive from "./Primitive.svelte";
|
||||
import TreeViewNode from "./TreeViewNode.svelte";
|
||||
import type FileViewState from "./FileViewState.svelte";
|
||||
|
||||
export default class TreeViewState {
|
||||
public root: TreeViewNode = $state(new TreeViewNode("Trailer", [new TreeViewNode("Root", [])]));
|
||||
public view: Primitive | undefined = $state();
|
||||
file_id: string;
|
||||
|
||||
constructor(file_id: string, root: TreeViewNode) {
|
||||
console.log("Creating tree view state", file_id, root);
|
||||
this.file_id = file_id;
|
||||
this.root = root;
|
||||
this.loadTreeView();
|
||||
}
|
||||
|
||||
public loadTreeView() {
|
||||
console.log("Loading tree view", this.root);
|
||||
invoke<Primitive>("get_prim_tree_by_path", {
|
||||
id: this.file_id,
|
||||
path: this.root,
|
||||
})
|
||||
.then((result) => {
|
||||
this.view = new Primitive(result);
|
||||
})
|
||||
.catch((err) => console.error(err));
|
||||
}
|
||||
|
||||
public expandTree(path: string[]) {
|
||||
if (path.length == 0) {
|
||||
console.error("Empty path");
|
||||
return;
|
||||
}
|
||||
let node = this.root;
|
||||
for (let key of path.slice(1, path.length)) {
|
||||
let _node: TreeViewNode | undefined = node.getChild(key);
|
||||
if (_node) {
|
||||
node = _node;
|
||||
} else {
|
||||
node = node.addChild(key);
|
||||
}
|
||||
}
|
||||
this.loadTreeView();
|
||||
}
|
||||
|
||||
public collapseTree(path: string[]) {
|
||||
if (path.length == 0) {
|
||||
console.error("Empty path");
|
||||
return;
|
||||
}
|
||||
if (path.length == 1) {
|
||||
this.root.clearChildren();
|
||||
return;
|
||||
}
|
||||
let node: TreeViewNode | undefined = this.root;
|
||||
for (let key of path.slice(1, path.length - 1)) {
|
||||
node = node?.getChild(key);
|
||||
}
|
||||
if (node) {
|
||||
node.removeChild(path[path.length - 1]);
|
||||
}
|
||||
this.loadTreeView();
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user