@papyrus-sdk/core 0.2.10 → 0.2.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +5 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +55 -9
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +55 -9
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -82,6 +82,11 @@ declare class SearchService {
|
|
|
82
82
|
private engine;
|
|
83
83
|
constructor(engine: DocumentEngine);
|
|
84
84
|
search(query: string): Promise<SearchResult[]>;
|
|
85
|
+
private normalizeForSearch;
|
|
86
|
+
private normalizeWithMap;
|
|
87
|
+
private stripMarkup;
|
|
88
|
+
private decodeHtmlEntities;
|
|
89
|
+
private normalizeWhitespace;
|
|
85
90
|
}
|
|
86
91
|
|
|
87
92
|
declare class PapyrusEventEmitter {
|
package/dist/index.d.ts
CHANGED
|
@@ -82,6 +82,11 @@ declare class SearchService {
|
|
|
82
82
|
private engine;
|
|
83
83
|
constructor(engine: DocumentEngine);
|
|
84
84
|
search(query: string): Promise<SearchResult[]>;
|
|
85
|
+
private normalizeForSearch;
|
|
86
|
+
private normalizeWithMap;
|
|
87
|
+
private stripMarkup;
|
|
88
|
+
private decodeHtmlEntities;
|
|
89
|
+
private normalizeWhitespace;
|
|
85
90
|
}
|
|
86
91
|
|
|
87
92
|
declare class PapyrusEventEmitter {
|
package/dist/index.js
CHANGED
|
@@ -251,27 +251,73 @@ var SearchService = class {
|
|
|
251
251
|
this.engine = engine;
|
|
252
252
|
}
|
|
253
253
|
async search(query) {
|
|
254
|
-
|
|
254
|
+
const trimmedQuery = query.trim();
|
|
255
|
+
if (!trimmedQuery || trimmedQuery.length < 2) return [];
|
|
256
|
+
const normalizedQuery = this.normalizeForSearch(trimmedQuery);
|
|
257
|
+
if (!normalizedQuery) return [];
|
|
255
258
|
if (typeof this.engine.searchText === "function") {
|
|
256
|
-
return await this.engine.searchText(
|
|
259
|
+
return await this.engine.searchText(trimmedQuery);
|
|
257
260
|
}
|
|
258
261
|
const results = [];
|
|
259
262
|
const pageCount = this.engine.getPageCount();
|
|
260
|
-
const normalizedQuery = query.toLowerCase();
|
|
261
263
|
for (let i = 0; i < pageCount; i++) {
|
|
262
264
|
const textContent = await this.engine.getTextContent(i);
|
|
263
|
-
const
|
|
264
|
-
|
|
265
|
+
const rawPageText = textContent.map((item) => item.str).join(" ");
|
|
266
|
+
const cleanPageText = this.normalizeWhitespace(
|
|
267
|
+
this.decodeHtmlEntities(this.stripMarkup(rawPageText))
|
|
268
|
+
);
|
|
269
|
+
if (!cleanPageText) continue;
|
|
270
|
+
const { normalized, map } = this.normalizeWithMap(cleanPageText);
|
|
271
|
+
if (!normalized) continue;
|
|
272
|
+
let pos = normalized.indexOf(normalizedQuery, 0);
|
|
265
273
|
let matchIndex = 0;
|
|
266
274
|
while (pos !== -1) {
|
|
267
|
-
const
|
|
268
|
-
const
|
|
269
|
-
|
|
270
|
-
|
|
275
|
+
const anchor = map[pos] ?? pos;
|
|
276
|
+
const start = Math.max(0, anchor - 40);
|
|
277
|
+
const end = Math.min(
|
|
278
|
+
cleanPageText.length,
|
|
279
|
+
anchor + trimmedQuery.length + 40
|
|
280
|
+
);
|
|
281
|
+
results.push({
|
|
282
|
+
pageIndex: i,
|
|
283
|
+
text: cleanPageText.substring(start, end),
|
|
284
|
+
matchIndex: matchIndex++
|
|
285
|
+
});
|
|
286
|
+
pos = normalized.indexOf(normalizedQuery, pos + 1);
|
|
271
287
|
}
|
|
272
288
|
}
|
|
273
289
|
return results;
|
|
274
290
|
}
|
|
291
|
+
normalizeForSearch(value) {
|
|
292
|
+
return value.toLocaleLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "");
|
|
293
|
+
}
|
|
294
|
+
normalizeWithMap(value) {
|
|
295
|
+
let normalized = "";
|
|
296
|
+
const map = [];
|
|
297
|
+
for (let i = 0; i < value.length; i += 1) {
|
|
298
|
+
const next = this.normalizeForSearch(value[i]);
|
|
299
|
+
if (!next) continue;
|
|
300
|
+
normalized += next;
|
|
301
|
+
for (let j = 0; j < next.length; j += 1) {
|
|
302
|
+
map.push(i);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
return { normalized, map };
|
|
306
|
+
}
|
|
307
|
+
stripMarkup(value) {
|
|
308
|
+
if (!value) return "";
|
|
309
|
+
return value.replace(/<script[\s\S]*?<\/script>/gi, " ").replace(/<style[\s\S]*?<\/style>/gi, " ").replace(/<[^>]+>/g, " ");
|
|
310
|
+
}
|
|
311
|
+
decodeHtmlEntities(value) {
|
|
312
|
+
if (!value) return "";
|
|
313
|
+
if (typeof document === "undefined") return value;
|
|
314
|
+
const textArea = document.createElement("textarea");
|
|
315
|
+
textArea.innerHTML = value;
|
|
316
|
+
return textArea.value;
|
|
317
|
+
}
|
|
318
|
+
normalizeWhitespace(value) {
|
|
319
|
+
return value.replace(/\s+/g, " ").trim();
|
|
320
|
+
}
|
|
275
321
|
};
|
|
276
322
|
// Annotate the CommonJS export names for ESM import in node:
|
|
277
323
|
0 && (module.exports = {
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../index.ts","../engine.ts","../store.ts","../services/event-emitter.ts","../services/search-service.ts"],"sourcesContent":["\nexport * from './engine';\nexport * from './store';\nexport * from './services/search-service';\nexport * from './services/event-emitter';\n","\r\nimport { DocumentEngine, TextItem, OutlineItem, DocumentLoadInput, TextSelection, PageDestination } from '@papyrus-sdk/types';\r\n\r\nexport abstract class BaseDocumentEngine implements DocumentEngine {\r\n abstract load(source: DocumentLoadInput): Promise<void>;\r\n abstract getPageCount(): number;\r\n abstract getCurrentPage(): number;\r\n abstract goToPage(page: number): void;\r\n abstract setZoom(zoom: number): void;\r\n abstract getZoom(): number;\r\n abstract rotate(direction: 'clockwise' | 'counterclockwise'): void;\r\n abstract getRotation(): number;\r\n abstract renderPage(pageIndex: number, target: any, scale: number): Promise<void>;\r\n abstract renderTextLayer(pageIndex: number, container: any, scale: number): Promise<void>;\r\n abstract getTextContent(pageIndex: number): Promise<TextItem[]>;\r\n abstract getPageDimensions(pageIndex: number): Promise<{ width: number, height: number }>;\r\n abstract selectText(pageIndex: number, rect: { x: number; y: number; width: number; height: number }): Promise<TextSelection | null>;\r\n abstract getOutline(): Promise<OutlineItem[]>;\r\n abstract getPageIndex(dest: PageDestination): Promise<number | null>;\r\n abstract destroy(): void;\r\n}\r\n","import { create } from \"zustand\";\nimport {\n ViewMode,\n Annotation,\n AnnotationReply,\n SearchResult,\n UITheme,\n PageTheme,\n OutlineItem,\n PapyrusEventType,\n PapyrusConfig,\n Locale,\n} from \"@papyrus-sdk/types\";\nimport { papyrusEvents } from \"./services/event-emitter\";\n\ninterface ViewerState {\n isLoaded: boolean;\n pageCount: number;\n currentPage: number;\n zoom: number;\n rotation: number;\n viewMode: ViewMode;\n uiTheme: UITheme;\n pageTheme: PageTheme;\n locale: Locale;\n accentColor: string;\n annotationColor: string;\n outline: OutlineItem[];\n sidebarLeftOpen: boolean;\n sidebarLeftTab: \"thumbnails\" | \"summary\";\n outlineSearchQuery: string;\n sidebarRightOpen: boolean;\n sidebarRightTab: \"search\" | \"annotations\" | \"pages\";\n searchQuery: string;\n searchResults: SearchResult[];\n activeSearchIndex: number;\n scrollToPageSignal: number | null;\n annotations: Annotation[];\n activeTool:\n | \"select\"\n | \"highlight\"\n | \"underline\"\n | \"squiggly\"\n | \"strikeout\"\n | \"text\"\n | \"comment\"\n | \"ink\";\n selectedAnnotationId: string | null;\n interactionMode: \"pan\" | \"select\";\n selectionActive: boolean;\n toolDockOpen: boolean;\n\n initializeStore: (config: PapyrusConfig) => void;\n setDocumentState: (state: Partial<ViewerState>) => void;\n toggleSidebarLeft: () => void;\n setSidebarLeftTab: (tab: \"thumbnails\" | \"summary\") => void;\n setOutlineSearch: (query: string) => void;\n toggleSidebarRight: (tab?: \"search\" | \"annotations\" | \"pages\") => void;\n addAnnotation: (annotation: Annotation) => void;\n updateAnnotation: (id: string, updates: Partial<Annotation>) => void;\n addAnnotationReply: (annotationId: string, content: string) => void;\n removeAnnotation: (id: string) => void;\n setSelectedAnnotation: (id: string | null) => void;\n setSearch: (query: string, results: SearchResult[]) => void;\n nextSearchResult: () => void;\n prevSearchResult: () => void;\n triggerScrollToPage: (pageIndex: number) => void;\n setAnnotationColor: (color: string) => void;\n setInteractionMode: (mode: \"pan\" | \"select\") => void;\n setSelectionActive: (active: boolean) => void;\n setAccentColor: (color: string) => void;\n}\n\nconst getDefaultViewerState = () => ({\n isLoaded: false,\n pageCount: 0,\n currentPage: 1,\n zoom: 1.0,\n rotation: 0,\n viewMode: \"continuous\" as ViewMode,\n uiTheme: \"light\" as UITheme,\n pageTheme: \"normal\" as PageTheme,\n locale: \"en\" as Locale,\n accentColor: \"#2563eb\",\n annotationColor: \"#fbbf24\",\n outline: [] as OutlineItem[],\n sidebarLeftOpen: true,\n sidebarLeftTab: \"thumbnails\" as const,\n outlineSearchQuery: \"\",\n sidebarRightOpen: false,\n sidebarRightTab: \"search\" as const,\n searchQuery: \"\",\n searchResults: [] as SearchResult[],\n activeSearchIndex: -1,\n scrollToPageSignal: null as number | null,\n annotations: [] as Annotation[],\n activeTool: \"select\" as const,\n selectedAnnotationId: null as string | null,\n interactionMode: \"pan\" as const,\n selectionActive: false,\n toolDockOpen: false,\n});\n\nexport const useViewerStore = create<ViewerState>((set, get) => ({\n ...getDefaultViewerState(),\n\n initializeStore: (config) =>\n set(() => {\n const defaults = getDefaultViewerState();\n return {\n ...defaults,\n currentPage: config.initialPage ?? defaults.currentPage,\n zoom: config.initialZoom ?? defaults.zoom,\n rotation: config.initialRotation ?? defaults.rotation,\n viewMode: config.initialViewMode ?? defaults.viewMode,\n uiTheme: config.initialUITheme ?? defaults.uiTheme,\n pageTheme: config.initialPageTheme ?? defaults.pageTheme,\n locale: config.initialLocale ?? defaults.locale,\n accentColor: config.initialAccentColor ?? defaults.accentColor,\n annotations: config.initialAnnotations ?? defaults.annotations,\n sidebarLeftOpen: config.sidebarLeftOpen ?? defaults.sidebarLeftOpen,\n sidebarRightOpen: config.sidebarRightOpen ?? defaults.sidebarRightOpen,\n };\n }),\n\n setDocumentState: (state) => {\n const oldPage = get().currentPage;\n const oldZoom = get().zoom;\n\n set((prev) => ({ ...prev, ...state }));\n\n if (state.currentPage !== undefined && state.currentPage !== oldPage) {\n papyrusEvents.emit(PapyrusEventType.PAGE_CHANGED, {\n pageNumber: state.currentPage,\n });\n }\n if (state.zoom !== undefined && state.zoom !== oldZoom) {\n papyrusEvents.emit(PapyrusEventType.ZOOM_CHANGED, { zoom: state.zoom });\n }\n if (state.isLoaded === true) {\n papyrusEvents.emit(PapyrusEventType.DOCUMENT_LOADED, {\n pageCount: get().pageCount,\n });\n }\n },\n\n toggleSidebarLeft: () =>\n set((state) => ({ sidebarLeftOpen: !state.sidebarLeftOpen })),\n setSidebarLeftTab: (tab) => set({ sidebarLeftTab: tab }),\n setOutlineSearch: (query) => set({ outlineSearchQuery: query }),\n toggleSidebarRight: (tab) =>\n set((state) => ({\n sidebarRightOpen: tab ? true : !state.sidebarRightOpen,\n sidebarRightTab: tab || state.sidebarRightTab,\n })),\n setAnnotationColor: (color) => set({ annotationColor: color }),\n\n addAnnotation: (ann) => {\n const shouldAutoSelect = ann.type === \"text\" || ann.type === \"comment\";\n set((state) => ({\n annotations: [...state.annotations, ann],\n selectedAnnotationId: shouldAutoSelect ? ann.id : state.selectedAnnotationId,\n }));\n papyrusEvents.emit(PapyrusEventType.ANNOTATION_CREATED, {\n annotation: ann,\n });\n },\n\n updateAnnotation: (id, updates) => {\n let updatedAnnotation: Annotation | null = null;\n set((state) => ({\n annotations: state.annotations.map((a) => {\n if (a.id !== id) return a;\n updatedAnnotation = {\n ...a,\n ...updates,\n updatedAt: Date.now(),\n };\n return updatedAnnotation;\n }),\n }));\n if (updatedAnnotation) {\n papyrusEvents.emit(PapyrusEventType.ANNOTATION_UPDATED, {\n annotation: updatedAnnotation,\n });\n }\n },\n\n addAnnotationReply: (annotationId, content) => {\n const nextContent = content.trim();\n if (!nextContent) return;\n\n const reply: AnnotationReply = {\n id: Math.random().toString(36).slice(2, 11),\n annotationId,\n content: nextContent,\n createdAt: Date.now(),\n };\n\n let updatedAnnotation: Annotation | null = null;\n set((state) => ({\n annotations: state.annotations.map((a) => {\n if (a.id !== annotationId) return a;\n const replies = [...(a.replies ?? []), reply];\n updatedAnnotation = {\n ...a,\n replies,\n updatedAt: Date.now(),\n };\n return updatedAnnotation;\n }),\n }));\n\n if (updatedAnnotation) {\n papyrusEvents.emit(PapyrusEventType.ANNOTATION_REPLY_ADDED, {\n annotationId,\n reply,\n annotation: updatedAnnotation,\n });\n papyrusEvents.emit(PapyrusEventType.ANNOTATION_UPDATED, {\n annotation: updatedAnnotation,\n });\n }\n },\n\n removeAnnotation: (id) => {\n set((state) => ({\n annotations: state.annotations.filter((a) => a.id !== id),\n selectedAnnotationId:\n state.selectedAnnotationId === id ? null : state.selectedAnnotationId,\n }));\n papyrusEvents.emit(PapyrusEventType.ANNOTATION_DELETED, {\n annotationId: id,\n });\n },\n\n setSelectedAnnotation: (id) => set({ selectedAnnotationId: id }),\n setInteractionMode: (mode) => set({ interactionMode: mode }),\n setSelectionActive: (active) => set({ selectionActive: active }),\n setAccentColor: (color) => set({ accentColor: color }),\n\n setSearch: (query, results) => {\n set({\n searchQuery: query,\n searchResults: results,\n activeSearchIndex: results.length > 0 ? 0 : -1,\n });\n papyrusEvents.emit(PapyrusEventType.SEARCH_TRIGGERED, { query });\n },\n\n nextSearchResult: () => {\n const state = get();\n if (state.searchResults.length === 0) return;\n const nextIndex =\n (state.activeSearchIndex + 1) % state.searchResults.length;\n const pageIndex = state.searchResults[nextIndex].pageIndex;\n set({\n activeSearchIndex: nextIndex,\n scrollToPageSignal: pageIndex,\n currentPage: pageIndex + 1,\n });\n },\n\n prevSearchResult: () => {\n const state = get();\n if (state.searchResults.length === 0) return;\n const prevIndex =\n (state.activeSearchIndex - 1 + state.searchResults.length) %\n state.searchResults.length;\n const pageIndex = state.searchResults[prevIndex].pageIndex;\n set({\n activeSearchIndex: prevIndex,\n scrollToPageSignal: pageIndex,\n currentPage: pageIndex + 1,\n });\n },\n\n triggerScrollToPage: (pageIndex) =>\n set({ scrollToPageSignal: pageIndex, currentPage: pageIndex + 1 }),\n}));\n","\r\nimport { PapyrusEventType, PapyrusEventListener, EventPayloads } from '@papyrus-sdk/types';\r\n\r\nexport class PapyrusEventEmitter {\r\n private listeners: Map<PapyrusEventType, Set<PapyrusEventListener<any>>> = new Map();\r\n\r\n on<T extends PapyrusEventType>(event: T, listener: PapyrusEventListener<T>): () => void {\r\n if (!this.listeners.has(event)) {\r\n this.listeners.set(event, new Set());\r\n }\r\n this.listeners.get(event)!.add(listener);\r\n\r\n // Return an unsubscribe function\r\n return () => this.off(event, listener);\r\n }\r\n\r\n off<T extends PapyrusEventType>(event: T, listener: PapyrusEventListener<T>): void {\r\n const eventListeners = this.listeners.get(event);\r\n if (eventListeners) {\r\n eventListeners.delete(listener);\r\n }\r\n }\r\n\r\n emit<T extends PapyrusEventType>(event: T, payload: EventPayloads[T]): void {\r\n const eventListeners = this.listeners.get(event);\r\n if (eventListeners) {\r\n eventListeners.forEach((listener) => listener(payload));\r\n }\r\n }\r\n}\r\n\r\n// Singleton instance for the core package\r\nexport const papyrusEvents = new PapyrusEventEmitter();\r\n","\r\nimport { DocumentEngine, SearchResult } from '@papyrus-sdk/types';\r\n\r\nexport class SearchService {\r\n private engine: DocumentEngine;\r\n constructor(engine: DocumentEngine) { this.engine = engine; }\r\n\r\n async search(query: string): Promise<SearchResult[]> {\r\n if (!query || query.length < 2) return [];\r\n if (typeof this.engine.searchText === 'function') {\r\n return await this.engine.searchText(query);\r\n }\r\n const results: SearchResult[] = [];\r\n const pageCount = this.engine.getPageCount();\r\n const normalizedQuery = query.toLowerCase();\r\n\r\n for (let i = 0; i < pageCount; i++) {\r\n const textContent = await this.engine.getTextContent(i);\r\n const fullPageText = textContent.map(item => item.str).join(' ');\r\n let pos = fullPageText.toLowerCase().indexOf(normalizedQuery, 0);\r\n let matchIndex = 0;\r\n while (pos !== -1) {\r\n const start = Math.max(0, pos - 20);\r\n const end = Math.min(fullPageText.length, pos + query.length + 20);\r\n results.push({ pageIndex: i, text: fullPageText.substring(start, end), matchIndex: matchIndex++ });\r\n pos = fullPageText.toLowerCase().indexOf(normalizedQuery, pos + 1);\r\n }\r\n }\r\n return results;\r\n }\r\n}\r\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACGO,IAAe,qBAAf,MAA4D;AAiBnE;;;ACpBA,qBAAuB;AACvB,mBAWO;;;ACTA,IAAM,sBAAN,MAA0B;AAAA,EAA1B;AACL,SAAQ,YAAmE,oBAAI,IAAI;AAAA;AAAA,EAEnF,GAA+B,OAAU,UAA+C;AACtF,QAAI,CAAC,KAAK,UAAU,IAAI,KAAK,GAAG;AAC9B,WAAK,UAAU,IAAI,OAAO,oBAAI,IAAI,CAAC;AAAA,IACrC;AACA,SAAK,UAAU,IAAI,KAAK,EAAG,IAAI,QAAQ;AAGvC,WAAO,MAAM,KAAK,IAAI,OAAO,QAAQ;AAAA,EACvC;AAAA,EAEA,IAAgC,OAAU,UAAyC;AACjF,UAAM,iBAAiB,KAAK,UAAU,IAAI,KAAK;AAC/C,QAAI,gBAAgB;AAClB,qBAAe,OAAO,QAAQ;AAAA,IAChC;AAAA,EACF;AAAA,EAEA,KAAiC,OAAU,SAAiC;AAC1E,UAAM,iBAAiB,KAAK,UAAU,IAAI,KAAK;AAC/C,QAAI,gBAAgB;AAClB,qBAAe,QAAQ,CAAC,aAAa,SAAS,OAAO,CAAC;AAAA,IACxD;AAAA,EACF;AACF;AAGO,IAAM,gBAAgB,IAAI,oBAAoB;;;ADyCrD,IAAM,wBAAwB,OAAO;AAAA,EACnC,UAAU;AAAA,EACV,WAAW;AAAA,EACX,aAAa;AAAA,EACb,MAAM;AAAA,EACN,UAAU;AAAA,EACV,UAAU;AAAA,EACV,SAAS;AAAA,EACT,WAAW;AAAA,EACX,QAAQ;AAAA,EACR,aAAa;AAAA,EACb,iBAAiB;AAAA,EACjB,SAAS,CAAC;AAAA,EACV,iBAAiB;AAAA,EACjB,gBAAgB;AAAA,EAChB,oBAAoB;AAAA,EACpB,kBAAkB;AAAA,EAClB,iBAAiB;AAAA,EACjB,aAAa;AAAA,EACb,eAAe,CAAC;AAAA,EAChB,mBAAmB;AAAA,EACnB,oBAAoB;AAAA,EACpB,aAAa,CAAC;AAAA,EACd,YAAY;AAAA,EACZ,sBAAsB;AAAA,EACtB,iBAAiB;AAAA,EACjB,iBAAiB;AAAA,EACjB,cAAc;AAChB;AAEO,IAAM,qBAAiB,uBAAoB,CAAC,KAAK,SAAS;AAAA,EAC/D,GAAG,sBAAsB;AAAA,EAEzB,iBAAiB,CAAC,WAChB,IAAI,MAAM;AACR,UAAM,WAAW,sBAAsB;AACvC,WAAO;AAAA,MACL,GAAG;AAAA,MACH,aAAa,OAAO,eAAe,SAAS;AAAA,MAC5C,MAAM,OAAO,eAAe,SAAS;AAAA,MACrC,UAAU,OAAO,mBAAmB,SAAS;AAAA,MAC7C,UAAU,OAAO,mBAAmB,SAAS;AAAA,MAC7C,SAAS,OAAO,kBAAkB,SAAS;AAAA,MAC3C,WAAW,OAAO,oBAAoB,SAAS;AAAA,MAC/C,QAAQ,OAAO,iBAAiB,SAAS;AAAA,MACzC,aAAa,OAAO,sBAAsB,SAAS;AAAA,MACnD,aAAa,OAAO,sBAAsB,SAAS;AAAA,MACnD,iBAAiB,OAAO,mBAAmB,SAAS;AAAA,MACpD,kBAAkB,OAAO,oBAAoB,SAAS;AAAA,IACxD;AAAA,EACF,CAAC;AAAA,EAEH,kBAAkB,CAAC,UAAU;AAC3B,UAAM,UAAU,IAAI,EAAE;AACtB,UAAM,UAAU,IAAI,EAAE;AAEtB,QAAI,CAAC,UAAU,EAAE,GAAG,MAAM,GAAG,MAAM,EAAE;AAErC,QAAI,MAAM,gBAAgB,UAAa,MAAM,gBAAgB,SAAS;AACpE,oBAAc,KAAK,8BAAiB,cAAc;AAAA,QAChD,YAAY,MAAM;AAAA,MACpB,CAAC;AAAA,IACH;AACA,QAAI,MAAM,SAAS,UAAa,MAAM,SAAS,SAAS;AACtD,oBAAc,KAAK,8BAAiB,cAAc,EAAE,MAAM,MAAM,KAAK,CAAC;AAAA,IACxE;AACA,QAAI,MAAM,aAAa,MAAM;AAC3B,oBAAc,KAAK,8BAAiB,iBAAiB;AAAA,QACnD,WAAW,IAAI,EAAE;AAAA,MACnB,CAAC;AAAA,IACH;AAAA,EACF;AAAA,EAEA,mBAAmB,MACjB,IAAI,CAAC,WAAW,EAAE,iBAAiB,CAAC,MAAM,gBAAgB,EAAE;AAAA,EAC9D,mBAAmB,CAAC,QAAQ,IAAI,EAAE,gBAAgB,IAAI,CAAC;AAAA,EACvD,kBAAkB,CAAC,UAAU,IAAI,EAAE,oBAAoB,MAAM,CAAC;AAAA,EAC9D,oBAAoB,CAAC,QACnB,IAAI,CAAC,WAAW;AAAA,IACd,kBAAkB,MAAM,OAAO,CAAC,MAAM;AAAA,IACtC,iBAAiB,OAAO,MAAM;AAAA,EAChC,EAAE;AAAA,EACJ,oBAAoB,CAAC,UAAU,IAAI,EAAE,iBAAiB,MAAM,CAAC;AAAA,EAE7D,eAAe,CAAC,QAAQ;AACtB,UAAM,mBAAmB,IAAI,SAAS,UAAU,IAAI,SAAS;AAC7D,QAAI,CAAC,WAAW;AAAA,MACd,aAAa,CAAC,GAAG,MAAM,aAAa,GAAG;AAAA,MACvC,sBAAsB,mBAAmB,IAAI,KAAK,MAAM;AAAA,IAC1D,EAAE;AACF,kBAAc,KAAK,8BAAiB,oBAAoB;AAAA,MACtD,YAAY;AAAA,IACd,CAAC;AAAA,EACH;AAAA,EAEA,kBAAkB,CAAC,IAAI,YAAY;AACjC,QAAI,oBAAuC;AAC3C,QAAI,CAAC,WAAW;AAAA,MACd,aAAa,MAAM,YAAY,IAAI,CAAC,MAAM;AACxC,YAAI,EAAE,OAAO,GAAI,QAAO;AACxB,4BAAoB;AAAA,UAClB,GAAG;AAAA,UACH,GAAG;AAAA,UACH,WAAW,KAAK,IAAI;AAAA,QACtB;AACA,eAAO;AAAA,MACT,CAAC;AAAA,IACH,EAAE;AACF,QAAI,mBAAmB;AACrB,oBAAc,KAAK,8BAAiB,oBAAoB;AAAA,QACtD,YAAY;AAAA,MACd,CAAC;AAAA,IACH;AAAA,EACF;AAAA,EAEA,oBAAoB,CAAC,cAAc,YAAY;AAC7C,UAAM,cAAc,QAAQ,KAAK;AACjC,QAAI,CAAC,YAAa;AAElB,UAAM,QAAyB;AAAA,MAC7B,IAAI,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,GAAG,EAAE;AAAA,MAC1C;AAAA,MACA,SAAS;AAAA,MACT,WAAW,KAAK,IAAI;AAAA,IACtB;AAEA,QAAI,oBAAuC;AAC3C,QAAI,CAAC,WAAW;AAAA,MACd,aAAa,MAAM,YAAY,IAAI,CAAC,MAAM;AACxC,YAAI,EAAE,OAAO,aAAc,QAAO;AAClC,cAAM,UAAU,CAAC,GAAI,EAAE,WAAW,CAAC,GAAI,KAAK;AAC5C,4BAAoB;AAAA,UAClB,GAAG;AAAA,UACH;AAAA,UACA,WAAW,KAAK,IAAI;AAAA,QACtB;AACA,eAAO;AAAA,MACT,CAAC;AAAA,IACH,EAAE;AAEF,QAAI,mBAAmB;AACrB,oBAAc,KAAK,8BAAiB,wBAAwB;AAAA,QAC1D;AAAA,QACA;AAAA,QACA,YAAY;AAAA,MACd,CAAC;AACD,oBAAc,KAAK,8BAAiB,oBAAoB;AAAA,QACtD,YAAY;AAAA,MACd,CAAC;AAAA,IACH;AAAA,EACF;AAAA,EAEA,kBAAkB,CAAC,OAAO;AACxB,QAAI,CAAC,WAAW;AAAA,MACd,aAAa,MAAM,YAAY,OAAO,CAAC,MAAM,EAAE,OAAO,EAAE;AAAA,MACxD,sBACE,MAAM,yBAAyB,KAAK,OAAO,MAAM;AAAA,IACrD,EAAE;AACF,kBAAc,KAAK,8BAAiB,oBAAoB;AAAA,MACtD,cAAc;AAAA,IAChB,CAAC;AAAA,EACH;AAAA,EAEA,uBAAuB,CAAC,OAAO,IAAI,EAAE,sBAAsB,GAAG,CAAC;AAAA,EAC/D,oBAAoB,CAAC,SAAS,IAAI,EAAE,iBAAiB,KAAK,CAAC;AAAA,EAC3D,oBAAoB,CAAC,WAAW,IAAI,EAAE,iBAAiB,OAAO,CAAC;AAAA,EAC/D,gBAAgB,CAAC,UAAU,IAAI,EAAE,aAAa,MAAM,CAAC;AAAA,EAErD,WAAW,CAAC,OAAO,YAAY;AAC7B,QAAI;AAAA,MACF,aAAa;AAAA,MACb,eAAe;AAAA,MACf,mBAAmB,QAAQ,SAAS,IAAI,IAAI;AAAA,IAC9C,CAAC;AACD,kBAAc,KAAK,8BAAiB,kBAAkB,EAAE,MAAM,CAAC;AAAA,EACjE;AAAA,EAEA,kBAAkB,MAAM;AACtB,UAAM,QAAQ,IAAI;AAClB,QAAI,MAAM,cAAc,WAAW,EAAG;AACtC,UAAM,aACH,MAAM,oBAAoB,KAAK,MAAM,cAAc;AACtD,UAAM,YAAY,MAAM,cAAc,SAAS,EAAE;AACjD,QAAI;AAAA,MACF,mBAAmB;AAAA,MACnB,oBAAoB;AAAA,MACpB,aAAa,YAAY;AAAA,IAC3B,CAAC;AAAA,EACH;AAAA,EAEA,kBAAkB,MAAM;AACtB,UAAM,QAAQ,IAAI;AAClB,QAAI,MAAM,cAAc,WAAW,EAAG;AACtC,UAAM,aACH,MAAM,oBAAoB,IAAI,MAAM,cAAc,UACnD,MAAM,cAAc;AACtB,UAAM,YAAY,MAAM,cAAc,SAAS,EAAE;AACjD,QAAI;AAAA,MACF,mBAAmB;AAAA,MACnB,oBAAoB;AAAA,MACpB,aAAa,YAAY;AAAA,IAC3B,CAAC;AAAA,EACH;AAAA,EAEA,qBAAqB,CAAC,cACpB,IAAI,EAAE,oBAAoB,WAAW,aAAa,YAAY,EAAE,CAAC;AACrE,EAAE;;;AEpRK,IAAM,gBAAN,MAAoB;AAAA,EAEzB,YAAY,QAAwB;AAAE,SAAK,SAAS;AAAA,EAAQ;AAAA,EAE5D,MAAM,OAAO,OAAwC;AACnD,QAAI,CAAC,SAAS,MAAM,SAAS,EAAG,QAAO,CAAC;AACxC,QAAI,OAAO,KAAK,OAAO,eAAe,YAAY;AAChD,aAAO,MAAM,KAAK,OAAO,WAAW,KAAK;AAAA,IAC3C;AACA,UAAM,UAA0B,CAAC;AACjC,UAAM,YAAY,KAAK,OAAO,aAAa;AAC3C,UAAM,kBAAkB,MAAM,YAAY;AAE1C,aAAS,IAAI,GAAG,IAAI,WAAW,KAAK;AAClC,YAAM,cAAc,MAAM,KAAK,OAAO,eAAe,CAAC;AACtD,YAAM,eAAe,YAAY,IAAI,UAAQ,KAAK,GAAG,EAAE,KAAK,GAAG;AAC/D,UAAI,MAAM,aAAa,YAAY,EAAE,QAAQ,iBAAiB,CAAC;AAC/D,UAAI,aAAa;AACjB,aAAO,QAAQ,IAAI;AACjB,cAAM,QAAQ,KAAK,IAAI,GAAG,MAAM,EAAE;AAClC,cAAM,MAAM,KAAK,IAAI,aAAa,QAAQ,MAAM,MAAM,SAAS,EAAE;AACjE,gBAAQ,KAAK,EAAE,WAAW,GAAG,MAAM,aAAa,UAAU,OAAO,GAAG,GAAG,YAAY,aAAa,CAAC;AACjG,cAAM,aAAa,YAAY,EAAE,QAAQ,iBAAiB,MAAM,CAAC;AAAA,MACnE;AAAA,IACF;AACA,WAAO;AAAA,EACT;AACF;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../index.ts","../engine.ts","../store.ts","../services/event-emitter.ts","../services/search-service.ts"],"sourcesContent":["\nexport * from './engine';\nexport * from './store';\nexport * from './services/search-service';\nexport * from './services/event-emitter';\n","\r\nimport { DocumentEngine, TextItem, OutlineItem, DocumentLoadInput, TextSelection, PageDestination } from '@papyrus-sdk/types';\r\n\r\nexport abstract class BaseDocumentEngine implements DocumentEngine {\r\n abstract load(source: DocumentLoadInput): Promise<void>;\r\n abstract getPageCount(): number;\r\n abstract getCurrentPage(): number;\r\n abstract goToPage(page: number): void;\r\n abstract setZoom(zoom: number): void;\r\n abstract getZoom(): number;\r\n abstract rotate(direction: 'clockwise' | 'counterclockwise'): void;\r\n abstract getRotation(): number;\r\n abstract renderPage(pageIndex: number, target: any, scale: number): Promise<void>;\r\n abstract renderTextLayer(pageIndex: number, container: any, scale: number): Promise<void>;\r\n abstract getTextContent(pageIndex: number): Promise<TextItem[]>;\r\n abstract getPageDimensions(pageIndex: number): Promise<{ width: number, height: number }>;\r\n abstract selectText(pageIndex: number, rect: { x: number; y: number; width: number; height: number }): Promise<TextSelection | null>;\r\n abstract getOutline(): Promise<OutlineItem[]>;\r\n abstract getPageIndex(dest: PageDestination): Promise<number | null>;\r\n abstract destroy(): void;\r\n}\r\n","import { create } from \"zustand\";\nimport {\n ViewMode,\n Annotation,\n AnnotationReply,\n SearchResult,\n UITheme,\n PageTheme,\n OutlineItem,\n PapyrusEventType,\n PapyrusConfig,\n Locale,\n} from \"@papyrus-sdk/types\";\nimport { papyrusEvents } from \"./services/event-emitter\";\n\ninterface ViewerState {\n isLoaded: boolean;\n pageCount: number;\n currentPage: number;\n zoom: number;\n rotation: number;\n viewMode: ViewMode;\n uiTheme: UITheme;\n pageTheme: PageTheme;\n locale: Locale;\n accentColor: string;\n annotationColor: string;\n outline: OutlineItem[];\n sidebarLeftOpen: boolean;\n sidebarLeftTab: \"thumbnails\" | \"summary\";\n outlineSearchQuery: string;\n sidebarRightOpen: boolean;\n sidebarRightTab: \"search\" | \"annotations\" | \"pages\";\n searchQuery: string;\n searchResults: SearchResult[];\n activeSearchIndex: number;\n scrollToPageSignal: number | null;\n annotations: Annotation[];\n activeTool:\n | \"select\"\n | \"highlight\"\n | \"underline\"\n | \"squiggly\"\n | \"strikeout\"\n | \"text\"\n | \"comment\"\n | \"ink\";\n selectedAnnotationId: string | null;\n interactionMode: \"pan\" | \"select\";\n selectionActive: boolean;\n toolDockOpen: boolean;\n\n initializeStore: (config: PapyrusConfig) => void;\n setDocumentState: (state: Partial<ViewerState>) => void;\n toggleSidebarLeft: () => void;\n setSidebarLeftTab: (tab: \"thumbnails\" | \"summary\") => void;\n setOutlineSearch: (query: string) => void;\n toggleSidebarRight: (tab?: \"search\" | \"annotations\" | \"pages\") => void;\n addAnnotation: (annotation: Annotation) => void;\n updateAnnotation: (id: string, updates: Partial<Annotation>) => void;\n addAnnotationReply: (annotationId: string, content: string) => void;\n removeAnnotation: (id: string) => void;\n setSelectedAnnotation: (id: string | null) => void;\n setSearch: (query: string, results: SearchResult[]) => void;\n nextSearchResult: () => void;\n prevSearchResult: () => void;\n triggerScrollToPage: (pageIndex: number) => void;\n setAnnotationColor: (color: string) => void;\n setInteractionMode: (mode: \"pan\" | \"select\") => void;\n setSelectionActive: (active: boolean) => void;\n setAccentColor: (color: string) => void;\n}\n\nconst getDefaultViewerState = () => ({\n isLoaded: false,\n pageCount: 0,\n currentPage: 1,\n zoom: 1.0,\n rotation: 0,\n viewMode: \"continuous\" as ViewMode,\n uiTheme: \"light\" as UITheme,\n pageTheme: \"normal\" as PageTheme,\n locale: \"en\" as Locale,\n accentColor: \"#2563eb\",\n annotationColor: \"#fbbf24\",\n outline: [] as OutlineItem[],\n sidebarLeftOpen: true,\n sidebarLeftTab: \"thumbnails\" as const,\n outlineSearchQuery: \"\",\n sidebarRightOpen: false,\n sidebarRightTab: \"search\" as const,\n searchQuery: \"\",\n searchResults: [] as SearchResult[],\n activeSearchIndex: -1,\n scrollToPageSignal: null as number | null,\n annotations: [] as Annotation[],\n activeTool: \"select\" as const,\n selectedAnnotationId: null as string | null,\n interactionMode: \"pan\" as const,\n selectionActive: false,\n toolDockOpen: false,\n});\n\nexport const useViewerStore = create<ViewerState>((set, get) => ({\n ...getDefaultViewerState(),\n\n initializeStore: (config) =>\n set(() => {\n const defaults = getDefaultViewerState();\n return {\n ...defaults,\n currentPage: config.initialPage ?? defaults.currentPage,\n zoom: config.initialZoom ?? defaults.zoom,\n rotation: config.initialRotation ?? defaults.rotation,\n viewMode: config.initialViewMode ?? defaults.viewMode,\n uiTheme: config.initialUITheme ?? defaults.uiTheme,\n pageTheme: config.initialPageTheme ?? defaults.pageTheme,\n locale: config.initialLocale ?? defaults.locale,\n accentColor: config.initialAccentColor ?? defaults.accentColor,\n annotations: config.initialAnnotations ?? defaults.annotations,\n sidebarLeftOpen: config.sidebarLeftOpen ?? defaults.sidebarLeftOpen,\n sidebarRightOpen: config.sidebarRightOpen ?? defaults.sidebarRightOpen,\n };\n }),\n\n setDocumentState: (state) => {\n const oldPage = get().currentPage;\n const oldZoom = get().zoom;\n\n set((prev) => ({ ...prev, ...state }));\n\n if (state.currentPage !== undefined && state.currentPage !== oldPage) {\n papyrusEvents.emit(PapyrusEventType.PAGE_CHANGED, {\n pageNumber: state.currentPage,\n });\n }\n if (state.zoom !== undefined && state.zoom !== oldZoom) {\n papyrusEvents.emit(PapyrusEventType.ZOOM_CHANGED, { zoom: state.zoom });\n }\n if (state.isLoaded === true) {\n papyrusEvents.emit(PapyrusEventType.DOCUMENT_LOADED, {\n pageCount: get().pageCount,\n });\n }\n },\n\n toggleSidebarLeft: () =>\n set((state) => ({ sidebarLeftOpen: !state.sidebarLeftOpen })),\n setSidebarLeftTab: (tab) => set({ sidebarLeftTab: tab }),\n setOutlineSearch: (query) => set({ outlineSearchQuery: query }),\n toggleSidebarRight: (tab) =>\n set((state) => ({\n sidebarRightOpen: tab ? true : !state.sidebarRightOpen,\n sidebarRightTab: tab || state.sidebarRightTab,\n })),\n setAnnotationColor: (color) => set({ annotationColor: color }),\n\n addAnnotation: (ann) => {\n const shouldAutoSelect = ann.type === \"text\" || ann.type === \"comment\";\n set((state) => ({\n annotations: [...state.annotations, ann],\n selectedAnnotationId: shouldAutoSelect ? ann.id : state.selectedAnnotationId,\n }));\n papyrusEvents.emit(PapyrusEventType.ANNOTATION_CREATED, {\n annotation: ann,\n });\n },\n\n updateAnnotation: (id, updates) => {\n let updatedAnnotation: Annotation | null = null;\n set((state) => ({\n annotations: state.annotations.map((a) => {\n if (a.id !== id) return a;\n updatedAnnotation = {\n ...a,\n ...updates,\n updatedAt: Date.now(),\n };\n return updatedAnnotation;\n }),\n }));\n if (updatedAnnotation) {\n papyrusEvents.emit(PapyrusEventType.ANNOTATION_UPDATED, {\n annotation: updatedAnnotation,\n });\n }\n },\n\n addAnnotationReply: (annotationId, content) => {\n const nextContent = content.trim();\n if (!nextContent) return;\n\n const reply: AnnotationReply = {\n id: Math.random().toString(36).slice(2, 11),\n annotationId,\n content: nextContent,\n createdAt: Date.now(),\n };\n\n let updatedAnnotation: Annotation | null = null;\n set((state) => ({\n annotations: state.annotations.map((a) => {\n if (a.id !== annotationId) return a;\n const replies = [...(a.replies ?? []), reply];\n updatedAnnotation = {\n ...a,\n replies,\n updatedAt: Date.now(),\n };\n return updatedAnnotation;\n }),\n }));\n\n if (updatedAnnotation) {\n papyrusEvents.emit(PapyrusEventType.ANNOTATION_REPLY_ADDED, {\n annotationId,\n reply,\n annotation: updatedAnnotation,\n });\n papyrusEvents.emit(PapyrusEventType.ANNOTATION_UPDATED, {\n annotation: updatedAnnotation,\n });\n }\n },\n\n removeAnnotation: (id) => {\n set((state) => ({\n annotations: state.annotations.filter((a) => a.id !== id),\n selectedAnnotationId:\n state.selectedAnnotationId === id ? null : state.selectedAnnotationId,\n }));\n papyrusEvents.emit(PapyrusEventType.ANNOTATION_DELETED, {\n annotationId: id,\n });\n },\n\n setSelectedAnnotation: (id) => set({ selectedAnnotationId: id }),\n setInteractionMode: (mode) => set({ interactionMode: mode }),\n setSelectionActive: (active) => set({ selectionActive: active }),\n setAccentColor: (color) => set({ accentColor: color }),\n\n setSearch: (query, results) => {\n set({\n searchQuery: query,\n searchResults: results,\n activeSearchIndex: results.length > 0 ? 0 : -1,\n });\n papyrusEvents.emit(PapyrusEventType.SEARCH_TRIGGERED, { query });\n },\n\n nextSearchResult: () => {\n const state = get();\n if (state.searchResults.length === 0) return;\n const nextIndex =\n (state.activeSearchIndex + 1) % state.searchResults.length;\n const pageIndex = state.searchResults[nextIndex].pageIndex;\n set({\n activeSearchIndex: nextIndex,\n scrollToPageSignal: pageIndex,\n currentPage: pageIndex + 1,\n });\n },\n\n prevSearchResult: () => {\n const state = get();\n if (state.searchResults.length === 0) return;\n const prevIndex =\n (state.activeSearchIndex - 1 + state.searchResults.length) %\n state.searchResults.length;\n const pageIndex = state.searchResults[prevIndex].pageIndex;\n set({\n activeSearchIndex: prevIndex,\n scrollToPageSignal: pageIndex,\n currentPage: pageIndex + 1,\n });\n },\n\n triggerScrollToPage: (pageIndex) =>\n set({ scrollToPageSignal: pageIndex, currentPage: pageIndex + 1 }),\n}));\n","\r\nimport { PapyrusEventType, PapyrusEventListener, EventPayloads } from '@papyrus-sdk/types';\r\n\r\nexport class PapyrusEventEmitter {\r\n private listeners: Map<PapyrusEventType, Set<PapyrusEventListener<any>>> = new Map();\r\n\r\n on<T extends PapyrusEventType>(event: T, listener: PapyrusEventListener<T>): () => void {\r\n if (!this.listeners.has(event)) {\r\n this.listeners.set(event, new Set());\r\n }\r\n this.listeners.get(event)!.add(listener);\r\n\r\n // Return an unsubscribe function\r\n return () => this.off(event, listener);\r\n }\r\n\r\n off<T extends PapyrusEventType>(event: T, listener: PapyrusEventListener<T>): void {\r\n const eventListeners = this.listeners.get(event);\r\n if (eventListeners) {\r\n eventListeners.delete(listener);\r\n }\r\n }\r\n\r\n emit<T extends PapyrusEventType>(event: T, payload: EventPayloads[T]): void {\r\n const eventListeners = this.listeners.get(event);\r\n if (eventListeners) {\r\n eventListeners.forEach((listener) => listener(payload));\r\n }\r\n }\r\n}\r\n\r\n// Singleton instance for the core package\r\nexport const papyrusEvents = new PapyrusEventEmitter();\r\n","import { DocumentEngine, SearchResult } from \"@papyrus-sdk/types\";\n\nexport class SearchService {\n private engine: DocumentEngine;\n constructor(engine: DocumentEngine) {\n this.engine = engine;\n }\n\n async search(query: string): Promise<SearchResult[]> {\n const trimmedQuery = query.trim();\n if (!trimmedQuery || trimmedQuery.length < 2) return [];\n const normalizedQuery = this.normalizeForSearch(trimmedQuery);\n if (!normalizedQuery) return [];\n\n if (typeof this.engine.searchText === \"function\") {\n return await this.engine.searchText(trimmedQuery);\n }\n\n const results: SearchResult[] = [];\n const pageCount = this.engine.getPageCount();\n\n for (let i = 0; i < pageCount; i++) {\n const textContent = await this.engine.getTextContent(i);\n const rawPageText = textContent.map((item) => item.str).join(\" \");\n const cleanPageText = this.normalizeWhitespace(\n this.decodeHtmlEntities(this.stripMarkup(rawPageText))\n );\n if (!cleanPageText) continue;\n\n const { normalized, map } = this.normalizeWithMap(cleanPageText);\n if (!normalized) continue;\n\n let pos = normalized.indexOf(normalizedQuery, 0);\n let matchIndex = 0;\n while (pos !== -1) {\n const anchor = map[pos] ?? pos;\n const start = Math.max(0, anchor - 40);\n const end = Math.min(\n cleanPageText.length,\n anchor + trimmedQuery.length + 40\n );\n results.push({\n pageIndex: i,\n text: cleanPageText.substring(start, end),\n matchIndex: matchIndex++,\n });\n pos = normalized.indexOf(normalizedQuery, pos + 1);\n }\n }\n\n return results;\n }\n\n private normalizeForSearch(value: string): string {\n return value\n .toLocaleLowerCase()\n .normalize(\"NFD\")\n .replace(/[\\u0300-\\u036f]/g, \"\");\n }\n\n private normalizeWithMap(value: string): {\n normalized: string;\n map: number[];\n } {\n let normalized = \"\";\n const map: number[] = [];\n\n for (let i = 0; i < value.length; i += 1) {\n const next = this.normalizeForSearch(value[i]);\n if (!next) continue;\n normalized += next;\n for (let j = 0; j < next.length; j += 1) {\n map.push(i);\n }\n }\n\n return { normalized, map };\n }\n\n private stripMarkup(value: string): string {\n if (!value) return \"\";\n return value\n .replace(/<script[\\s\\S]*?<\\/script>/gi, \" \")\n .replace(/<style[\\s\\S]*?<\\/style>/gi, \" \")\n .replace(/<[^>]+>/g, \" \");\n }\n\n private decodeHtmlEntities(value: string): string {\n if (!value) return \"\";\n if (typeof document === \"undefined\") return value;\n\n const textArea = document.createElement(\"textarea\");\n textArea.innerHTML = value;\n return textArea.value;\n }\n\n private normalizeWhitespace(value: string): string {\n return value.replace(/\\s+/g, \" \").trim();\n }\n}\n"],"mappings":"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","names":[]}
|
package/dist/index.mjs
CHANGED
|
@@ -224,27 +224,73 @@ var SearchService = class {
|
|
|
224
224
|
this.engine = engine;
|
|
225
225
|
}
|
|
226
226
|
async search(query) {
|
|
227
|
-
|
|
227
|
+
const trimmedQuery = query.trim();
|
|
228
|
+
if (!trimmedQuery || trimmedQuery.length < 2) return [];
|
|
229
|
+
const normalizedQuery = this.normalizeForSearch(trimmedQuery);
|
|
230
|
+
if (!normalizedQuery) return [];
|
|
228
231
|
if (typeof this.engine.searchText === "function") {
|
|
229
|
-
return await this.engine.searchText(
|
|
232
|
+
return await this.engine.searchText(trimmedQuery);
|
|
230
233
|
}
|
|
231
234
|
const results = [];
|
|
232
235
|
const pageCount = this.engine.getPageCount();
|
|
233
|
-
const normalizedQuery = query.toLowerCase();
|
|
234
236
|
for (let i = 0; i < pageCount; i++) {
|
|
235
237
|
const textContent = await this.engine.getTextContent(i);
|
|
236
|
-
const
|
|
237
|
-
|
|
238
|
+
const rawPageText = textContent.map((item) => item.str).join(" ");
|
|
239
|
+
const cleanPageText = this.normalizeWhitespace(
|
|
240
|
+
this.decodeHtmlEntities(this.stripMarkup(rawPageText))
|
|
241
|
+
);
|
|
242
|
+
if (!cleanPageText) continue;
|
|
243
|
+
const { normalized, map } = this.normalizeWithMap(cleanPageText);
|
|
244
|
+
if (!normalized) continue;
|
|
245
|
+
let pos = normalized.indexOf(normalizedQuery, 0);
|
|
238
246
|
let matchIndex = 0;
|
|
239
247
|
while (pos !== -1) {
|
|
240
|
-
const
|
|
241
|
-
const
|
|
242
|
-
|
|
243
|
-
|
|
248
|
+
const anchor = map[pos] ?? pos;
|
|
249
|
+
const start = Math.max(0, anchor - 40);
|
|
250
|
+
const end = Math.min(
|
|
251
|
+
cleanPageText.length,
|
|
252
|
+
anchor + trimmedQuery.length + 40
|
|
253
|
+
);
|
|
254
|
+
results.push({
|
|
255
|
+
pageIndex: i,
|
|
256
|
+
text: cleanPageText.substring(start, end),
|
|
257
|
+
matchIndex: matchIndex++
|
|
258
|
+
});
|
|
259
|
+
pos = normalized.indexOf(normalizedQuery, pos + 1);
|
|
244
260
|
}
|
|
245
261
|
}
|
|
246
262
|
return results;
|
|
247
263
|
}
|
|
264
|
+
normalizeForSearch(value) {
|
|
265
|
+
return value.toLocaleLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "");
|
|
266
|
+
}
|
|
267
|
+
normalizeWithMap(value) {
|
|
268
|
+
let normalized = "";
|
|
269
|
+
const map = [];
|
|
270
|
+
for (let i = 0; i < value.length; i += 1) {
|
|
271
|
+
const next = this.normalizeForSearch(value[i]);
|
|
272
|
+
if (!next) continue;
|
|
273
|
+
normalized += next;
|
|
274
|
+
for (let j = 0; j < next.length; j += 1) {
|
|
275
|
+
map.push(i);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
return { normalized, map };
|
|
279
|
+
}
|
|
280
|
+
stripMarkup(value) {
|
|
281
|
+
if (!value) return "";
|
|
282
|
+
return value.replace(/<script[\s\S]*?<\/script>/gi, " ").replace(/<style[\s\S]*?<\/style>/gi, " ").replace(/<[^>]+>/g, " ");
|
|
283
|
+
}
|
|
284
|
+
decodeHtmlEntities(value) {
|
|
285
|
+
if (!value) return "";
|
|
286
|
+
if (typeof document === "undefined") return value;
|
|
287
|
+
const textArea = document.createElement("textarea");
|
|
288
|
+
textArea.innerHTML = value;
|
|
289
|
+
return textArea.value;
|
|
290
|
+
}
|
|
291
|
+
normalizeWhitespace(value) {
|
|
292
|
+
return value.replace(/\s+/g, " ").trim();
|
|
293
|
+
}
|
|
248
294
|
};
|
|
249
295
|
export {
|
|
250
296
|
BaseDocumentEngine,
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../engine.ts","../store.ts","../services/event-emitter.ts","../services/search-service.ts"],"sourcesContent":["\r\nimport { DocumentEngine, TextItem, OutlineItem, DocumentLoadInput, TextSelection, PageDestination } from '@papyrus-sdk/types';\r\n\r\nexport abstract class BaseDocumentEngine implements DocumentEngine {\r\n abstract load(source: DocumentLoadInput): Promise<void>;\r\n abstract getPageCount(): number;\r\n abstract getCurrentPage(): number;\r\n abstract goToPage(page: number): void;\r\n abstract setZoom(zoom: number): void;\r\n abstract getZoom(): number;\r\n abstract rotate(direction: 'clockwise' | 'counterclockwise'): void;\r\n abstract getRotation(): number;\r\n abstract renderPage(pageIndex: number, target: any, scale: number): Promise<void>;\r\n abstract renderTextLayer(pageIndex: number, container: any, scale: number): Promise<void>;\r\n abstract getTextContent(pageIndex: number): Promise<TextItem[]>;\r\n abstract getPageDimensions(pageIndex: number): Promise<{ width: number, height: number }>;\r\n abstract selectText(pageIndex: number, rect: { x: number; y: number; width: number; height: number }): Promise<TextSelection | null>;\r\n abstract getOutline(): Promise<OutlineItem[]>;\r\n abstract getPageIndex(dest: PageDestination): Promise<number | null>;\r\n abstract destroy(): void;\r\n}\r\n","import { create } from \"zustand\";\nimport {\n ViewMode,\n Annotation,\n AnnotationReply,\n SearchResult,\n UITheme,\n PageTheme,\n OutlineItem,\n PapyrusEventType,\n PapyrusConfig,\n Locale,\n} from \"@papyrus-sdk/types\";\nimport { papyrusEvents } from \"./services/event-emitter\";\n\ninterface ViewerState {\n isLoaded: boolean;\n pageCount: number;\n currentPage: number;\n zoom: number;\n rotation: number;\n viewMode: ViewMode;\n uiTheme: UITheme;\n pageTheme: PageTheme;\n locale: Locale;\n accentColor: string;\n annotationColor: string;\n outline: OutlineItem[];\n sidebarLeftOpen: boolean;\n sidebarLeftTab: \"thumbnails\" | \"summary\";\n outlineSearchQuery: string;\n sidebarRightOpen: boolean;\n sidebarRightTab: \"search\" | \"annotations\" | \"pages\";\n searchQuery: string;\n searchResults: SearchResult[];\n activeSearchIndex: number;\n scrollToPageSignal: number | null;\n annotations: Annotation[];\n activeTool:\n | \"select\"\n | \"highlight\"\n | \"underline\"\n | \"squiggly\"\n | \"strikeout\"\n | \"text\"\n | \"comment\"\n | \"ink\";\n selectedAnnotationId: string | null;\n interactionMode: \"pan\" | \"select\";\n selectionActive: boolean;\n toolDockOpen: boolean;\n\n initializeStore: (config: PapyrusConfig) => void;\n setDocumentState: (state: Partial<ViewerState>) => void;\n toggleSidebarLeft: () => void;\n setSidebarLeftTab: (tab: \"thumbnails\" | \"summary\") => void;\n setOutlineSearch: (query: string) => void;\n toggleSidebarRight: (tab?: \"search\" | \"annotations\" | \"pages\") => void;\n addAnnotation: (annotation: Annotation) => void;\n updateAnnotation: (id: string, updates: Partial<Annotation>) => void;\n addAnnotationReply: (annotationId: string, content: string) => void;\n removeAnnotation: (id: string) => void;\n setSelectedAnnotation: (id: string | null) => void;\n setSearch: (query: string, results: SearchResult[]) => void;\n nextSearchResult: () => void;\n prevSearchResult: () => void;\n triggerScrollToPage: (pageIndex: number) => void;\n setAnnotationColor: (color: string) => void;\n setInteractionMode: (mode: \"pan\" | \"select\") => void;\n setSelectionActive: (active: boolean) => void;\n setAccentColor: (color: string) => void;\n}\n\nconst getDefaultViewerState = () => ({\n isLoaded: false,\n pageCount: 0,\n currentPage: 1,\n zoom: 1.0,\n rotation: 0,\n viewMode: \"continuous\" as ViewMode,\n uiTheme: \"light\" as UITheme,\n pageTheme: \"normal\" as PageTheme,\n locale: \"en\" as Locale,\n accentColor: \"#2563eb\",\n annotationColor: \"#fbbf24\",\n outline: [] as OutlineItem[],\n sidebarLeftOpen: true,\n sidebarLeftTab: \"thumbnails\" as const,\n outlineSearchQuery: \"\",\n sidebarRightOpen: false,\n sidebarRightTab: \"search\" as const,\n searchQuery: \"\",\n searchResults: [] as SearchResult[],\n activeSearchIndex: -1,\n scrollToPageSignal: null as number | null,\n annotations: [] as Annotation[],\n activeTool: \"select\" as const,\n selectedAnnotationId: null as string | null,\n interactionMode: \"pan\" as const,\n selectionActive: false,\n toolDockOpen: false,\n});\n\nexport const useViewerStore = create<ViewerState>((set, get) => ({\n ...getDefaultViewerState(),\n\n initializeStore: (config) =>\n set(() => {\n const defaults = getDefaultViewerState();\n return {\n ...defaults,\n currentPage: config.initialPage ?? defaults.currentPage,\n zoom: config.initialZoom ?? defaults.zoom,\n rotation: config.initialRotation ?? defaults.rotation,\n viewMode: config.initialViewMode ?? defaults.viewMode,\n uiTheme: config.initialUITheme ?? defaults.uiTheme,\n pageTheme: config.initialPageTheme ?? defaults.pageTheme,\n locale: config.initialLocale ?? defaults.locale,\n accentColor: config.initialAccentColor ?? defaults.accentColor,\n annotations: config.initialAnnotations ?? defaults.annotations,\n sidebarLeftOpen: config.sidebarLeftOpen ?? defaults.sidebarLeftOpen,\n sidebarRightOpen: config.sidebarRightOpen ?? defaults.sidebarRightOpen,\n };\n }),\n\n setDocumentState: (state) => {\n const oldPage = get().currentPage;\n const oldZoom = get().zoom;\n\n set((prev) => ({ ...prev, ...state }));\n\n if (state.currentPage !== undefined && state.currentPage !== oldPage) {\n papyrusEvents.emit(PapyrusEventType.PAGE_CHANGED, {\n pageNumber: state.currentPage,\n });\n }\n if (state.zoom !== undefined && state.zoom !== oldZoom) {\n papyrusEvents.emit(PapyrusEventType.ZOOM_CHANGED, { zoom: state.zoom });\n }\n if (state.isLoaded === true) {\n papyrusEvents.emit(PapyrusEventType.DOCUMENT_LOADED, {\n pageCount: get().pageCount,\n });\n }\n },\n\n toggleSidebarLeft: () =>\n set((state) => ({ sidebarLeftOpen: !state.sidebarLeftOpen })),\n setSidebarLeftTab: (tab) => set({ sidebarLeftTab: tab }),\n setOutlineSearch: (query) => set({ outlineSearchQuery: query }),\n toggleSidebarRight: (tab) =>\n set((state) => ({\n sidebarRightOpen: tab ? true : !state.sidebarRightOpen,\n sidebarRightTab: tab || state.sidebarRightTab,\n })),\n setAnnotationColor: (color) => set({ annotationColor: color }),\n\n addAnnotation: (ann) => {\n const shouldAutoSelect = ann.type === \"text\" || ann.type === \"comment\";\n set((state) => ({\n annotations: [...state.annotations, ann],\n selectedAnnotationId: shouldAutoSelect ? ann.id : state.selectedAnnotationId,\n }));\n papyrusEvents.emit(PapyrusEventType.ANNOTATION_CREATED, {\n annotation: ann,\n });\n },\n\n updateAnnotation: (id, updates) => {\n let updatedAnnotation: Annotation | null = null;\n set((state) => ({\n annotations: state.annotations.map((a) => {\n if (a.id !== id) return a;\n updatedAnnotation = {\n ...a,\n ...updates,\n updatedAt: Date.now(),\n };\n return updatedAnnotation;\n }),\n }));\n if (updatedAnnotation) {\n papyrusEvents.emit(PapyrusEventType.ANNOTATION_UPDATED, {\n annotation: updatedAnnotation,\n });\n }\n },\n\n addAnnotationReply: (annotationId, content) => {\n const nextContent = content.trim();\n if (!nextContent) return;\n\n const reply: AnnotationReply = {\n id: Math.random().toString(36).slice(2, 11),\n annotationId,\n content: nextContent,\n createdAt: Date.now(),\n };\n\n let updatedAnnotation: Annotation | null = null;\n set((state) => ({\n annotations: state.annotations.map((a) => {\n if (a.id !== annotationId) return a;\n const replies = [...(a.replies ?? []), reply];\n updatedAnnotation = {\n ...a,\n replies,\n updatedAt: Date.now(),\n };\n return updatedAnnotation;\n }),\n }));\n\n if (updatedAnnotation) {\n papyrusEvents.emit(PapyrusEventType.ANNOTATION_REPLY_ADDED, {\n annotationId,\n reply,\n annotation: updatedAnnotation,\n });\n papyrusEvents.emit(PapyrusEventType.ANNOTATION_UPDATED, {\n annotation: updatedAnnotation,\n });\n }\n },\n\n removeAnnotation: (id) => {\n set((state) => ({\n annotations: state.annotations.filter((a) => a.id !== id),\n selectedAnnotationId:\n state.selectedAnnotationId === id ? null : state.selectedAnnotationId,\n }));\n papyrusEvents.emit(PapyrusEventType.ANNOTATION_DELETED, {\n annotationId: id,\n });\n },\n\n setSelectedAnnotation: (id) => set({ selectedAnnotationId: id }),\n setInteractionMode: (mode) => set({ interactionMode: mode }),\n setSelectionActive: (active) => set({ selectionActive: active }),\n setAccentColor: (color) => set({ accentColor: color }),\n\n setSearch: (query, results) => {\n set({\n searchQuery: query,\n searchResults: results,\n activeSearchIndex: results.length > 0 ? 0 : -1,\n });\n papyrusEvents.emit(PapyrusEventType.SEARCH_TRIGGERED, { query });\n },\n\n nextSearchResult: () => {\n const state = get();\n if (state.searchResults.length === 0) return;\n const nextIndex =\n (state.activeSearchIndex + 1) % state.searchResults.length;\n const pageIndex = state.searchResults[nextIndex].pageIndex;\n set({\n activeSearchIndex: nextIndex,\n scrollToPageSignal: pageIndex,\n currentPage: pageIndex + 1,\n });\n },\n\n prevSearchResult: () => {\n const state = get();\n if (state.searchResults.length === 0) return;\n const prevIndex =\n (state.activeSearchIndex - 1 + state.searchResults.length) %\n state.searchResults.length;\n const pageIndex = state.searchResults[prevIndex].pageIndex;\n set({\n activeSearchIndex: prevIndex,\n scrollToPageSignal: pageIndex,\n currentPage: pageIndex + 1,\n });\n },\n\n triggerScrollToPage: (pageIndex) =>\n set({ scrollToPageSignal: pageIndex, currentPage: pageIndex + 1 }),\n}));\n","\r\nimport { PapyrusEventType, PapyrusEventListener, EventPayloads } from '@papyrus-sdk/types';\r\n\r\nexport class PapyrusEventEmitter {\r\n private listeners: Map<PapyrusEventType, Set<PapyrusEventListener<any>>> = new Map();\r\n\r\n on<T extends PapyrusEventType>(event: T, listener: PapyrusEventListener<T>): () => void {\r\n if (!this.listeners.has(event)) {\r\n this.listeners.set(event, new Set());\r\n }\r\n this.listeners.get(event)!.add(listener);\r\n\r\n // Return an unsubscribe function\r\n return () => this.off(event, listener);\r\n }\r\n\r\n off<T extends PapyrusEventType>(event: T, listener: PapyrusEventListener<T>): void {\r\n const eventListeners = this.listeners.get(event);\r\n if (eventListeners) {\r\n eventListeners.delete(listener);\r\n }\r\n }\r\n\r\n emit<T extends PapyrusEventType>(event: T, payload: EventPayloads[T]): void {\r\n const eventListeners = this.listeners.get(event);\r\n if (eventListeners) {\r\n eventListeners.forEach((listener) => listener(payload));\r\n }\r\n }\r\n}\r\n\r\n// Singleton instance for the core package\r\nexport const papyrusEvents = new PapyrusEventEmitter();\r\n","\r\nimport { DocumentEngine, SearchResult } from '@papyrus-sdk/types';\r\n\r\nexport class SearchService {\r\n private engine: DocumentEngine;\r\n constructor(engine: DocumentEngine) { this.engine = engine; }\r\n\r\n async search(query: string): Promise<SearchResult[]> {\r\n if (!query || query.length < 2) return [];\r\n if (typeof this.engine.searchText === 'function') {\r\n return await this.engine.searchText(query);\r\n }\r\n const results: SearchResult[] = [];\r\n const pageCount = this.engine.getPageCount();\r\n const normalizedQuery = query.toLowerCase();\r\n\r\n for (let i = 0; i < pageCount; i++) {\r\n const textContent = await this.engine.getTextContent(i);\r\n const fullPageText = textContent.map(item => item.str).join(' ');\r\n let pos = fullPageText.toLowerCase().indexOf(normalizedQuery, 0);\r\n let matchIndex = 0;\r\n while (pos !== -1) {\r\n const start = Math.max(0, pos - 20);\r\n const end = Math.min(fullPageText.length, pos + query.length + 20);\r\n results.push({ pageIndex: i, text: fullPageText.substring(start, end), matchIndex: matchIndex++ });\r\n pos = fullPageText.toLowerCase().indexOf(normalizedQuery, pos + 1);\r\n }\r\n }\r\n return results;\r\n }\r\n}\r\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../engine.ts","../store.ts","../services/event-emitter.ts","../services/search-service.ts"],"sourcesContent":["\r\nimport { DocumentEngine, TextItem, OutlineItem, DocumentLoadInput, TextSelection, PageDestination } from '@papyrus-sdk/types';\r\n\r\nexport abstract class BaseDocumentEngine implements DocumentEngine {\r\n abstract load(source: DocumentLoadInput): Promise<void>;\r\n abstract getPageCount(): number;\r\n abstract getCurrentPage(): number;\r\n abstract goToPage(page: number): void;\r\n abstract setZoom(zoom: number): void;\r\n abstract getZoom(): number;\r\n abstract rotate(direction: 'clockwise' | 'counterclockwise'): void;\r\n abstract getRotation(): number;\r\n abstract renderPage(pageIndex: number, target: any, scale: number): Promise<void>;\r\n abstract renderTextLayer(pageIndex: number, container: any, scale: number): Promise<void>;\r\n abstract getTextContent(pageIndex: number): Promise<TextItem[]>;\r\n abstract getPageDimensions(pageIndex: number): Promise<{ width: number, height: number }>;\r\n abstract selectText(pageIndex: number, rect: { x: number; y: number; width: number; height: number }): Promise<TextSelection | null>;\r\n abstract getOutline(): Promise<OutlineItem[]>;\r\n abstract getPageIndex(dest: PageDestination): Promise<number | null>;\r\n abstract destroy(): void;\r\n}\r\n","import { create } from \"zustand\";\nimport {\n ViewMode,\n Annotation,\n AnnotationReply,\n SearchResult,\n UITheme,\n PageTheme,\n OutlineItem,\n PapyrusEventType,\n PapyrusConfig,\n Locale,\n} from \"@papyrus-sdk/types\";\nimport { papyrusEvents } from \"./services/event-emitter\";\n\ninterface ViewerState {\n isLoaded: boolean;\n pageCount: number;\n currentPage: number;\n zoom: number;\n rotation: number;\n viewMode: ViewMode;\n uiTheme: UITheme;\n pageTheme: PageTheme;\n locale: Locale;\n accentColor: string;\n annotationColor: string;\n outline: OutlineItem[];\n sidebarLeftOpen: boolean;\n sidebarLeftTab: \"thumbnails\" | \"summary\";\n outlineSearchQuery: string;\n sidebarRightOpen: boolean;\n sidebarRightTab: \"search\" | \"annotations\" | \"pages\";\n searchQuery: string;\n searchResults: SearchResult[];\n activeSearchIndex: number;\n scrollToPageSignal: number | null;\n annotations: Annotation[];\n activeTool:\n | \"select\"\n | \"highlight\"\n | \"underline\"\n | \"squiggly\"\n | \"strikeout\"\n | \"text\"\n | \"comment\"\n | \"ink\";\n selectedAnnotationId: string | null;\n interactionMode: \"pan\" | \"select\";\n selectionActive: boolean;\n toolDockOpen: boolean;\n\n initializeStore: (config: PapyrusConfig) => void;\n setDocumentState: (state: Partial<ViewerState>) => void;\n toggleSidebarLeft: () => void;\n setSidebarLeftTab: (tab: \"thumbnails\" | \"summary\") => void;\n setOutlineSearch: (query: string) => void;\n toggleSidebarRight: (tab?: \"search\" | \"annotations\" | \"pages\") => void;\n addAnnotation: (annotation: Annotation) => void;\n updateAnnotation: (id: string, updates: Partial<Annotation>) => void;\n addAnnotationReply: (annotationId: string, content: string) => void;\n removeAnnotation: (id: string) => void;\n setSelectedAnnotation: (id: string | null) => void;\n setSearch: (query: string, results: SearchResult[]) => void;\n nextSearchResult: () => void;\n prevSearchResult: () => void;\n triggerScrollToPage: (pageIndex: number) => void;\n setAnnotationColor: (color: string) => void;\n setInteractionMode: (mode: \"pan\" | \"select\") => void;\n setSelectionActive: (active: boolean) => void;\n setAccentColor: (color: string) => void;\n}\n\nconst getDefaultViewerState = () => ({\n isLoaded: false,\n pageCount: 0,\n currentPage: 1,\n zoom: 1.0,\n rotation: 0,\n viewMode: \"continuous\" as ViewMode,\n uiTheme: \"light\" as UITheme,\n pageTheme: \"normal\" as PageTheme,\n locale: \"en\" as Locale,\n accentColor: \"#2563eb\",\n annotationColor: \"#fbbf24\",\n outline: [] as OutlineItem[],\n sidebarLeftOpen: true,\n sidebarLeftTab: \"thumbnails\" as const,\n outlineSearchQuery: \"\",\n sidebarRightOpen: false,\n sidebarRightTab: \"search\" as const,\n searchQuery: \"\",\n searchResults: [] as SearchResult[],\n activeSearchIndex: -1,\n scrollToPageSignal: null as number | null,\n annotations: [] as Annotation[],\n activeTool: \"select\" as const,\n selectedAnnotationId: null as string | null,\n interactionMode: \"pan\" as const,\n selectionActive: false,\n toolDockOpen: false,\n});\n\nexport const useViewerStore = create<ViewerState>((set, get) => ({\n ...getDefaultViewerState(),\n\n initializeStore: (config) =>\n set(() => {\n const defaults = getDefaultViewerState();\n return {\n ...defaults,\n currentPage: config.initialPage ?? defaults.currentPage,\n zoom: config.initialZoom ?? defaults.zoom,\n rotation: config.initialRotation ?? defaults.rotation,\n viewMode: config.initialViewMode ?? defaults.viewMode,\n uiTheme: config.initialUITheme ?? defaults.uiTheme,\n pageTheme: config.initialPageTheme ?? defaults.pageTheme,\n locale: config.initialLocale ?? defaults.locale,\n accentColor: config.initialAccentColor ?? defaults.accentColor,\n annotations: config.initialAnnotations ?? defaults.annotations,\n sidebarLeftOpen: config.sidebarLeftOpen ?? defaults.sidebarLeftOpen,\n sidebarRightOpen: config.sidebarRightOpen ?? defaults.sidebarRightOpen,\n };\n }),\n\n setDocumentState: (state) => {\n const oldPage = get().currentPage;\n const oldZoom = get().zoom;\n\n set((prev) => ({ ...prev, ...state }));\n\n if (state.currentPage !== undefined && state.currentPage !== oldPage) {\n papyrusEvents.emit(PapyrusEventType.PAGE_CHANGED, {\n pageNumber: state.currentPage,\n });\n }\n if (state.zoom !== undefined && state.zoom !== oldZoom) {\n papyrusEvents.emit(PapyrusEventType.ZOOM_CHANGED, { zoom: state.zoom });\n }\n if (state.isLoaded === true) {\n papyrusEvents.emit(PapyrusEventType.DOCUMENT_LOADED, {\n pageCount: get().pageCount,\n });\n }\n },\n\n toggleSidebarLeft: () =>\n set((state) => ({ sidebarLeftOpen: !state.sidebarLeftOpen })),\n setSidebarLeftTab: (tab) => set({ sidebarLeftTab: tab }),\n setOutlineSearch: (query) => set({ outlineSearchQuery: query }),\n toggleSidebarRight: (tab) =>\n set((state) => ({\n sidebarRightOpen: tab ? true : !state.sidebarRightOpen,\n sidebarRightTab: tab || state.sidebarRightTab,\n })),\n setAnnotationColor: (color) => set({ annotationColor: color }),\n\n addAnnotation: (ann) => {\n const shouldAutoSelect = ann.type === \"text\" || ann.type === \"comment\";\n set((state) => ({\n annotations: [...state.annotations, ann],\n selectedAnnotationId: shouldAutoSelect ? ann.id : state.selectedAnnotationId,\n }));\n papyrusEvents.emit(PapyrusEventType.ANNOTATION_CREATED, {\n annotation: ann,\n });\n },\n\n updateAnnotation: (id, updates) => {\n let updatedAnnotation: Annotation | null = null;\n set((state) => ({\n annotations: state.annotations.map((a) => {\n if (a.id !== id) return a;\n updatedAnnotation = {\n ...a,\n ...updates,\n updatedAt: Date.now(),\n };\n return updatedAnnotation;\n }),\n }));\n if (updatedAnnotation) {\n papyrusEvents.emit(PapyrusEventType.ANNOTATION_UPDATED, {\n annotation: updatedAnnotation,\n });\n }\n },\n\n addAnnotationReply: (annotationId, content) => {\n const nextContent = content.trim();\n if (!nextContent) return;\n\n const reply: AnnotationReply = {\n id: Math.random().toString(36).slice(2, 11),\n annotationId,\n content: nextContent,\n createdAt: Date.now(),\n };\n\n let updatedAnnotation: Annotation | null = null;\n set((state) => ({\n annotations: state.annotations.map((a) => {\n if (a.id !== annotationId) return a;\n const replies = [...(a.replies ?? []), reply];\n updatedAnnotation = {\n ...a,\n replies,\n updatedAt: Date.now(),\n };\n return updatedAnnotation;\n }),\n }));\n\n if (updatedAnnotation) {\n papyrusEvents.emit(PapyrusEventType.ANNOTATION_REPLY_ADDED, {\n annotationId,\n reply,\n annotation: updatedAnnotation,\n });\n papyrusEvents.emit(PapyrusEventType.ANNOTATION_UPDATED, {\n annotation: updatedAnnotation,\n });\n }\n },\n\n removeAnnotation: (id) => {\n set((state) => ({\n annotations: state.annotations.filter((a) => a.id !== id),\n selectedAnnotationId:\n state.selectedAnnotationId === id ? null : state.selectedAnnotationId,\n }));\n papyrusEvents.emit(PapyrusEventType.ANNOTATION_DELETED, {\n annotationId: id,\n });\n },\n\n setSelectedAnnotation: (id) => set({ selectedAnnotationId: id }),\n setInteractionMode: (mode) => set({ interactionMode: mode }),\n setSelectionActive: (active) => set({ selectionActive: active }),\n setAccentColor: (color) => set({ accentColor: color }),\n\n setSearch: (query, results) => {\n set({\n searchQuery: query,\n searchResults: results,\n activeSearchIndex: results.length > 0 ? 0 : -1,\n });\n papyrusEvents.emit(PapyrusEventType.SEARCH_TRIGGERED, { query });\n },\n\n nextSearchResult: () => {\n const state = get();\n if (state.searchResults.length === 0) return;\n const nextIndex =\n (state.activeSearchIndex + 1) % state.searchResults.length;\n const pageIndex = state.searchResults[nextIndex].pageIndex;\n set({\n activeSearchIndex: nextIndex,\n scrollToPageSignal: pageIndex,\n currentPage: pageIndex + 1,\n });\n },\n\n prevSearchResult: () => {\n const state = get();\n if (state.searchResults.length === 0) return;\n const prevIndex =\n (state.activeSearchIndex - 1 + state.searchResults.length) %\n state.searchResults.length;\n const pageIndex = state.searchResults[prevIndex].pageIndex;\n set({\n activeSearchIndex: prevIndex,\n scrollToPageSignal: pageIndex,\n currentPage: pageIndex + 1,\n });\n },\n\n triggerScrollToPage: (pageIndex) =>\n set({ scrollToPageSignal: pageIndex, currentPage: pageIndex + 1 }),\n}));\n","\r\nimport { PapyrusEventType, PapyrusEventListener, EventPayloads } from '@papyrus-sdk/types';\r\n\r\nexport class PapyrusEventEmitter {\r\n private listeners: Map<PapyrusEventType, Set<PapyrusEventListener<any>>> = new Map();\r\n\r\n on<T extends PapyrusEventType>(event: T, listener: PapyrusEventListener<T>): () => void {\r\n if (!this.listeners.has(event)) {\r\n this.listeners.set(event, new Set());\r\n }\r\n this.listeners.get(event)!.add(listener);\r\n\r\n // Return an unsubscribe function\r\n return () => this.off(event, listener);\r\n }\r\n\r\n off<T extends PapyrusEventType>(event: T, listener: PapyrusEventListener<T>): void {\r\n const eventListeners = this.listeners.get(event);\r\n if (eventListeners) {\r\n eventListeners.delete(listener);\r\n }\r\n }\r\n\r\n emit<T extends PapyrusEventType>(event: T, payload: EventPayloads[T]): void {\r\n const eventListeners = this.listeners.get(event);\r\n if (eventListeners) {\r\n eventListeners.forEach((listener) => listener(payload));\r\n }\r\n }\r\n}\r\n\r\n// Singleton instance for the core package\r\nexport const papyrusEvents = new PapyrusEventEmitter();\r\n","import { DocumentEngine, SearchResult } from \"@papyrus-sdk/types\";\n\nexport class SearchService {\n private engine: DocumentEngine;\n constructor(engine: DocumentEngine) {\n this.engine = engine;\n }\n\n async search(query: string): Promise<SearchResult[]> {\n const trimmedQuery = query.trim();\n if (!trimmedQuery || trimmedQuery.length < 2) return [];\n const normalizedQuery = this.normalizeForSearch(trimmedQuery);\n if (!normalizedQuery) return [];\n\n if (typeof this.engine.searchText === \"function\") {\n return await this.engine.searchText(trimmedQuery);\n }\n\n const results: SearchResult[] = [];\n const pageCount = this.engine.getPageCount();\n\n for (let i = 0; i < pageCount; i++) {\n const textContent = await this.engine.getTextContent(i);\n const rawPageText = textContent.map((item) => item.str).join(\" \");\n const cleanPageText = this.normalizeWhitespace(\n this.decodeHtmlEntities(this.stripMarkup(rawPageText))\n );\n if (!cleanPageText) continue;\n\n const { normalized, map } = this.normalizeWithMap(cleanPageText);\n if (!normalized) continue;\n\n let pos = normalized.indexOf(normalizedQuery, 0);\n let matchIndex = 0;\n while (pos !== -1) {\n const anchor = map[pos] ?? pos;\n const start = Math.max(0, anchor - 40);\n const end = Math.min(\n cleanPageText.length,\n anchor + trimmedQuery.length + 40\n );\n results.push({\n pageIndex: i,\n text: cleanPageText.substring(start, end),\n matchIndex: matchIndex++,\n });\n pos = normalized.indexOf(normalizedQuery, pos + 1);\n }\n }\n\n return results;\n }\n\n private normalizeForSearch(value: string): string {\n return value\n .toLocaleLowerCase()\n .normalize(\"NFD\")\n .replace(/[\\u0300-\\u036f]/g, \"\");\n }\n\n private normalizeWithMap(value: string): {\n normalized: string;\n map: number[];\n } {\n let normalized = \"\";\n const map: number[] = [];\n\n for (let i = 0; i < value.length; i += 1) {\n const next = this.normalizeForSearch(value[i]);\n if (!next) continue;\n normalized += next;\n for (let j = 0; j < next.length; j += 1) {\n map.push(i);\n }\n }\n\n return { normalized, map };\n }\n\n private stripMarkup(value: string): string {\n if (!value) return \"\";\n return value\n .replace(/<script[\\s\\S]*?<\\/script>/gi, \" \")\n .replace(/<style[\\s\\S]*?<\\/style>/gi, \" \")\n .replace(/<[^>]+>/g, \" \");\n }\n\n private decodeHtmlEntities(value: string): string {\n if (!value) return \"\";\n if (typeof document === \"undefined\") return value;\n\n const textArea = document.createElement(\"textarea\");\n textArea.innerHTML = value;\n return textArea.value;\n }\n\n private normalizeWhitespace(value: string): string {\n return value.replace(/\\s+/g, \" \").trim();\n }\n}\n"],"mappings":"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","names":[]}
|