From f828eedf3e395a2faf9641d32fc40901d43662df Mon Sep 17 00:00:00 2001 From: Allan Lasser Date: Thu, 26 Sep 2024 16:55:48 -0400 Subject: [PATCH 01/72] Rename RedactionPane -> RedactionLayer --- src/lib/components/forms/ConfirmRedaction.svelte | 2 +- src/lib/components/viewer/PDFPage.svelte | 4 ++-- .../{RedactionPane.svelte => RedactionLayer.svelte} | 0 src/lib/components/viewer/stories/PDF.stories.svelte | 2 +- ...nPane.stories.svelte => RedactionLayer.stories.svelte} | 8 ++++---- .../components/viewer/toolbars/RedactionToolbar.svelte | 3 +-- 6 files changed, 9 insertions(+), 10 deletions(-) rename src/lib/components/viewer/{RedactionPane.svelte => RedactionLayer.svelte} (100%) rename src/lib/components/viewer/stories/{RedactionPane.stories.svelte => RedactionLayer.stories.svelte} (79%) diff --git a/src/lib/components/forms/ConfirmRedaction.svelte b/src/lib/components/forms/ConfirmRedaction.svelte index 9101914eb..d0b7734ef 100644 --- a/src/lib/components/forms/ConfirmRedaction.svelte +++ b/src/lib/components/forms/ConfirmRedaction.svelte @@ -20,7 +20,7 @@ This almost certainly lives in a modal. import { pending, redactions, - } from "$lib/components/viewer/RedactionPane.svelte"; + } from "@/lib/components/viewer/RedactionLayer.svelte"; import { canonicalUrl } from "$lib/api/documents"; export let document: Document; diff --git a/src/lib/components/viewer/PDFPage.svelte b/src/lib/components/viewer/PDFPage.svelte index 58b7525e4..8789381d0 100644 --- a/src/lib/components/viewer/PDFPage.svelte +++ b/src/lib/components/viewer/PDFPage.svelte @@ -32,7 +32,7 @@ Selectable text can be rendered in one of two ways: import Note from "./Note.svelte"; import NotesPane from "./NotesPane.svelte"; import Page from "./Page.svelte"; - import RedactionPane, { pending, redactions } from "./RedactionPane.svelte"; + import RedactionLayer, { pending, redactions } from "./RedactionLayer.svelte"; // writable ui import Action from "../common/Action.svelte"; @@ -395,7 +395,7 @@ Selectable text can be rendered in one of two ways: {/if} {#if redactions_for_page.length > 0 || $mode === "redacting"} - diff --git a/src/lib/components/viewer/RedactionPane.svelte b/src/lib/components/viewer/RedactionLayer.svelte similarity index 100% rename from src/lib/components/viewer/RedactionPane.svelte rename to src/lib/components/viewer/RedactionLayer.svelte diff --git a/src/lib/components/viewer/stories/PDF.stories.svelte b/src/lib/components/viewer/stories/PDF.stories.svelte index 6d8ad0735..5f3316260 100644 --- a/src/lib/components/viewer/stories/PDF.stories.svelte +++ b/src/lib/components/viewer/stories/PDF.stories.svelte @@ -5,7 +5,7 @@ import { Story } from "@storybook/addon-svelte-csf"; import PDF from "../PDF.svelte"; - import { redactions } from "../RedactionPane.svelte"; + import { redactions } from "../RedactionLayer.svelte"; import { IMAGE_WIDTHS_MAP } from "@/config/config.js"; import { pdfUrl } from "$lib/api/documents"; diff --git a/src/lib/components/viewer/stories/RedactionPane.stories.svelte b/src/lib/components/viewer/stories/RedactionLayer.stories.svelte similarity index 79% rename from src/lib/components/viewer/stories/RedactionPane.stories.svelte rename to src/lib/components/viewer/stories/RedactionLayer.stories.svelte index 17e503eed..c7a91690e 100644 --- a/src/lib/components/viewer/stories/RedactionPane.stories.svelte +++ b/src/lib/components/viewer/stories/RedactionLayer.stories.svelte @@ -1,13 +1,13 @@ @@ -28,7 +28,7 @@ {#each redacted as page}
- +
{/each}
diff --git a/src/lib/components/viewer/toolbars/RedactionToolbar.svelte b/src/lib/components/viewer/toolbars/RedactionToolbar.svelte index 03d7e2abc..75864d240 100644 --- a/src/lib/components/viewer/toolbars/RedactionToolbar.svelte +++ b/src/lib/components/viewer/toolbars/RedactionToolbar.svelte @@ -14,9 +14,8 @@ import Button from "$lib/components/common/Button.svelte"; import Flex from "$lib/components/common/Flex.svelte"; - import { redactions, undo, clear } from "../RedactionPane.svelte"; import Tooltip from "$lib/components/common/Tooltip.svelte"; - + import { redactions, undo, clear } from "../RedactionLayer.svelte"; import ConfirmRedaction from "$lib/components/forms/ConfirmRedaction.svelte"; import Portal from "$lib/components/layouts/Portal.svelte"; import Modal from "$lib/components/layouts/Modal.svelte"; From 11ea0c4065e64f038d4a6eacf8723a7c6a18dac3 Mon Sep 17 00:00:00 2001 From: Allan Lasser Date: Thu, 26 Sep 2024 16:57:14 -0400 Subject: [PATCH 02/72] Rename AnnotationPane -> AnnotationLayer --- .../{AnnotationPane.svelte => AnnotationLayer.svelte} | 2 +- src/lib/components/viewer/PDFPage.svelte | 4 ++-- ...Pane.stories.svelte => AnnotationLayer.stories.svelte} | 8 ++++---- 3 files changed, 7 insertions(+), 7 deletions(-) rename src/lib/components/viewer/{AnnotationPane.svelte => AnnotationLayer.svelte} (99%) rename src/lib/components/viewer/stories/{AnnotationPane.stories.svelte => AnnotationLayer.stories.svelte} (90%) diff --git a/src/lib/components/viewer/AnnotationPane.svelte b/src/lib/components/viewer/AnnotationLayer.svelte similarity index 99% rename from src/lib/components/viewer/AnnotationPane.svelte rename to src/lib/components/viewer/AnnotationLayer.svelte index 0cfddc356..694faecd5 100644 --- a/src/lib/components/viewer/AnnotationPane.svelte +++ b/src/lib/components/viewer/AnnotationLayer.svelte @@ -1,5 +1,5 @@ +
- {#await text} - - {#each Array(total).fill(null) as p, n} - -
-
- {/each} - {:then { pages }} - {#each pages as { page, contents }} - - {/each} - {/await} + {#each text.pages as { page, contents }} + +
+        {@html highlight(contents, query)}
+      
+
+ {/each}
diff --git a/src/lib/components/viewer/TextPage.svelte b/src/lib/components/viewer/TextPage.svelte deleted file mode 100644 index 4434c89c2..000000000 --- a/src/lib/components/viewer/TextPage.svelte +++ /dev/null @@ -1,42 +0,0 @@ - - - -
-    {@html highlight(contents, query)}
-  
-
- - diff --git a/src/lib/components/viewer/Viewer.svelte b/src/lib/components/viewer/Viewer.svelte index 84eaa3bf2..442a37b17 100644 --- a/src/lib/components/viewer/Viewer.svelte +++ b/src/lib/components/viewer/Viewer.svelte @@ -81,14 +81,7 @@ {#if showPDF} {:else if mode === "text"} - + {:else if mode === "grid"} {:else if mode === "notes"} From 574ff082b6a622187e6a69ca58bd1ee0e9e7c548 Mon Sep 17 00:00:00 2001 From: Allan Lasser Date: Thu, 26 Sep 2024 17:25:10 -0400 Subject: [PATCH 05/72] Add pageSizes to viewer utils --- src/lib/components/viewer/PDF.svelte | 7 +++--- src/lib/utils/viewer.ts | 37 ++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 4 deletions(-) diff --git a/src/lib/components/viewer/PDF.svelte b/src/lib/components/viewer/PDF.svelte index ed286fede..773d20207 100644 --- a/src/lib/components/viewer/PDF.svelte +++ b/src/lib/components/viewer/PDF.svelte @@ -22,15 +22,14 @@ import PdfPage from "./PDFPage.svelte"; - import { pageSizes } from "@/api/pageSize.js"; import { scrollToPage } from "$lib/utils/scroll"; - import { remToPx } from "@/lib/utils/layout"; - import { isEmbedded } from "@/lib/utils/viewer"; + import { remToPx } from "$lib/utils/layout"; + import { isEmbedded, pageSizes } from "$lib/utils/viewer"; export let asset_url: URL = null; export let document: Document; - export let query: string = ""; // search query export let scale: number | "width" | "height" = 1; + export let query: string = ""; // search query export let embed = isEmbedded(); // https://mozilla.github.io/pdf.js/api/draft/module-pdfjsLib-PDFDocumentProxy.html diff --git a/src/lib/utils/viewer.ts b/src/lib/utils/viewer.ts index 346cbc431..c5da3b942 100644 --- a/src/lib/utils/viewer.ts +++ b/src/lib/utils/viewer.ts @@ -1,4 +1,5 @@ import { getContext } from "svelte"; +import type { Writable } from "svelte/store"; import type { Document, Note, ViewerMode } from "$lib/api/types"; import { canonicalUrl, pageHashUrl } from "../api/documents"; import { noteHashUrl } from "../api/notes"; @@ -16,6 +17,10 @@ export function isEmbedded(): boolean { return getContext("embed") ?? false; } +export function getCurrentPage(): Writable { + return getContext("currentPage"); +} + export function getViewerHref(options: ViewerHrefOptions = {}) { const { document, page, note, mode = "document", embed = false } = options; let hash = ""; @@ -36,3 +41,35 @@ export function getViewerHref(options: ViewerHrefOptions = {}) { return href; } } + +/** + * Parse page_spec into width and height of each page + * + * @param pageSpec A string encoding page dimensions in a compact format + * @returns an array of [width, height] tuples + */ +export function pageSizes(pageSpec: string): [width: number, height: number][] { + // Handle empty page spec + if (pageSpec.trim().length == 0) return []; + + const parts = pageSpec.split(";"); + + return parts.reduce((sizes, part, i) => { + const [size, range] = part.split(":"); + const [width, height] = size.split("x").map(parseFloat); + + range.split(",").forEach((rangePart) => { + if (rangePart.includes("-")) { + const [start, end] = rangePart.split("-").map((x) => parseInt(x, 10)); + for (let page = start; page <= end; page++) { + sizes[page] = [width, height]; + } + } else { + const page = parseInt(rangePart, 10); + sizes[page] = [width, height]; + } + }); + + return sizes; + }, Array(parts.length)); +} From fbbed4038861303179e89e8d44ceaac004106912 Mon Sep 17 00:00:00 2001 From: Allan Lasser Date: Thu, 26 Sep 2024 17:30:46 -0400 Subject: [PATCH 06/72] Consolidate NotesPane into AnnotationsLayer - Consolidate NoteLink into AnnotationsLayer - AnnotationLayer fixes - Fix styling of annotations - Fix viewer "Edit Access" story - Fix active note highlight styling --- .../components/viewer/AnnotationLayer.svelte | 106 +++++++++++++---- src/lib/components/viewer/Note.svelte | 10 +- src/lib/components/viewer/NoteLink.svelte | 60 ---------- src/lib/components/viewer/NotesPane.svelte | 110 ------------------ src/lib/components/viewer/PDFPage.svelte | 17 +-- .../stories/AnnotationLayer.stories.svelte | 58 +++++---- .../viewer/stories/NotesPane.stories.svelte | 76 ------------ .../viewer/stories/TextPage.stories.svelte | 24 ---- .../viewer/stories/Viewer.stories.svelte | 7 +- src/lib/utils/viewer.ts | 8 ++ 10 files changed, 141 insertions(+), 335 deletions(-) delete mode 100644 src/lib/components/viewer/NoteLink.svelte delete mode 100644 src/lib/components/viewer/NotesPane.svelte delete mode 100644 src/lib/components/viewer/stories/NotesPane.stories.svelte delete mode 100644 src/lib/components/viewer/stories/TextPage.stories.svelte diff --git a/src/lib/components/viewer/AnnotationLayer.svelte b/src/lib/components/viewer/AnnotationLayer.svelte index 694faecd5..fad1fc3c1 100644 --- a/src/lib/components/viewer/AnnotationLayer.svelte +++ b/src/lib/components/viewer/AnnotationLayer.svelte @@ -8,49 +8,59 @@ and instances of the EditNote form. Only one note can be added/edited at a time. --> - - - {note.title} - - - diff --git a/src/lib/components/viewer/NotesPane.svelte b/src/lib/components/viewer/NotesPane.svelte deleted file mode 100644 index d6f5255cc..000000000 --- a/src/lib/components/viewer/NotesPane.svelte +++ /dev/null @@ -1,110 +0,0 @@ - - - -
- {#each notes as note} - {@const is_active = note.id === $activeNote?.id} - openNote(e, note)} - > - - {#if is_active} - - {/if} - - {#if is_active} - - {:else} - - {/if} - {/each} -
- - diff --git a/src/lib/components/viewer/PDFPage.svelte b/src/lib/components/viewer/PDFPage.svelte index 766e678e2..e0cb3bc4e 100644 --- a/src/lib/components/viewer/PDFPage.svelte +++ b/src/lib/components/viewer/PDFPage.svelte @@ -29,7 +29,6 @@ Selectable text can be rendered in one of two ways: // page parts import Note from "./Note.svelte"; - import NotesPane from "./NotesPane.svelte"; import Page from "./Page.svelte"; import AnnotationLayer from "./AnnotationLayer.svelte"; import RedactionLayer, { pending, redactions } from "./RedactionLayer.svelte"; @@ -377,22 +376,16 @@ Selectable text can be rendered in one of two ways: {/if} - {#if $mode === "annotating"} + {#await pdf then pdf} - {:else} - {#await pdf then pdf} - - {/await} - {/if} + {/await} {#if redactions_for_page.length > 0 || $mode === "redacting"} import type { Document, Note, ViewerMode } from "$lib/api/types"; + import ViewerContextDecorator from "@/../.storybook/decorators/ViewerContextDecorator.svelte"; import { Story } from "@storybook/addon-svelte-csf"; import AnnotationLayer from "../AnnotationLayer.svelte"; import Page from "../Page.svelte"; @@ -26,31 +27,40 @@ }, {}); - + + + + {#each sizes as [width, height], page_number} + +
+ +
+
+ {/each} +
+
+
- - - {#each sizes as [width, height], page_number} - -
- -
-
- {/each} -
+ + + + {#each sizes as [width, height], page_number} + +
+ +
+
+ {/each} +
+
diff --git a/src/lib/components/viewer/stories/TextPage.stories.svelte b/src/lib/components/viewer/stories/TextPage.stories.svelte deleted file mode 100644 index 1aae7d49a..000000000 --- a/src/lib/components/viewer/stories/TextPage.stories.svelte +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - diff --git a/src/lib/components/viewer/stories/Viewer.stories.svelte b/src/lib/components/viewer/stories/Viewer.stories.svelte index 82b8f613a..958d2c648 100644 --- a/src/lib/components/viewer/stories/Viewer.stories.svelte +++ b/src/lib/components/viewer/stories/Viewer.stories.svelte @@ -7,6 +7,7 @@ import doc from "@/test/fixtures/documents/document-expanded.json"; import txt from "@/test/fixtures/documents/document.txt.json"; + import Note from "../Note.svelte"; const document = doc as Document; @@ -44,7 +45,11 @@ name="Edit Access" args={{ ...args, - document: { ...document, edit_access: true }, + document: { + ...document, + edit_access: true, + notes: document.notes.map((note) => ({ ...note, edit_access: true })), + }, }} /> { return getContext("currentPage"); } +export function getCurrentMode(): Writable { + return getContext("currentMode"); +} + +export function getActiveNote(): Writable { + return getContext("activeNote"); +} + export function getViewerHref(options: ViewerHrefOptions = {}) { const { document, page, note, mode = "document", embed = false } = options; let hash = ""; From acdf4809fbf7ff2dfaa6e83e885f7550a3984363 Mon Sep 17 00:00:00 2001 From: Allan Lasser Date: Thu, 26 Sep 2024 20:02:54 -0400 Subject: [PATCH 07/72] Refactors PageActions out of PDFPage --- src/lib/components/viewer/PDFPage.svelte | 130 ----------------- src/lib/components/viewer/PageActions.svelte | 139 +++++++++++++++++++ 2 files changed, 139 insertions(+), 130 deletions(-) create mode 100644 src/lib/components/viewer/PageActions.svelte diff --git a/src/lib/components/viewer/PDFPage.svelte b/src/lib/components/viewer/PDFPage.svelte index e0cb3bc4e..57cb321ba 100644 --- a/src/lib/components/viewer/PDFPage.svelte +++ b/src/lib/components/viewer/PDFPage.svelte @@ -20,12 +20,6 @@ Selectable text can be rendered in one of two ways: import * as pdfjs from "pdfjs-dist/legacy/build/pdf.mjs"; import { getContext } from "svelte"; import { _ } from "svelte-i18n"; - import { - Share16, - Comment16, - ListOrdered16, - KebabHorizontal16, - } from "svelte-octicons"; // page parts import Note from "./Note.svelte"; @@ -34,21 +28,9 @@ Selectable text can be rendered in one of two ways: import RedactionLayer, { pending, redactions } from "./RedactionLayer.svelte"; // writable ui - import Action from "../common/Action.svelte"; - import Button from "../common/Button.svelte"; - import Dropdown from "@/lib/components/common/Dropdown.svelte"; - import EditNote from "../forms/EditNote.svelte"; - import EditSections from "../forms/EditSections.svelte"; - import Flex from "../common/Flex.svelte"; - import Menu from "$lib/components/common/Menu.svelte"; - import MenuItem from "$lib/components/common/MenuItem.svelte"; - import Modal from "../layouts/Modal.svelte"; - import Share from "../documents/Share.svelte"; - import Portal from "../layouts/Portal.svelte"; import { highlight } from "$lib/utils/search"; import { isPageLevel } from "$lib/api/notes"; - import { remToPx } from "@/lib/utils/layout"; import { isEmbedded } from "@/lib/utils/viewer"; export let document: Document; @@ -68,9 +50,6 @@ Selectable text can be rendered in one of two ways: // make hidden things visible, for debugging export let debug = false; - // share page - let pageShareOpen = false; - const mode: Writable = getContext("currentMode"); let canvas: HTMLCanvasElement; @@ -238,7 +217,6 @@ Selectable text can be rendered in one of two ways: } let pageWidth: number; - $: id = `page_${page_number}`; @@ -266,78 +244,6 @@ Selectable text can be rendered in one of two ways: {/if} -
- {#if !embed} - {#if pageWidth > remToPx(32) || !document.edit_access} - - (pageShareOpen = true)}> - {$_("dialog.share")} - - {#if document.edit_access} -
- (pageNote = true)}> - {$_("annotate.cta.add-note")} - - - (editSection = true)} - > - {#if section} - {$_("annotate.cta.edit-section")} - {:else} - {$_("annotate.cta.add-section")} - {/if} - -
- {/if} -
- {:else} - - - - { - close(); - pageShareOpen = true; - }} - > - - {$_("dialog.share")} - - {#if document.edit_access} - { - close(); - pageNote = true; - }} - > - - {$_("annotate.cta.add-note")} - - - { - close(); - editSection = true; - }} - > - - {#if section} - {$_("annotate.cta.edit-section")} - {:else} - {$_("annotate.cta.add-section")} - {/if} - - {/if} - - - {/if} - {/if} -
- {#if page_level_notes.length}
{#each page_level_notes as note} @@ -395,42 +301,6 @@ Selectable text can be rendered in one of two ways: {/if}
-{#if pageShareOpen} - - (pageShareOpen = false)}> -

{$_("dialog.share")}

- -
-
-{/if} -{#if editSection} - - -

- {#if section} - {$_("annotate.cta.edit-section")} - {:else} - {$_("annotate.cta.add-section")} - {/if} -

- -
-
-{/if} -{#if pageNote} - - -

- {$_("annotate.cta.add-note")} -

- -
-
-{/if} diff --git a/src/lib/components/viewer/PDFPage.svelte b/src/lib/components/viewer/PDFPage.svelte index 4be1585df..719e3e4a0 100644 --- a/src/lib/components/viewer/PDFPage.svelte +++ b/src/lib/components/viewer/PDFPage.svelte @@ -44,7 +44,6 @@ Selectable text can be rendered in one of two ways: export let query: string = ""; // search query export let notes: NoteType[] = []; - export let section: Section = undefined; // one at most export let text: TextPosition[] = []; const embed = isEmbedded(); @@ -207,14 +206,6 @@ Selectable text can be rendered in one of two ways: }} bind:width={pageWidth} > - - {#if section} -

- {section.title} -

- {/if} -
- {#if page_level_notes.length}
{#each page_level_notes as note} @@ -274,15 +265,6 @@ Selectable text can be rendered in one of two ways: diff --git a/src/lib/components/viewer/PageActions.svelte b/src/lib/components/viewer/PageActions.svelte index aeea34aa0..f937d8196 100644 --- a/src/lib/components/viewer/PageActions.svelte +++ b/src/lib/components/viewer/PageActions.svelte @@ -7,7 +7,7 @@ KebabHorizontal16, } from "svelte-octicons"; - import type { Document, Section } from "$lib/api/types"; + import type { Document } from "$lib/api/types"; import Action from "$lib/components/common/Action.svelte"; import Button from "$lib/components/common/Button.svelte"; import EditNote from "$lib/components/forms/EditNote.svelte"; @@ -20,11 +20,11 @@ import Menu from "$lib/components/common/Menu.svelte"; import MenuItem from "$lib/components/common/MenuItem.svelte"; import { remToPx } from "$lib/utils/layout"; + import { getSections } from "$lib/utils/viewer"; export let document: Document; export let page_number: number; export let pageWidth: number; - export let section: Section = undefined; // one at most export let canEdit: boolean = false; @@ -33,9 +33,10 @@ let editSection = false; $: id = `page_${page_number}`; + $: section = getSections(document)[page_number]; -
+
{#if pageWidth > remToPx(32) || !canEdit} (pageShareOpen = true)}> @@ -137,3 +138,9 @@ {/if} + + diff --git a/src/lib/components/viewer/PaginationToolbar.svelte b/src/lib/components/viewer/PaginationToolbar.svelte index fa2cb2d16..408d82a97 100644 --- a/src/lib/components/viewer/PaginationToolbar.svelte +++ b/src/lib/components/viewer/PaginationToolbar.svelte @@ -1,10 +1,10 @@ - - - - + - - - + diff --git a/src/lib/components/viewer/stories/Note.stories.svelte b/src/lib/components/viewer/stories/Note.stories.svelte index ddf39efd3..01589e587 100644 --- a/src/lib/components/viewer/stories/Note.stories.svelte +++ b/src/lib/components/viewer/stories/Note.stories.svelte @@ -7,8 +7,8 @@ import.meta.url, ).href; - import { Story } from "@storybook/addon-svelte-csf"; - import { setContext } from "svelte"; + import { Story, Template } from "@storybook/addon-svelte-csf"; + import ViewerContext from "../ViewerContext.svelte"; import Note from "../Note.svelte"; import doc from "@/test/fixtures/documents/document-expanded.json"; @@ -36,35 +36,27 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - {#await load(url) then pdf} - - {/await} - + + + + + + + + + diff --git a/src/lib/components/viewer/stories/Notes.stories.svelte b/src/lib/components/viewer/stories/Notes.stories.svelte index 2c5fdbbd6..e78a18648 100644 --- a/src/lib/components/viewer/stories/Notes.stories.svelte +++ b/src/lib/components/viewer/stories/Notes.stories.svelte @@ -1,9 +1,9 @@ - - - - + + + - - - + - - - + diff --git a/src/lib/components/viewer/stories/PDF.stories.svelte b/src/lib/components/viewer/stories/PDF.stories.svelte index 3bd48eca3..aee2b10ee 100644 --- a/src/lib/components/viewer/stories/PDF.stories.svelte +++ b/src/lib/components/viewer/stories/PDF.stories.svelte @@ -26,25 +26,27 @@ const loadingUrl = createApiUrl("loading/"); - const section: Section = { id: 1, page_number: 1, title: "Something uneasy" }; - const long_section: Section = { - id: 1, - page_number: 1, - title: - "What it means is that tonight a Santa Ana will begin to blow, a hot wind from the northeast whining down through the Cajon and SanGorgonio Passes, blowing up sand storms out along Route 66, drying the hills andthe nerves to flash point.", - }; + const sections: Section[] = [ + { id: 1, page_number: 1, title: "Something uneasy" }, + { + id: 1, + page_number: 1, + title: + "What it means is that tonight a Santa Ana will begin to blow, a hot wind from the northeast whining down through the Cajon and SanGorgonio Passes, blowing up sand storms out along Route 66, drying the hills andthe nerves to flash point.", + }, + ]; let args = { context: { - document, - mode: "document", - asset_url: pdfUrl(document), - }, - props: { document: { ...document, notes: [], }, + mode: "document", + asset_url: pdfUrl(document), + }, + props: { + scale: "width", }, }; @@ -61,23 +63,23 @@ - - - - - + - - - - - + + + + res( + ctx.status(400, "Ambiguous Error"), + ctx.json("Something went horribly wrong."), + ), + ), + ], + }, + }} + args={{ + ...args, + context: { ...args.context, asset_url: new URL(loadingUrl) }, + }} /> - -
- - -
-
-
- - - +
- +
- - -
- - -
-
-
+ diff --git a/src/lib/components/viewer/stories/PDFPage.stories.svelte b/src/lib/components/viewer/stories/PDFPage.stories.svelte index 891b5f461..3aec5ae6c 100644 --- a/src/lib/components/viewer/stories/PDFPage.stories.svelte +++ b/src/lib/components/viewer/stories/PDFPage.stories.svelte @@ -36,20 +36,19 @@ - + - + - + - + diff --git a/src/lib/utils/viewer.ts b/src/lib/utils/viewer.ts index 49d4440c2..98ebc0bdf 100644 --- a/src/lib/utils/viewer.ts +++ b/src/lib/utils/viewer.ts @@ -1,4 +1,4 @@ -import type { Document, Note, ViewerMode } from "$lib/api/types"; +import type { Document, Note, Section, ViewerMode } from "$lib/api/types"; import { canonicalUrl, pageHashUrl } from "../api/documents"; import { noteHashUrl } from "../api/notes"; @@ -83,3 +83,30 @@ export function pageSizes(pageSpec: string): [width: number, height: number][] { return sizes; }, Array(parts.length)); } + +/** + * Index notes and sections by page + */ +export function getNotes(document: Document): Record { + return ( + document.notes?.reduce>((m, note) => { + if (!m[note.page_number]) { + m[note.page_number] = []; + } + m[note.page_number].push(note); + return m; + }, {}) ?? {} + ); +} + +/** + * Get the sections from a document + */ +export function getSections(document: Document): Record { + return ( + document.sections?.reduce((m, section) => { + m[section.page_number] = section; + return m; + }, {}) ?? {} + ); +} diff --git a/src/routes/(app)/documents/[id]-[slug]/+page.svelte b/src/routes/(app)/documents/[id]-[slug]/+page.svelte index a2f500ff9..53505abf4 100644 --- a/src/routes/(app)/documents/[id]-[slug]/+page.svelte +++ b/src/routes/(app)/documents/[id]-[slug]/+page.svelte @@ -1,11 +1,13 @@ - + + + {#if shouldPreload($currentMode)} + + {/if} + + diff --git a/src/routes/(app)/documents/[id]-[slug]/+page.svelte b/src/routes/(app)/documents/[id]-[slug]/+page.svelte index 53505abf4..1527d2efe 100644 --- a/src/routes/(app)/documents/[id]-[slug]/+page.svelte +++ b/src/routes/(app)/documents/[id]-[slug]/+page.svelte @@ -1,84 +1,18 @@ - - - - - {#if shouldPreload($currentMode)} - - {/if} - - diff --git a/src/routes/embed/documents/[id]-[slug]/+page.svelte b/src/routes/embed/documents/[id]-[slug]/+page.svelte index 7d04e750a..2372c5c04 100644 --- a/src/routes/embed/documents/[id]-[slug]/+page.svelte +++ b/src/routes/embed/documents/[id]-[slug]/+page.svelte @@ -1,85 +1,22 @@ - - - - - - {#if shouldPreload($currentMode)} - - {/if} - - Date: Mon, 14 Oct 2024 09:42:30 -0400 Subject: [PATCH 24/72] Consolidate scrollToHash behavior Clean up ViewerContext.scrollToHash logic --- .../components/viewer/ViewerContext.svelte | 50 +++++++------------ 1 file changed, 18 insertions(+), 32 deletions(-) diff --git a/src/lib/components/viewer/ViewerContext.svelte b/src/lib/components/viewer/ViewerContext.svelte index ba1e80fe7..5a428257f 100644 --- a/src/lib/components/viewer/ViewerContext.svelte +++ b/src/lib/components/viewer/ViewerContext.svelte @@ -79,8 +79,7 @@ layouts, stories, and tests. From a61d1612d698ab447d501604d580f8b3c7d9cf90 Mon Sep 17 00:00:00 2001 From: Allan Lasser Date: Mon, 14 Oct 2024 10:06:01 -0400 Subject: [PATCH 25/72] Update pageFromHash to return null fallback --- src/lib/api/documents.ts | 7 ++++--- src/lib/api/tests/documents.test.ts | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/lib/api/documents.ts b/src/lib/api/documents.ts index c45c77597..486566b34 100644 --- a/src/lib/api/documents.ts +++ b/src/lib/api/documents.ts @@ -20,6 +20,7 @@ import type { WriteMode, ViewerMode, ValidationError, + Nullable, } from "./types"; import { writable, type Writable } from "svelte/store"; @@ -550,13 +551,13 @@ export function pageHashUrl(page: number): string { * * @param hash URL hash */ -export function pageFromHash(hash: string): number { +export function pageFromHash(hash: string): Nullable { const re = /^#document\/p(\d+)/; // match pages and notes const match = re.exec(hash); - if (!match) return 1; + if (!match) return null; - return +match[1] || 1; + return +match[1] || null; } /** diff --git a/src/lib/api/tests/documents.test.ts b/src/lib/api/tests/documents.test.ts index 48f3e8cc5..6f7d937cf 100644 --- a/src/lib/api/tests/documents.test.ts +++ b/src/lib/api/tests/documents.test.ts @@ -714,7 +714,7 @@ describe("document helper methods", () => { expect(documents.pageFromHash(hash)).toStrictEqual(10); // invalid hash returns page 1 - expect(documents.pageFromHash("#nopage")).toStrictEqual(1); + expect(documents.pageFromHash("#nopage")).toStrictEqual(null); // match a note hash expect(documents.pageFromHash("#document/p2/a2000002")).toStrictEqual(2); From e0f341d3ad1345433f4aaf9b651318d1b35bd393 Mon Sep 17 00:00:00 2001 From: Allan Lasser Date: Mon, 14 Oct 2024 10:09:02 -0400 Subject: [PATCH 26/72] Removes `activeNote` from ViewerContext --- .../components/viewer/AnnotationLayer.svelte | 40 ++++++++++--------- src/lib/components/viewer/README.md | 1 - .../components/viewer/ViewerContext.svelte | 5 --- 3 files changed, 22 insertions(+), 24 deletions(-) diff --git a/src/lib/components/viewer/AnnotationLayer.svelte b/src/lib/components/viewer/AnnotationLayer.svelte index 1b54d0067..90d70efa2 100644 --- a/src/lib/components/viewer/AnnotationLayer.svelte +++ b/src/lib/components/viewer/AnnotationLayer.svelte @@ -10,7 +10,7 @@ Only one note can be added/edited at a time. Assumes it's a child of a ViewerContext -->
Date: Mon, 14 Oct 2024 10:25:43 -0400 Subject: [PATCH 28/72] Attempted fix for mode switching --- src/lib/components/viewer/ViewerContext.svelte | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/lib/components/viewer/ViewerContext.svelte b/src/lib/components/viewer/ViewerContext.svelte index 14fd249be..fe4a2a276 100644 --- a/src/lib/components/viewer/ViewerContext.svelte +++ b/src/lib/components/viewer/ViewerContext.svelte @@ -139,8 +139,10 @@ layouts, stories, and tests. }); afterNavigate(() => { + console.log(mode); + $currentMode = mode; const { hash } = $pageStore.url; - if (shouldPaginate($currentMode)) { + if (shouldPaginate(mode)) { scrollToHash(hash); } }); From bd99c07db07269579c43ea0700520448e6782d22 Mon Sep 17 00:00:00 2001 From: Allan Lasser Date: Mon, 14 Oct 2024 10:27:29 -0400 Subject: [PATCH 29/72] Grid pages link to document mode --- src/lib/components/viewer/Grid.svelte | 4 ++-- src/lib/components/viewer/Page.svelte | 8 +++++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/lib/components/viewer/Grid.svelte b/src/lib/components/viewer/Grid.svelte index 5fb2dd6f4..5560e03aa 100644 --- a/src/lib/components/viewer/Grid.svelte +++ b/src/lib/components/viewer/Grid.svelte @@ -37,8 +37,8 @@ {#each sizes as aspect, n} {@const page_number = n + 1} {@const height = width * aspect} - - + + Page {page_number}, {document.title} - +
From 53702baccad8a0f7dd52bc378cb371abc375b1d3 Mon Sep 17 00:00:00 2001 From: Allan Lasser Date: Mon, 14 Oct 2024 10:57:20 -0400 Subject: [PATCH 35/72] Add scroll margin top to notes --- src/lib/components/viewer/Note.svelte | 2 ++ src/lib/components/viewer/PDF.svelte | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/lib/components/viewer/Note.svelte b/src/lib/components/viewer/Note.svelte index 678feae61..919ce3b23 100644 --- a/src/lib/components/viewer/Note.svelte +++ b/src/lib/components/viewer/Note.svelte @@ -251,6 +251,8 @@ border: 1px solid var(--gray-2, #d8dee2); background: var(--white, #fff); + scroll-margin-top: 6rem; + /* shadow-2 */ box-shadow: 0px 2px 8px 2px var(--shadow-1, rgba(30, 48, 56, 0.15)); } diff --git a/src/lib/components/viewer/PDF.svelte b/src/lib/components/viewer/PDF.svelte index 95936e75b..f53b23b41 100644 --- a/src/lib/components/viewer/PDF.svelte +++ b/src/lib/components/viewer/PDF.svelte @@ -57,7 +57,7 @@ {#each sizes as [width, height], n} {@const page_number = n + 1} {#if sections[n]} -

+

{sections[n].title}

{/if} From e34f65a584477fa373b461b413ec11291645999b Mon Sep 17 00:00:00 2001 From: Allan Lasser Date: Mon, 14 Oct 2024 11:03:23 -0400 Subject: [PATCH 36/72] Fix reading toolbar icons --- src/lib/components/viewer/ReadingToolbar.svelte | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib/components/viewer/ReadingToolbar.svelte b/src/lib/components/viewer/ReadingToolbar.svelte index eb45b7d06..f2e31a310 100644 --- a/src/lib/components/viewer/ReadingToolbar.svelte +++ b/src/lib/components/viewer/ReadingToolbar.svelte @@ -152,5 +152,6 @@ Assumes it's a child of a ViewerContext } .icon { flex: 0 0 auto; + display: inline-flex; } From cef2f626c8ebc4eb3b578e4a416e2f3099f0b092 Mon Sep 17 00:00:00 2001 From: Allan Lasser Date: Mon, 14 Oct 2024 11:07:07 -0400 Subject: [PATCH 37/72] Fix broken storybooks --- src/lib/components/viewer/Text.svelte | 5 ++--- src/lib/components/viewer/stories/Note.stories.svelte | 3 ++- src/lib/components/viewer/stories/Viewer.stories.svelte | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/lib/components/viewer/Text.svelte b/src/lib/components/viewer/Text.svelte index d12a7093a..492afb603 100644 --- a/src/lib/components/viewer/Text.svelte +++ b/src/lib/components/viewer/Text.svelte @@ -6,7 +6,6 @@ -->
- {#each text.pages as { page, contents }} + {#each text?.pages as { page, contents }}
         {@html highlight(contents, query)}
diff --git a/src/lib/components/viewer/stories/Note.stories.svelte b/src/lib/components/viewer/stories/Note.stories.svelte
index 01589e587..c042ac4e9 100644
--- a/src/lib/components/viewer/stories/Note.stories.svelte
+++ b/src/lib/components/viewer/stories/Note.stories.svelte
@@ -13,6 +13,7 @@
 
   import doc from "@/test/fixtures/documents/document-expanded.json";
   import pdfFile from "@/test/fixtures/documents/examples/agreement-between-conservatives-and-liberal-democrats-to-form-a-coalition-government.pdf";
+  import { writable } from "svelte/store";
 
   const document = doc as Document;
   const notes = document.notes as NoteType[];
@@ -58,5 +59,5 @@
 
 
diff --git a/src/lib/components/viewer/stories/Viewer.stories.svelte b/src/lib/components/viewer/stories/Viewer.stories.svelte
index 82ccd6457..3d66f8116 100644
--- a/src/lib/components/viewer/stories/Viewer.stories.svelte
+++ b/src/lib/components/viewer/stories/Viewer.stories.svelte
@@ -35,8 +35,8 @@
 
 

From 17d79dd4760e70bc9d7786331f0f032eb6bc398a Mon Sep 17 00:00:00 2001
From: Allan Lasser 
Date: Mon, 14 Oct 2024 11:42:30 -0400
Subject: [PATCH 38/72] Refactor zoom state and fns into Viewer context and
 utils

---
 src/lib/components/viewer/Grid.svelte         |   8 +-
 src/lib/components/viewer/PDF.svelte          |  17 +-
 .../viewer/PaginationToolbar.svelte           |   2 +-
 src/lib/components/viewer/Text.svelte         |  12 +-
 src/lib/components/viewer/Viewer.svelte       |   7 +-
 .../components/viewer/ViewerContext.svelte    |   8 +-
 src/lib/components/viewer/Zoom.svelte         | 111 ++----------
 src/lib/components/viewer/tests/Zoom.test.ts  |  26 ---
 .../tests/__snapshots__/viewer.test.ts.snap   | 164 ++++++++++++++++++
 src/lib/utils/tests/viewer.test.ts            |  45 ++++-
 src/lib/utils/viewer.ts                       |  93 +++++++++-
 11 files changed, 351 insertions(+), 142 deletions(-)
 delete mode 100644 src/lib/components/viewer/tests/Zoom.test.ts
 create mode 100644 src/lib/utils/tests/__snapshots__/viewer.test.ts.snap

diff --git a/src/lib/components/viewer/Grid.svelte b/src/lib/components/viewer/Grid.svelte
index 5560e03aa..d5b05757d 100644
--- a/src/lib/components/viewer/Grid.svelte
+++ b/src/lib/components/viewer/Grid.svelte
@@ -15,11 +15,16 @@
   import { IMAGE_WIDTHS_MAP } from "@/config/config.js";
   import { pageImageUrl } from "$lib/api/documents";
   import { pageSizesFromSpec } from "@/api/pageSize.js";
-  import { getDocument } from "$lib/components/viewer/ViewerContext.svelte";
+  import {
+    getDocument,
+    getZoom,
+  } from "$lib/components/viewer/ViewerContext.svelte";
+  import { zoomToSize } from "@/lib/utils/viewer";
 
   export let size: Sizes = "thumbnail";
 
   const document = getDocument();
+  const zoom = getZoom();
 
   const SCALE = {
     thumbnail: 1,
@@ -28,6 +33,7 @@
     large: 3,
   };
 
+  $: size = zoomToSize($zoom);
   $: sizes = pageSizesFromSpec(document.page_spec);
   $: scale = SCALE[size] ?? 1;
   $: width = IMAGE_WIDTHS_MAP.get(size) / scale;
diff --git a/src/lib/components/viewer/PDF.svelte b/src/lib/components/viewer/PDF.svelte
index f53b23b41..48da46f89 100644
--- a/src/lib/components/viewer/PDF.svelte
+++ b/src/lib/components/viewer/PDF.svelte
@@ -17,14 +17,25 @@
 
   import { scrollToPage } from "$lib/utils/scroll";
   import { remToPx } from "$lib/utils/layout";
-  import { getNotes, getSections, pageSizes } from "$lib/utils/viewer";
-  import { getCurrentPage, getDocument, getPDF } from "./ViewerContext.svelte";
+  import {
+    getNotes,
+    getSections,
+    pageSizes,
+    zoomToScale,
+  } from "$lib/utils/viewer";
+  import {
+    getCurrentPage,
+    getDocument,
+    getPDF,
+    getZoom,
+  } from "./ViewerContext.svelte";
 
-  export let scale: number | "width" | "height" = 1;
   const document: Document = getDocument();
   const currentPage = getCurrentPage();
   const pdf = getPDF();
+  const zoom = getZoom();
 
+  $: scale = zoomToScale($zoom);
   $: sizes = document.page_spec ? pageSizes(document.page_spec) : [];
   $: notes = getNotes(document);
   $: sections = getSections(document);
diff --git a/src/lib/components/viewer/PaginationToolbar.svelte b/src/lib/components/viewer/PaginationToolbar.svelte
index 37cc3d23a..8566e01bf 100644
--- a/src/lib/components/viewer/PaginationToolbar.svelte
+++ b/src/lib/components/viewer/PaginationToolbar.svelte
@@ -133,7 +133,7 @@
   {/if}
 
   
- +
diff --git a/src/lib/components/viewer/Text.svelte b/src/lib/components/viewer/Text.svelte index 492afb603..15e1b09aa 100644 --- a/src/lib/components/viewer/Text.svelte +++ b/src/lib/components/viewer/Text.svelte @@ -11,13 +11,17 @@ import { highlight } from "$lib/utils/search"; import Page from "./Page.svelte"; - import { getText, getCurrentPage, getQuery } from "./ViewerContext.svelte"; - - export let zoom: number = 1; + import { + getText, + getCurrentPage, + getQuery, + getZoom, + } from "./ViewerContext.svelte"; const text = getText(); const query = getQuery(); const currentPage = getCurrentPage(); + const zoom = getZoom(); onMount(async () => { if ($currentPage > 1) { @@ -26,7 +30,7 @@ }); -
+
{#each text?.pages as { page, contents }}
diff --git a/src/lib/components/viewer/Viewer.svelte b/src/lib/components/viewer/Viewer.svelte
index a04d388db..56829e5cb 100644
--- a/src/lib/components/viewer/Viewer.svelte
+++ b/src/lib/components/viewer/Viewer.svelte
@@ -24,7 +24,6 @@ Assumes it's a child of a ViewerContext
   import RedactionToolbar from "./RedactionToolbar.svelte";
 
   // utils
-  import { zoomToScale, zoom, zoomToSize } from "./Zoom.svelte";
   import { getCurrentMode, isEmbedded } from "./ViewerContext.svelte";
 
   const embed = isEmbedded();
@@ -73,11 +72,11 @@ Assumes it's a child of a ViewerContext
 
     
     {#if showPDF}
-      
+      
     {:else if mode === "text"}
-      
+      
     {:else if mode === "grid"}
-      
+      
     {:else if mode === "notes"}
       
     {/if}
diff --git a/src/lib/components/viewer/ViewerContext.svelte b/src/lib/components/viewer/ViewerContext.svelte
index 27730a204..82b6c6961 100644
--- a/src/lib/components/viewer/ViewerContext.svelte
+++ b/src/lib/components/viewer/ViewerContext.svelte
@@ -14,6 +14,7 @@ layouts, stories, and tests.
     DocumentText,
     Note,
     ViewerMode,
+    Zoom,
   } from "@/lib/api/types";
   import {
     pageFromHash,
@@ -71,6 +72,10 @@ layouts, stories, and tests.
   export function getPDFProgress(): Writable {
     return getContext("progress");
   }
+
+  export function getZoom(): Writable {
+    return getContext("zoom");
+  }
 
 
 
+  import { getDefaultZoom, getZoomLevels } from "@/lib/utils/viewer";
+  import { getCurrentMode, getZoom } from "./ViewerContext.svelte";
 
-
 
 {#if zoomLevels.length}
   
diff --git a/src/lib/components/viewer/tests/Zoom.test.ts b/src/lib/components/viewer/tests/Zoom.test.ts
deleted file mode 100644
index 68d5aff9a..000000000
--- a/src/lib/components/viewer/tests/Zoom.test.ts
+++ /dev/null
@@ -1,26 +0,0 @@
-import { test, expect } from "vitest";
-import { getDefaultZoom, zoomToScale, zoomToSize } from "../Zoom.svelte";
-
-test("zoomToScale", () => {
-  expect(zoomToScale("width")).toEqual("width");
-  expect(zoomToScale("height")).toEqual("height");
-  expect(zoomToScale(1.1)).toEqual(1.1);
-  expect(zoomToScale("1.2")).toEqual(1.2);
-  expect(zoomToScale(undefined)).toEqual(1);
-  expect(zoomToScale("foobar")).toEqual(1);
-});
-
-test("zoomToSize", () => {
-  expect(zoomToSize("xlarge")).toEqual("xlarge");
-  expect(zoomToSize("large")).toEqual("large");
-  expect(zoomToSize(2000)).toEqual("small");
-});
-
-test("getDefaultZoom", () => {
-  expect(getDefaultZoom("document")).toEqual("width");
-  expect(getDefaultZoom("text")).toEqual(1);
-  expect(getDefaultZoom("grid")).toEqual("small");
-  expect(getDefaultZoom("notes")).toEqual(1);
-  expect(getDefaultZoom("annotating")).toEqual("width");
-  expect(getDefaultZoom("redacting")).toEqual("width");
-});
diff --git a/src/lib/utils/tests/__snapshots__/viewer.test.ts.snap b/src/lib/utils/tests/__snapshots__/viewer.test.ts.snap
new file mode 100644
index 000000000..4532219eb
--- /dev/null
+++ b/src/lib/utils/tests/__snapshots__/viewer.test.ts.snap
@@ -0,0 +1,164 @@
+// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
+
+exports[`getZoomLevels 1`] = `
+[
+  [
+    "width",
+    "zoom.fitWidth",
+  ],
+  [
+    "height",
+    "zoom.fitHeight",
+  ],
+  [
+    0.5,
+    "50%",
+  ],
+  [
+    0.75,
+    "75%",
+  ],
+  [
+    1,
+    "100%",
+  ],
+  [
+    1.25,
+    "125%",
+  ],
+  [
+    1.5,
+    "150%",
+  ],
+  [
+    2,
+    "200%",
+  ],
+]
+`;
+
+exports[`getZoomLevels 2`] = `
+[
+  [
+    0.5,
+    "50%",
+  ],
+  [
+    0.75,
+    "75%",
+  ],
+  [
+    1,
+    "100%",
+  ],
+  [
+    1.25,
+    "125%",
+  ],
+  [
+    1.5,
+    "150%",
+  ],
+  [
+    2,
+    "200%",
+  ],
+]
+`;
+
+exports[`getZoomLevels 3`] = `
+[
+  [
+    "thumbnail",
+    "zoom.thumbnail",
+  ],
+  [
+    "small",
+    "zoom.small",
+  ],
+  [
+    "normal",
+    "zoom.normal",
+  ],
+  [
+    "large",
+    "zoom.large",
+  ],
+]
+`;
+
+exports[`getZoomLevels 4`] = `[]`;
+
+exports[`getZoomLevels 5`] = `
+[
+  [
+    "width",
+    "zoom.fitWidth",
+  ],
+  [
+    "height",
+    "zoom.fitHeight",
+  ],
+  [
+    0.5,
+    "50%",
+  ],
+  [
+    0.75,
+    "75%",
+  ],
+  [
+    1,
+    "100%",
+  ],
+  [
+    1.25,
+    "125%",
+  ],
+  [
+    1.5,
+    "150%",
+  ],
+  [
+    2,
+    "200%",
+  ],
+]
+`;
+
+exports[`getZoomLevels 6`] = `
+[
+  [
+    "width",
+    "zoom.fitWidth",
+  ],
+  [
+    "height",
+    "zoom.fitHeight",
+  ],
+  [
+    0.5,
+    "50%",
+  ],
+  [
+    0.75,
+    "75%",
+  ],
+  [
+    1,
+    "100%",
+  ],
+  [
+    1.25,
+    "125%",
+  ],
+  [
+    1.5,
+    "150%",
+  ],
+  [
+    2,
+    "200%",
+  ],
+]
+`;
diff --git a/src/lib/utils/tests/viewer.test.ts b/src/lib/utils/tests/viewer.test.ts
index c5171282b..dd8421dd7 100644
--- a/src/lib/utils/tests/viewer.test.ts
+++ b/src/lib/utils/tests/viewer.test.ts
@@ -1,7 +1,15 @@
-import { describe, it, expect, vi } from "vitest";
+import { describe, it, test, expect } from "vitest";
 import { document } from "@/test/fixtures/documents";
 import { note } from "@/test/fixtures/notes";
-import { fitPage, getViewerHref, pageSizes } from "../viewer";
+import {
+  fitPage,
+  getViewerHref,
+  pageSizes,
+  zoomToScale,
+  zoomToSize,
+  getZoomLevels,
+  getDefaultZoom,
+} from "../viewer";
 import {
   canonicalUrl,
   pageHashUrl,
@@ -83,3 +91,36 @@ describe("fitPage", () => {
     expect(fitPage(750, 2000, container, "height")).toEqual(1000 / 2000);
   });
 });
+
+test("zoomToScale", () => {
+  expect(zoomToScale("width")).toEqual("width");
+  expect(zoomToScale("height")).toEqual("height");
+  expect(zoomToScale(1.1)).toEqual(1.1);
+  expect(zoomToScale("1.2")).toEqual(1.2);
+  expect(zoomToScale(undefined)).toEqual(1);
+  expect(zoomToScale("foobar")).toEqual(1);
+});
+
+test("zoomToSize", () => {
+  expect(zoomToSize("xlarge")).toEqual("xlarge");
+  expect(zoomToSize("large")).toEqual("large");
+  expect(zoomToSize(2000)).toEqual("small");
+});
+
+test("getDefaultZoom", () => {
+  expect(getDefaultZoom("document")).toEqual("width");
+  expect(getDefaultZoom("text")).toEqual(1);
+  expect(getDefaultZoom("grid")).toEqual("small");
+  expect(getDefaultZoom("notes")).toEqual(1);
+  expect(getDefaultZoom("annotating")).toEqual("width");
+  expect(getDefaultZoom("redacting")).toEqual("width");
+});
+
+test("getZoomLevels", () => {
+  expect(getZoomLevels("document")).toMatchSnapshot();
+  expect(getZoomLevels("text")).toMatchSnapshot();
+  expect(getZoomLevels("grid")).toMatchSnapshot();
+  expect(getZoomLevels("notes")).toMatchSnapshot();
+  expect(getZoomLevels("annotating")).toMatchSnapshot();
+  expect(getZoomLevels("redacting")).toMatchSnapshot();
+});
diff --git a/src/lib/utils/viewer.ts b/src/lib/utils/viewer.ts
index 98ebc0bdf..c5cae3801 100644
--- a/src/lib/utils/viewer.ts
+++ b/src/lib/utils/viewer.ts
@@ -1,6 +1,14 @@
-import type { Document, Note, Section, ViewerMode } from "$lib/api/types";
+import type {
+  Document,
+  Note,
+  Section,
+  Sizes,
+  ViewerMode,
+  Zoom,
+} from "$lib/api/types";
 import { canonicalUrl, pageHashUrl } from "../api/documents";
 import { noteHashUrl } from "../api/notes";
+import { IMAGE_WIDTHS_MAP } from "@/config/config.js";
 
 interface ViewerHrefOptions {
   document?: Document;
@@ -110,3 +118,86 @@ export function getSections(document: Document): Record {
     }, {}) ?? {}
   );
 }
+
+// for typescript
+export function zoomToScale(zoom: any): number | "width" | "height" {
+  if (zoom === "width" || zoom === "height") {
+    return zoom;
+  }
+
+  return +zoom || 1;
+}
+
+export function zoomToSize(zoom: any): Sizes {
+  if (IMAGE_WIDTHS_MAP.has(zoom)) {
+    return zoom;
+  }
+
+  return "small";
+}
+
+/**
+ * Generate a default zoom, based on mode
+ * @param mode
+ */
+export function getDefaultZoom(mode: ViewerMode): Zoom {
+  switch (mode) {
+    case "document":
+      return "width";
+
+    case "annotating":
+      return "width";
+
+    case "redacting":
+      return "width";
+
+    case "grid":
+      return "small";
+
+    default:
+      return 1;
+  }
+}
+
+/**
+ * Generate zoom levels based on mode, since each zooms in a slightly different way
+ */
+export function getZoomLevels(mode: ViewerMode): [string | number, string][] {
+  switch (mode) {
+    case "document":
+    case "annotating":
+    case "redacting":
+      return [
+        ["width", "zoom.fitWidth"],
+        ["height", "zoom.fitHeight"],
+        [0.5, "50%"],
+        [0.75, "75%"],
+        [1, "100%"],
+        [1.25, "125%"],
+        [1.5, "150%"],
+        [2, "200%"],
+      ];
+
+    case "text":
+      return [
+        [0.5, "50%"],
+        [0.75, "75%"],
+        [1, "100%"],
+        [1.25, "125%"],
+        [1.5, "150%"],
+        [2, "200%"],
+      ];
+
+    case "grid":
+      return [
+        ["thumbnail", "zoom.thumbnail"],
+        ["small", "zoom.small"],
+        ["normal", "zoom.normal"],
+        ["large", "zoom.large"],
+      ];
+
+    default:
+      // todo: notes, maybe
+      return [];
+  }
+}

From ceabbf0f4365058e8b84fbc7740f16ef93fd58b4 Mon Sep 17 00:00:00 2001
From: Allan Lasser 
Date: Mon, 14 Oct 2024 11:42:42 -0400
Subject: [PATCH 39/72] Adds some console logging for debugging annotation
 links

---
 src/lib/components/viewer/AnnotationLayer.svelte | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/src/lib/components/viewer/AnnotationLayer.svelte b/src/lib/components/viewer/AnnotationLayer.svelte
index 7957b71c1..b7808e2d4 100644
--- a/src/lib/components/viewer/AnnotationLayer.svelte
+++ b/src/lib/components/viewer/AnnotationLayer.svelte
@@ -56,6 +56,9 @@ Assumes it's a child of a ViewerContext
   $: currentNote = notes.find(
     (note) => note?.id && note.id === noteFromHash($page.url.hash),
   );
+  $: {
+    console.log(notes, currentNote, $page.url.hash);
+  }
   $: writing = $mode === "annotating";
   $: editing = Boolean(currentNote) || (Boolean(newNote) && !dragging);
   $: edit_page_note =

From 0d9c78fbde51841f3f8e7ea59d34cf895ca912ed Mon Sep 17 00:00:00 2001
From: Allan Lasser 
Date: Mon, 14 Oct 2024 14:22:13 -0400
Subject: [PATCH 40/72] Update AnnotationLayer rendering

---
 .../components/viewer/AnnotationLayer.svelte  |  40 ++----
 src/lib/components/viewer/Note.svelte         | 114 ++++++++++++------
 src/lib/components/viewer/PDF.svelte          |   4 +-
 src/lib/components/viewer/PDFPage.svelte      |  15 ++-
 .../stories/AnnotationLayer.stories.svelte    |  11 +-
 5 files changed, 99 insertions(+), 85 deletions(-)

diff --git a/src/lib/components/viewer/AnnotationLayer.svelte b/src/lib/components/viewer/AnnotationLayer.svelte
index b7808e2d4..f5f9dcb0f 100644
--- a/src/lib/components/viewer/AnnotationLayer.svelte
+++ b/src/lib/components/viewer/AnnotationLayer.svelte
@@ -35,30 +35,25 @@ Assumes it's a child of a ViewerContext
     getDocument,
     isEmbedded,
   } from "$lib/components/viewer/ViewerContext.svelte";
-  import { getViewerHref } from "$lib/utils/viewer";
+  import { getNotes, getViewerHref } from "$lib/utils/viewer";
   import Note from "./Note.svelte";
   import { page } from "$app/stores";
 
   export let scale = 1.5;
+  export let page_number: number; // zero-indexed
 
   const document = getDocument();
   const embed = isEmbedded();
   const mode = getCurrentMode();
 
-  export let notes: NoteType[] = [];
-  export let page_number: number; // zero-indexed
-
-  let container: HTMLElement;
   let newNote: Partial & BBox = null; // is this too clever?
   let dragging = false;
-  let form: EditNote;
 
+  $: notes =
+    getNotes(document)[page_number]?.filter((note) => !isPageLevel(note)) ?? [];
   $: currentNote = notes.find(
     (note) => note?.id && note.id === noteFromHash($page.url.hash),
   );
-  $: {
-    console.log(notes, currentNote, $page.url.hash);
-  }
   $: writing = $mode === "annotating";
   $: editing = Boolean(currentNote) || (Boolean(newNote) && !dragging);
   $: edit_page_note =
@@ -146,13 +141,14 @@ Assumes it's a child of a ViewerContext
     const target = e.target as HTMLAnchorElement;
     const href =
       target?.href || getViewerHref({ document, note, mode: $mode, embed });
+    console.log(target?.href, href, note);
     pushState(href, {});
   }
 
   function closeNote() {
     newNote = null;
     dragging = false;
-    pushState(window.location.pathname, {});
+    pushState(getViewerHref({ document, mode: $mode, embed }), {});
   }
 
   function onkeypress(e: KeyboardEvent) {
@@ -165,7 +161,6 @@ Assumes it's a child of a ViewerContext
 
 
 
- {#each notes || [] as note} - {@const is_active = note && currentNote && note.id === currentNote.id} + {#each notes as note} + {@const is_active = note.id === currentNote?.id} openNote(e, note)} > - {#if is_active} - - {/if} {#if is_active}
{#if writing}
- +
{:else} - + {/if} {:else}
-

{note.title} {canEdit} {document.edit_access} {embed}

-
- (shareNoteOpen = true)}> - {$_("dialog.share")} - - {#if canEdit} - - {$_("dialog.edit")} - + {#if !page_level} + + {/if} +
+

{note.title} {canEdit} {document.edit_access} {embed}

+ {#if user} +

+ {$_("annotation.by", { values: { name: getUserName(user) } })} +

{/if}
@@ -221,12 +230,24 @@ {$_(`access.${access[note.access].value}.title`)} - - {#if user} -

- {$_("annotation.by", { values: { name: getUserName(user) } })} -

- {/if} +
+ {#if canEdit} + + {/if} + +
{#if shareNoteOpen} @@ -244,8 +265,9 @@ padding: var(--font-xs, 0.75rem) var(--font-md, 1rem); flex-direction: column; align-items: flex-start; - gap: var(--font-xs, 0.75rem); + gap: 1rem; pointer-events: all; + position: relative; border-radius: 0.5rem; border: 1px solid var(--gray-2, #d8dee2); @@ -286,9 +308,22 @@ header { display: flex; - justify-content: space-between; align-items: center; align-self: stretch; + gap: 0.5rem; + } + + .headerText { + flex: 1 1 auto; + display: flex; + align-items: baseline; + gap: 1rem; + } + + h3 { + flex: 0 1 auto; + text-align: left; + font-weight: var(--font-semibold); } .actions { @@ -309,52 +344,59 @@ } .public .highlight { - border-color: var(--note-public); + border-color: var(--yellow-3); } .private .highlight { - border-color: var(--note-private); + border-color: var(--blue-3); } .organization .highlight { - border-color: var(--note-org); + border-color: var(--green-3); + } + + .content { + line-height: 1.5; + font-size: var(--font-md); + } + + .author { + font-size: var(--font-xs); + color: var(--gray-5); } canvas { max-width: 100%; - padding: 0.25rem; + padding: 1rem; } footer { display: flex; - justify-content: space-between; + gap: 1rem; align-items: center; align-self: stretch; } - footer p { - color: var(--gray-4, #5c717c); - font-size: var(--font-sm); - } - span.access { display: flex; align-items: center; gap: 0.5rem; font-size: var(--font-sm); + font-weight: var(--font-semibold); } span.access.public { - fill: var(--note-public); + fill: var(--yellow-3); + color: var(--yellow-4); } span.access.organization { - color: var(--note-org); - fill: var(--note-org); + fill: var(--green-3); + color: var(--green-4); } span.access.private { - color: var(--note-private); - fill: var(--note-private); + color: var(--blue-4); + fill: var(--blue-3); } diff --git a/src/lib/components/viewer/PDF.svelte b/src/lib/components/viewer/PDF.svelte index 48da46f89..de7fb0f90 100644 --- a/src/lib/components/viewer/PDF.svelte +++ b/src/lib/components/viewer/PDF.svelte @@ -68,11 +68,11 @@ {#each sizes as [width, height], n} {@const page_number = n + 1} {#if sections[n]} -

+

{sections[n].title}

{/if} - + {/each}
diff --git a/src/lib/components/viewer/PDFPage.svelte b/src/lib/components/viewer/PDFPage.svelte index aa9b3e55e..33b8f53c8 100644 --- a/src/lib/components/viewer/PDFPage.svelte +++ b/src/lib/components/viewer/PDFPage.svelte @@ -10,7 +10,7 @@ Selectable text can be rendered in one of two ways: - Passed in as a server-fetched JSON object --> @@ -33,7 +26,7 @@ {#each sizes as [width, height], page_number}
- +
{/each} @@ -47,7 +40,7 @@ {#each sizes as [width, height], page_number}
- +
{/each} From e8c41092cb04bd1a7624a355969e76e844c8ce5f Mon Sep 17 00:00:00 2001 From: Allan Lasser Date: Mon, 14 Oct 2024 14:22:47 -0400 Subject: [PATCH 41/72] Check --- .../components/viewer/AnnotationLayer.svelte | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/src/lib/components/viewer/AnnotationLayer.svelte b/src/lib/components/viewer/AnnotationLayer.svelte index f5f9dcb0f..24689a7c4 100644 --- a/src/lib/components/viewer/AnnotationLayer.svelte +++ b/src/lib/components/viewer/AnnotationLayer.svelte @@ -278,25 +278,6 @@ Assumes it's a child of a ViewerContext left: -3rem; } - .note button { - border: none; - padding: 0; - background: none; - cursor: pointer; - - position: absolute; - margin: auto 0; - top: 0; - left: 0; - bottom: 0; - right: 0; - - display: flex; - padding-left: 0.5rem; - justify-content: left; - align-items: center; - } - .note-form { position: absolute; pointer-events: all; From bd6a419748a2fa0c290ca107d144a25a65b747ae Mon Sep 17 00:00:00 2001 From: Allan Lasser Date: Mon, 14 Oct 2024 14:43:50 -0400 Subject: [PATCH 42/72] Attempts fix for note open/closed state --- .../components/viewer/AnnotationLayer.svelte | 18 +++++++++--------- src/lib/components/viewer/Note.svelte | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/lib/components/viewer/AnnotationLayer.svelte b/src/lib/components/viewer/AnnotationLayer.svelte index 24689a7c4..5971b78df 100644 --- a/src/lib/components/viewer/AnnotationLayer.svelte +++ b/src/lib/components/viewer/AnnotationLayer.svelte @@ -15,7 +15,6 @@ Assumes it's a child of a ViewerContext import { pushState } from "$app/navigation"; import { _ } from "svelte-i18n"; - import { XCircleFill16 } from "svelte-octicons"; import EditNote from "../forms/EditNote.svelte"; import NoteTab from "./NoteTab.svelte"; @@ -51,9 +50,11 @@ Assumes it's a child of a ViewerContext $: notes = getNotes(document)[page_number]?.filter((note) => !isPageLevel(note)) ?? []; - $: currentNote = notes.find( - (note) => note?.id && note.id === noteFromHash($page.url.hash), - ); + $: currentId = noteFromHash($page.url.hash ?? ""); + $: currentNote = notes.find((note) => note.id === currentId); + $: { + console.log(currentId, currentNote); + } $: writing = $mode === "annotating"; $: editing = Boolean(currentNote) || (Boolean(newNote) && !dragging); $: edit_page_note = @@ -141,14 +142,14 @@ Assumes it's a child of a ViewerContext const target = e.target as HTMLAnchorElement; const href = target?.href || getViewerHref({ document, note, mode: $mode, embed }); - console.log(target?.href, href, note); pushState(href, {}); } function closeNote() { newNote = null; dragging = false; - pushState(getViewerHref({ document, mode: $mode, embed }), {}); + const href = getViewerHref({ document, mode: $mode, embed }); + pushState(href, {}); } function onkeypress(e: KeyboardEvent) { @@ -169,8 +170,7 @@ Assumes it's a child of a ViewerContext on:pointermove|self={pointermove} on:pointerup|self={pointerup} > - {#each notes as note} - {@const is_active = note.id === currentNote?.id} + {#each notes as note (note.id)} - {#if is_active} + {#if note.id === currentId}
{/if}
-

{note.title} {canEdit} {document.edit_access} {embed}

+

{note.title}

{#if user}

{$_("annotation.by", { values: { name: getUserName(user) } })} From 72b76752701e02b71ff222e6c2a4210af97991c5 Mon Sep 17 00:00:00 2001 From: Allan Lasser Date: Wed, 16 Oct 2024 12:20:46 -0400 Subject: [PATCH 43/72] Derive currentNote from page url hash --- .../components/viewer/AnnotationLayer.svelte | 26 +++++-------------- .../components/viewer/ViewerContext.svelte | 19 +++++++++++++- 2 files changed, 25 insertions(+), 20 deletions(-) diff --git a/src/lib/components/viewer/AnnotationLayer.svelte b/src/lib/components/viewer/AnnotationLayer.svelte index 5971b78df..28beeb2c8 100644 --- a/src/lib/components/viewer/AnnotationLayer.svelte +++ b/src/lib/components/viewer/AnnotationLayer.svelte @@ -22,21 +22,15 @@ Assumes it's a child of a ViewerContext import Modal from "../layouts/Modal.svelte"; import Portal from "../layouts/Portal.svelte"; - import { - noteHashUrl, - width, - height, - isPageLevel, - noteFromHash, - } from "$lib/api/notes"; + import { width, height, isPageLevel } from "$lib/api/notes"; import { getCurrentMode, + getCurrentNote, getDocument, isEmbedded, } from "$lib/components/viewer/ViewerContext.svelte"; import { getNotes, getViewerHref } from "$lib/utils/viewer"; import Note from "./Note.svelte"; - import { page } from "$app/stores"; export let scale = 1.5; export let page_number: number; // zero-indexed @@ -44,23 +38,17 @@ Assumes it's a child of a ViewerContext const document = getDocument(); const embed = isEmbedded(); const mode = getCurrentMode(); + const currentNote = getCurrentNote(); let newNote: Partial & BBox = null; // is this too clever? let dragging = false; $: notes = getNotes(document)[page_number]?.filter((note) => !isPageLevel(note)) ?? []; - $: currentId = noteFromHash($page.url.hash ?? ""); - $: currentNote = notes.find((note) => note.id === currentId); - $: { - console.log(currentId, currentNote); - } $: writing = $mode === "annotating"; $: editing = Boolean(currentNote) || (Boolean(newNote) && !dragging); $: edit_page_note = - Boolean(currentNote) && - isPageLevel(currentNote) && - currentNote.page_number === page_number; + writing && Boolean($currentNote) && isPageLevel($currentNote); function pointerdown(e: PointerEvent) { if (currentNote || newNote) return; @@ -180,7 +168,7 @@ Assumes it's a child of a ViewerContext > - {#if note.id === currentId} + {#if note.id === $currentNote.id}

diff --git a/src/lib/components/viewer/ViewerContext.svelte b/src/lib/components/viewer/ViewerContext.svelte index 82b6c6961..a2a423766 100644 --- a/src/lib/components/viewer/ViewerContext.svelte +++ b/src/lib/components/viewer/ViewerContext.svelte @@ -4,7 +4,13 @@ layouts, stories, and tests. -->
Date: Thu, 17 Oct 2024 10:42:59 -0400 Subject: [PATCH 52/72] Adds success handler for EditNote form --- src/lib/components/forms/EditNote.svelte | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib/components/forms/EditNote.svelte b/src/lib/components/forms/EditNote.svelte index f1925c869..40b18efad 100644 --- a/src/lib/components/forms/EditNote.svelte +++ b/src/lib/components/forms/EditNote.svelte @@ -39,6 +39,7 @@ Positioning and generating coordinates should happen outside of this form. submitter.disabled = true; return ({ result, update }) => { if (result.type === "success") { + dispatch("success", result.note); // invalidate(`document:${document.id}`); update(result); dispatch("close"); From 00438ae25f65e29a2d96f64c63281b487a1baaee Mon Sep 17 00:00:00 2001 From: Allan Lasser Date: Thu, 17 Oct 2024 10:43:42 -0400 Subject: [PATCH 53/72] Update logic for drawing new annotations --- .../components/viewer/AnnotationLayer.svelte | 129 +++++++++--------- 1 file changed, 64 insertions(+), 65 deletions(-) diff --git a/src/lib/components/viewer/AnnotationLayer.svelte b/src/lib/components/viewer/AnnotationLayer.svelte index 0cf083883..408fa7baf 100644 --- a/src/lib/components/viewer/AnnotationLayer.svelte +++ b/src/lib/components/viewer/AnnotationLayer.svelte @@ -10,9 +10,9 @@ Only one note can be added/edited at a time. Assumes it's a child of a ViewerContext --> @@ -161,11 +164,11 @@ Assumes it's a child of a ViewerContext
{#each notes as note (note.id)}
- {#if !dragging} + {#if !drawing}
- +
{/if} {/if} @@ -243,6 +241,7 @@ Assumes it's a child of a ViewerContext note={$currentNote} {page_number} on:close={closeNote} + on:success={handleNewNoteSuccess} /> @@ -266,7 +265,7 @@ Assumes it's a child of a ViewerContext pointer-events: all; } - .notes.dragging :global(*) { + .notes.drawing :global(*) { pointer-events: none; } @@ -286,7 +285,7 @@ Assumes it's a child of a ViewerContext z-index: 10; } - .notes.editing { + .notes.activeNote { cursor: auto; pointer-events: none; } From 89c576e5040b36c19ae8be7fcea1b95b360b85c1 Mon Sep 17 00:00:00 2001 From: Allan Lasser Date: Thu, 17 Oct 2024 14:12:06 -0400 Subject: [PATCH 54/72] Always keep highlight visible --- .../components/viewer/AnnotationLayer.svelte | 25 +++++++++---------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/src/lib/components/viewer/AnnotationLayer.svelte b/src/lib/components/viewer/AnnotationLayer.svelte index 408fa7baf..f299d1ae3 100644 --- a/src/lib/components/viewer/AnnotationLayer.svelte +++ b/src/lib/components/viewer/AnnotationLayer.svelte @@ -181,6 +181,18 @@ Assumes it's a child of a ViewerContext >
+ openNote(e, note)} + > + {note.title} + {#if note.id === $currentNote?.id}
{/if} - {:else} - openNote(e, note)} - > - {note.title} - {/if} {/each} From 80bf20340a72971ba4513cebfc403a2892353a24 Mon Sep 17 00:00:00 2001 From: Allan Lasser Date: Thu, 17 Oct 2024 14:12:15 -0400 Subject: [PATCH 55/72] Try using stores for reactive prop values --- .../components/viewer/ViewerContext.svelte | 57 ++++++++++++------- 1 file changed, 37 insertions(+), 20 deletions(-) diff --git a/src/lib/components/viewer/ViewerContext.svelte b/src/lib/components/viewer/ViewerContext.svelte index f9c73b51f..08eb7f4b1 100644 --- a/src/lib/components/viewer/ViewerContext.svelte +++ b/src/lib/components/viewer/ViewerContext.svelte @@ -43,28 +43,28 @@ layouts, stories, and tests. } export function getDocument(): Document { - return getContext("document"); + return get(getContext("document")); } export function getText(): Maybe { - return getContext("text"); + return get(getContext("text")); } export function getAssetUrl(): URL { - return getContext("asset_url"); + return get(getContext("asset_url")); } export function getQuery(): string { - return getContext("query") ?? ""; + return get(getContext("query") ?? writable("")); } export function isEmbedded(): boolean { // are we embedded? - return getContext("embed") ?? false; + return get(getContext("embed") ?? writable(false)); } export function getCurrentNote(): Readable { - return getContext("currentNote"); + return get(getContext("currentNote")); } export function getCurrentPage(): Writable { @@ -92,7 +92,6 @@ layouts, stories, and tests. import { page as pageStore } from "$app/stores"; import type { _ } from "svelte-i18n"; import { noteFromHash } from "@/lib/api/notes"; - import type { Page } from "@sveltejs/kit"; export let document: Document; export let text: Maybe = undefined; @@ -115,22 +114,40 @@ layouts, stories, and tests. total: 0, }); - $: { - console.log($pageStore.url.hash); - } - + // to react to prop changes, values need to be a writable store + const stores = { + document: writable(document), + text: writable(text), + note: writable(note), + asset_url: writable(asset_url), + embed: writable(embed), + page: writable(page), + mode: writable(mode), + query: writable(query), + zoom: writable(zoom), + }; + // when a prop changes, update its stored value + $: stores.document.set(document); + $: stores.text.set(text); + $: stores.note.set(note); + $: stores.asset_url.set(asset_url); + $: stores.embed.set(embed); + $: stores.page.set(page); + $: stores.mode.set(mode); + $: stores.query.set(query); + $: stores.zoom.set(zoom); // stores we need deeper in the component tree, available via context - setContext("document", document); - setContext("text", text); - setContext("asset_url", asset_url); - setContext("embed", embed); - setContext("query", query); - setContext("currentNote", writable(note)); - setContext("currentPage", writable(page)); - setContext("currentMode", writable(mode)); + setContext("document", stores.document); + setContext("text", stores.text); + setContext("asset_url", stores.asset_url); + setContext("embed", stores.embed); + setContext("query", stores.query); + setContext("currentNote", stores.note); + setContext("currentPage", stores.page); + setContext("currentMode", stores.mode); + setContext("zoom", stores.zoom); setContext("progress", progress); setContext("pdf", pdf); - setContext("zoom", writable(zoom)); $: currentDoc = getDocument(); $: currentMode = getCurrentMode(); From 63113a0eedf1bdcffcea47b5c21afe263e6d4ef8 Mon Sep 17 00:00:00 2001 From: Allan Lasser Date: Thu, 17 Oct 2024 14:22:12 -0400 Subject: [PATCH 56/72] State fix --- .../components/viewer/ViewerContext.svelte | 37 ++++++------------- 1 file changed, 12 insertions(+), 25 deletions(-) diff --git a/src/lib/components/viewer/ViewerContext.svelte b/src/lib/components/viewer/ViewerContext.svelte index 08eb7f4b1..617ac3c8a 100644 --- a/src/lib/components/viewer/ViewerContext.svelte +++ b/src/lib/components/viewer/ViewerContext.svelte @@ -51,20 +51,20 @@ layouts, stories, and tests. } export function getAssetUrl(): URL { - return get(getContext("asset_url")); + return getContext("asset_url"); } export function getQuery(): string { - return get(getContext("query") ?? writable("")); + return getContext("query") ?? ""; } export function isEmbedded(): boolean { // are we embedded? - return get(getContext("embed") ?? writable(false)); + return getContext("embed") ?? false; } export function getCurrentNote(): Readable { - return get(getContext("currentNote")); + return getContext("currentNote"); } export function getCurrentPage(): Writable { @@ -118,34 +118,21 @@ layouts, stories, and tests. const stores = { document: writable(document), text: writable(text), - note: writable(note), - asset_url: writable(asset_url), - embed: writable(embed), - page: writable(page), - mode: writable(mode), - query: writable(query), - zoom: writable(zoom), }; // when a prop changes, update its stored value $: stores.document.set(document); $: stores.text.set(text); - $: stores.note.set(note); - $: stores.asset_url.set(asset_url); - $: stores.embed.set(embed); - $: stores.page.set(page); - $: stores.mode.set(mode); - $: stores.query.set(query); - $: stores.zoom.set(zoom); + // stores we need deeper in the component tree, available via context setContext("document", stores.document); setContext("text", stores.text); - setContext("asset_url", stores.asset_url); - setContext("embed", stores.embed); - setContext("query", stores.query); - setContext("currentNote", stores.note); - setContext("currentPage", stores.page); - setContext("currentMode", stores.mode); - setContext("zoom", stores.zoom); + setContext("asset_url", asset_url); + setContext("embed", embed); + setContext("query", query); + setContext("currentNote", note); + setContext("currentPage", page); + setContext("currentMode", mode); + setContext("zoom", zoom); setContext("progress", progress); setContext("pdf", pdf); From ab0316b238b7d1c1e8122ea3f5096db4c416d352 Mon Sep 17 00:00:00 2001 From: Allan Lasser Date: Thu, 17 Oct 2024 14:44:58 -0400 Subject: [PATCH 57/72] Undo state changes --- .../components/viewer/ViewerContext.svelte | 23 ++++++------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/src/lib/components/viewer/ViewerContext.svelte b/src/lib/components/viewer/ViewerContext.svelte index 617ac3c8a..a059ac305 100644 --- a/src/lib/components/viewer/ViewerContext.svelte +++ b/src/lib/components/viewer/ViewerContext.svelte @@ -43,11 +43,11 @@ layouts, stories, and tests. } export function getDocument(): Document { - return get(getContext("document")); + return getContext("document"); } export function getText(): Maybe { - return get(getContext("text")); + return getContext("text"); } export function getAssetUrl(): URL { @@ -114,24 +114,15 @@ layouts, stories, and tests. total: 0, }); - // to react to prop changes, values need to be a writable store - const stores = { - document: writable(document), - text: writable(text), - }; - // when a prop changes, update its stored value - $: stores.document.set(document); - $: stores.text.set(text); - // stores we need deeper in the component tree, available via context - setContext("document", stores.document); - setContext("text", stores.text); + setContext("document", document); + setContext("text", text); setContext("asset_url", asset_url); setContext("embed", embed); setContext("query", query); - setContext("currentNote", note); - setContext("currentPage", page); - setContext("currentMode", mode); + setContext("currentNote", writable(note)); + setContext("currentPage", writable(page)); + setContext("currentMode", writable(mode)); setContext("zoom", zoom); setContext("progress", progress); setContext("pdf", pdf); From 551a14a8d73b1c76719af5d9e259151b5b4ca854 Mon Sep 17 00:00:00 2001 From: Allan Lasser Date: Thu, 17 Oct 2024 14:47:06 -0400 Subject: [PATCH 58/72] Optimistically update notes array --- src/lib/components/viewer/AnnotationLayer.svelte | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/lib/components/viewer/AnnotationLayer.svelte b/src/lib/components/viewer/AnnotationLayer.svelte index f299d1ae3..467f6861c 100644 --- a/src/lib/components/viewer/AnnotationLayer.svelte +++ b/src/lib/components/viewer/AnnotationLayer.svelte @@ -151,11 +151,7 @@ Assumes it's a child of a ViewerContext function handleNewNoteSuccess(e: CustomEvent) { const note = e.detail; // optimistically update document notes - console.log( - notes.some(({ id }) => id === note.id), - notes, - note, - ); + notes.push(note); } @@ -222,7 +218,12 @@ Assumes it's a child of a ViewerContext {#if !drawing}
- +
{/if} {/if} @@ -240,7 +241,6 @@ Assumes it's a child of a ViewerContext note={$currentNote} {page_number} on:close={closeNote} - on:success={handleNewNoteSuccess} /> From d40c95397e6d447a0a0ae996e6278b877a964d77 Mon Sep 17 00:00:00 2001 From: Allan Lasser Date: Thu, 17 Oct 2024 14:50:48 -0400 Subject: [PATCH 59/72] Fix zoom store --- src/lib/components/viewer/ViewerContext.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/components/viewer/ViewerContext.svelte b/src/lib/components/viewer/ViewerContext.svelte index a059ac305..35942f533 100644 --- a/src/lib/components/viewer/ViewerContext.svelte +++ b/src/lib/components/viewer/ViewerContext.svelte @@ -123,7 +123,7 @@ layouts, stories, and tests. setContext("currentNote", writable(note)); setContext("currentPage", writable(page)); setContext("currentMode", writable(mode)); - setContext("zoom", zoom); + setContext("zoom", writable(zoom)); setContext("progress", progress); setContext("pdf", pdf); From 4bff3a1a0efefee9db70ba337ee154f2d6fff9af Mon Sep 17 00:00:00 2001 From: Allan Lasser Date: Thu, 17 Oct 2024 15:17:43 -0400 Subject: [PATCH 60/72] Invalidate document when creating note --- src/lib/components/forms/EditNote.svelte | 2 -- src/lib/components/viewer/AnnotationLayer.svelte | 7 +++++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/lib/components/forms/EditNote.svelte b/src/lib/components/forms/EditNote.svelte index 40b18efad..368b694a0 100644 --- a/src/lib/components/forms/EditNote.svelte +++ b/src/lib/components/forms/EditNote.svelte @@ -8,7 +8,6 @@ Positioning and generating coordinates should happen outside of this form. import type { Bounds, Document, Note } from "$lib/api/types"; import { enhance } from "$app/forms"; - import { invalidate } from "$app/navigation"; import { createEventDispatcher } from "svelte"; import { _ } from "svelte-i18n"; @@ -40,7 +39,6 @@ Positioning and generating coordinates should happen outside of this form. return ({ result, update }) => { if (result.type === "success") { dispatch("success", result.note); - // invalidate(`document:${document.id}`); update(result); dispatch("close"); } diff --git a/src/lib/components/viewer/AnnotationLayer.svelte b/src/lib/components/viewer/AnnotationLayer.svelte index 467f6861c..0dd0eda5a 100644 --- a/src/lib/components/viewer/AnnotationLayer.svelte +++ b/src/lib/components/viewer/AnnotationLayer.svelte @@ -12,7 +12,7 @@ Assumes it's a child of a ViewerContext From 461bdce28321d38eb1d79b3f3fe5ec78e9e86ff0 Mon Sep 17 00:00:00 2001 From: Allan Lasser Date: Thu, 17 Oct 2024 15:34:41 -0400 Subject: [PATCH 61/72] Removes optimistic update --- src/lib/components/viewer/AnnotationLayer.svelte | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/lib/components/viewer/AnnotationLayer.svelte b/src/lib/components/viewer/AnnotationLayer.svelte index 0dd0eda5a..248d89e0f 100644 --- a/src/lib/components/viewer/AnnotationLayer.svelte +++ b/src/lib/components/viewer/AnnotationLayer.svelte @@ -36,7 +36,7 @@ Assumes it's a child of a ViewerContext export let scale = 1.5; export let page_number: number; // zero-indexed - const document = getDocument(); + let document = getDocument(); const embed = isEmbedded(); const mode = getCurrentMode(); const currentNote = getCurrentNote(); @@ -151,8 +151,6 @@ Assumes it's a child of a ViewerContext function handleNewNoteSuccess(e: CustomEvent) { const note = e.detail; - // optimistically update document notes - setContext("document", { ...document, notes: [...notes, note] }); // invalidate the document invalidate(`document:${document.id}`); } From 53a1c2212243b959848c28d6cc62133ecf418b28 Mon Sep 17 00:00:00 2001 From: Allan Lasser Date: Thu, 17 Oct 2024 16:41:08 -0400 Subject: [PATCH 62/72] Annotation layer styling --- .../components/viewer/AnnotationLayer.svelte | 63 ++++++++++++------- src/lib/components/viewer/Note.svelte | 4 +- src/lib/components/viewer/NoteTab.svelte | 12 ++-- .../components/viewer/ViewerContext.svelte | 9 +-- src/style/kit.css | 7 ++- 5 files changed, 60 insertions(+), 35 deletions(-) diff --git a/src/lib/components/viewer/AnnotationLayer.svelte b/src/lib/components/viewer/AnnotationLayer.svelte index 248d89e0f..6c631535b 100644 --- a/src/lib/components/viewer/AnnotationLayer.svelte +++ b/src/lib/components/viewer/AnnotationLayer.svelte @@ -31,7 +31,7 @@ Assumes it's a child of a ViewerContext } from "$lib/components/viewer/ViewerContext.svelte"; import { getNotes, getViewerHref } from "$lib/utils/viewer"; import Note from "./Note.svelte"; - import { setContext } from "svelte"; + import { fly } from "svelte/transition"; export let scale = 1.5; export let page_number: number; // zero-indexed @@ -69,7 +69,6 @@ Assumes it's a child of a ViewerContext function startDrawingBox(e: PointerEvent) { if ($currentNote || newNote) return; - console.log("startDrawingBox"); drawing = true; $currentNote = null; @@ -82,7 +81,6 @@ Assumes it's a child of a ViewerContext y1: y, y2: y, }; - console.log(`drawing: ${drawing}`, newNote); } function continueDrawingBox(e: PointerEvent) { @@ -170,13 +168,13 @@ Assumes it's a child of a ViewerContext {#each notes as note (note.id)} openNote(e, note)} > - + {#if note.id === $currentNote?.id} -
{#if writing} -
+
{:else} - +
+ +
{/if} {/if} {/each} @@ -218,7 +211,11 @@ Assumes it's a child of a ViewerContext >
{#if !drawing} -
+
-
+ +
+