@embedpdf/plugin-interaction 3.0.0-next.14 → 3.0.0-next.15
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/contract/host.cjs +17 -0
- package/dist/contract/host.cjs.map +1 -0
- package/dist/contract/host.d.cts +3 -0
- package/dist/contract/host.d.ts +3 -0
- package/dist/contract/host.js +14 -0
- package/dist/contract/host.js.map +1 -0
- package/dist/contract-C9IFDdDd.d.cts +179 -0
- package/dist/contract-C9IFDdDd.d.ts +179 -0
- package/dist/contract-CmEMPz9L.js +19 -0
- package/dist/contract-CmEMPz9L.js.map +1 -0
- package/dist/contract-yO6QKpba.cjs +36 -0
- package/dist/contract-yO6QKpba.cjs.map +1 -0
- package/dist/contract.cjs +2 -2
- package/dist/contract.d.cts +2 -2
- package/dist/contract.d.ts +2 -2
- package/dist/contract.js +2 -2
- package/dist/host-contract-Crcu-9DW.d.ts +121 -0
- package/dist/host-contract-DCb-QVfd.d.cts +121 -0
- package/dist/index.cjs +347 -166
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +23 -52
- package/dist/index.d.ts +23 -52
- package/dist/index.js +347 -163
- package/dist/index.js.map +1 -1
- package/package.json +7 -3
- package/dist/contract-B3tBU_U9.cjs +0 -36
- package/dist/contract-B3tBU_U9.cjs.map +0 -1
- package/dist/contract-CrM0i9pQ.js +0 -19
- package/dist/contract-CrM0i9pQ.js.map +0 -1
- package/dist/contract-F3gsFtSf.d.cts +0 -239
- package/dist/contract-F3gsFtSf.d.ts +0 -239
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":["InteractionToken","FeedbackToken"],"sources":["../src/capability.ts","../src/reducer.ts","../src/interaction.plugin.ts","../src/feedback.ts"],"sourcesContent":["import type { PluginContext } from '@embedpdf/core';\nimport type {\n Cursor,\n InteractionAction,\n InteractionCapability,\n InteractionHandler,\n InteractionState,\n Tool,\n ToolCursorSkin,\n ToolId,\n} from './types';\n\ninterface Claim {\n cursor: Cursor;\n priority: number;\n}\n\n/**\n * The interaction hub. Tools + handlers + the captured-gesture owner + cursor\n * claims live in this closure (runtime registries, not serializable state); the\n * reducer state holds only the active tool id and the resolved cursor, so the UI\n * can react to them. The router (`dispatch`) is the heart: on down, the first\n * eligible handler (by priority) to return true OWNS the gesture; move/up route\n * to it; with no owner, move is hover (cursor feedback).\n *\n * Cursor arbitration, top to bottom: the highest-priority CLAIM (hover feedback\n * — 'move' over an annotation, 'text' over text) → over a page, the tool's\n * declared cursor → over a gap, the tool's `gapCursor` (default the neutral\n * arrow). The winning KEYWORD (claim or base, never gap) is then restyled\n * through the armed tool's skin when it maps that keyword. One resolver, so an\n * image cursor can never shadow hover feedback or outlive the page under the\n * pointer.\n */\nexport function createInteractionCapability(\n ctx: PluginContext<InteractionState, InteractionAction>,\n builtinTools: Tool[],\n): InteractionCapability {\n const tools = new Map<ToolId, Tool>();\n for (const t of builtinTools) tools.set(t.id, t);\n const handlers: InteractionHandler[] = [];\n const claims = new Map<string, Claim>();\n const cursorSkins = new Map<ToolId, ToolCursorSkin>();\n const toolCbs = new Set<() => void>();\n let owner: InteractionHandler | null = null;\n // Whether the last dispatched sample hit a page — the base cursor only\n // applies where the tool can act; over gaps it falls back to `gapCursor`.\n let overPage = false;\n\n const toolOf = (id: ToolId): Tool =>\n tools.get(id) ?? { id, cursor: 'default', enables: new Set() };\n const active = (): Tool => toolOf(ctx.getState().activeToolId);\n\n const resolveCursor = (): Cursor => {\n let top: Claim | null = null;\n for (const c of claims.values()) if (!top || c.priority > top.priority) top = c;\n const tool = active();\n const skin = cursorSkins.get(tool.id);\n // The cursor keeps its MEANING (a keyword — the winning claim's, else the\n // tool's declared base); the skin may restyle that keyword in the armed\n // tool's identity (an I-beam + tool icon over text). Unmapped keywords\n // render as-is — a foreign affordance drops the tool identity — and gaps\n // resolve before the skin: identity only where the action is possible.\n if (top) return skin?.[top.cursor] ?? top.cursor;\n if (!overPage) return tool.gapCursor ?? 'default';\n return skin?.[tool.cursor] ?? tool.cursor;\n };\n const syncCursor = (): void => {\n const next = resolveCursor();\n if (next !== ctx.getState().cursor) ctx.dispatch({ type: 'SET_CURSOR', cursor: next });\n };\n\n // Lens scoping: a handler registered with a `source` only sees samples\n // stamped with it. Filtering is on DEFINITE mismatch only — an unstamped\n // sample (custom dispatcher, single-lens embed) still routes everywhere.\n const handlerSources = new Map<InteractionHandler, string>();\n const eligible = (source?: string): InteractionHandler[] => {\n const tool = active();\n return handlers\n .filter((h) => {\n const hs = handlerSources.get(h);\n return hs === undefined || source === undefined || hs === source;\n })\n .filter((h) => h.enabledFor(tool))\n .sort((a, b) => b.priority - a.priority);\n };\n\n return {\n activeTool: active,\n activeToolId: () => ctx.getState().activeToolId,\n cursor: () => ctx.getState().cursor,\n tools: () => [...tools.values()],\n\n activateTool: (id) => {\n if (!tools.has(id)) throw new Error(`[interaction] unknown tool '${id}'`);\n owner = null;\n claims.clear(); // drop the previous tool's hover claims; handlers re-claim on next hover\n ctx.dispatch({ type: 'SET_TOOL', toolId: id });\n syncCursor();\n toolCbs.forEach((cb) => cb());\n },\n\n onToolChange: (cb) => {\n toolCbs.add(cb);\n return () => toolCbs.delete(cb);\n },\n\n registerTool: (tool) => {\n tools.set(tool.id, tool);\n return () => {\n tools.delete(tool.id);\n cursorSkins.delete(tool.id);\n };\n },\n\n setToolCursor: (id, skin) => {\n if (skin === null) cursorSkins.delete(id);\n else cursorSkins.set(id, skin);\n syncCursor();\n },\n\n registerHandler: (handler, options) => {\n handlers.push(handler);\n if (options?.source !== undefined) handlerSources.set(handler, options.source);\n return () => {\n const i = handlers.indexOf(handler);\n if (i >= 0) handlers.splice(i, 1);\n handlerSources.delete(handler);\n if (owner === handler) owner = null;\n };\n },\n\n setCursor: (token, cursor, priority = 0) => {\n if (cursor === null) claims.delete(token);\n else claims.set(token, { cursor, priority });\n syncCursor();\n },\n\n wouldClaimTouch: (sample) => {\n for (const h of eligible(sample.source)) if (h.claimsTouch?.(sample)) return true;\n return false;\n },\n\n dispatch: (sample) => {\n const nowOverPage = sample.page != null;\n if (nowOverPage !== overPage) {\n overPage = nowOverPage;\n syncCursor(); // crossing a page edge re-resolves the base cursor\n }\n if (sample.phase === 'down') {\n owner = null;\n for (const h of eligible(sample.source)) {\n if (h.onDown(sample)) {\n owner = h;\n break;\n }\n }\n } else if (sample.phase === 'move') {\n if (owner) owner.onMove?.(sample);\n else for (const h of eligible(sample.source)) h.onHover?.(sample);\n } else if (sample.phase === 'cancel') {\n // Abort, don't commit: navigation took the pointer (second finger →\n // pinch) or the system cancelled it. onUp is the fallback for handlers\n // that predate cancel — committing beats a stuck gesture.\n const o = owner;\n owner = null;\n if (o) (o.onCancel ?? o.onUp)?.call(o, sample);\n } else {\n owner?.onUp?.(sample);\n owner = null;\n }\n },\n };\n}\n","import type { InteractionAction, InteractionConfig, InteractionState } from './types';\n\nexport const initialInteractionState = (config: InteractionConfig): InteractionState => ({\n activeToolId: config.defaultTool ?? 'pointer',\n cursor: 'default',\n});\n\nexport const interactionReducer = (\n state: InteractionState,\n action: InteractionAction,\n): InteractionState => {\n switch (action.type) {\n case 'SET_TOOL':\n return state.activeToolId === action.toolId\n ? state\n : { ...state, activeToolId: action.toolId };\n case 'SET_CURSOR':\n return state.cursor === action.cursor ? state : { ...state, cursor: action.cursor };\n default:\n return state;\n }\n};\n","import { definePlugin } from '@embedpdf/core';\nimport { createInteractionCapability } from './capability';\nimport { initialInteractionState, interactionReducer } from './reducer';\nimport { InteractionToken } from './types';\nimport type {\n InteractionAction,\n InteractionCapability,\n InteractionConfig,\n InteractionState,\n Tool,\n} from './types';\n\n/**\n * The two built-in tools. Features ADD tools via `registerTool` (a draw tool, a\n * redact tool…). `enables` is the composition seam:\n * pointer → text selection + annotation editing + annotation marquee selection\n * pan → scrolling (contributed by Stage) + annotation editing, NO text select\n *\n * Both carry `form-fill`: filling a form is the RESTING state of a viewer\n * (Acrobat's hand tool fills forms too), so widgets are fill-controls under the\n * default tools and only become geometry-editable under a form-design tool.\n * Both carry `link-nav` for the same reason: links navigate under the default\n * tools (Acrobat behaviour) and only become geometry-editable under the link\n * tool. Tags are opaque to the hub — without the form/link plugins they're inert.\n */\nexport const builtinTools = (): Tool[] => [\n {\n id: 'pointer',\n cursor: 'default',\n enables: new Set([\n 'text-select',\n 'annotation-edit',\n 'annotation-marquee',\n 'form-fill',\n 'link-nav',\n ]),\n },\n {\n id: 'pan',\n cursor: 'grab',\n gapCursor: 'grab', // pan works anywhere — the open hand doesn't stop at page edges\n enables: new Set(['scroll', 'annotation-edit', 'form-fill', 'link-nav']),\n },\n];\n\n/**\n * The interaction hub plugin — document-scoped, depends on nothing. Every feature\n * plugin (selection, annotation, forms, redaction) `requires` this token; Stage\n * `optional`-ly contributes a scroll handler. Works with `<Stage>` or a standalone\n * `<PageView>` — it only routes page-space pointer samples.\n */\nexport const interactionPlugin = (config: InteractionConfig = {}) =>\n definePlugin<InteractionState, InteractionAction, InteractionCapability>({\n id: 'interaction',\n token: InteractionToken,\n scope: 'document',\n initialState: () => initialInteractionState(config),\n reduce: interactionReducer,\n capability: (ctx) => createInteractionCapability(ctx, builtinTools()),\n });\n","/**\n * Platform feedback (haptics) — the OUTPUT half of the interaction seam:\n * pointers come in, feedback goes back out to the device.\n *\n * ONE workspace-scoped provider, MANY consumers: any plugin lists\n * {@link FeedbackToken} as an optional dependency and calls the capability at\n * its semantic moments (the selection handler on a long-press word-select; an\n * annotation pickup or a snap later). The plugin itself stays DOM-free — the\n * PROVIDER is injected from outside (`@embedpdf/web` ships `vibrationFeedback`\n * and a WKWebView bridge; native shells bring their own), exactly like the\n * stage's Scheduler seam: host dependencies enter through explicit injection,\n * never a hidden global.\n *\n * The vocabulary is deliberately the PLATFORM's haptic taxonomy — iOS's three\n * generator families, which Android mirrors in `HapticFeedbackConstants` and\n * the web approximates with vibration patterns — not app vocabulary. A verb\n * belongs here only if a provider would map it to a physically different\n * output; call-site semantics (\"word selected\", \"annotation picked up\") pick a\n * family, they never add one.\n */\nimport { definePlugin } from '@embedpdf/core';\n\nimport { FeedbackToken } from './feedback.types';\nimport type { FeedbackPluginOptions, PlatformFeedback } from './feedback.types';\n\n/** No provider → every call is a cheap no-op; consumers never branch. */\nconst NOOP: PlatformFeedback = { selection() {}, impact() {}, notify() {} };\n\n/** The plugin is stateless; the action type exists only to satisfy the\n * reducer contract (nothing ever dispatches it). */\ntype FeedbackAction = { type: 'feedback/noop' };\n\n/**\n * Register the workspace's ONE feedback provider. Stateless: the capability IS\n * the provider (or the no-op).\n */\nexport const feedbackPlugin = (options: FeedbackPluginOptions = {}) =>\n definePlugin<Record<string, never>, FeedbackAction, PlatformFeedback>({\n id: 'feedback',\n scope: 'workspace',\n token: FeedbackToken,\n initialState: () => ({}),\n reduce: (state) => state,\n capability: () => options.provider ?? NOOP,\n });\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["PluginError","samplePointOn","InteractionToken","INTERACTION_DEFAULTS","InteractionToken","FeedbackToken"],"sources":["../src/model.ts","../src/controller.ts","../src/interaction.plugin.ts","../src/state.ts","../src/feedback.ts"],"sourcesContent":["/**\n * The interaction state: the active tool, the stack `pushTool` saves, and the\n * resolved cursor, so UI can react. Tools, handlers and cursor claims are\n * registries in the controller's closure. Every function below is a pure\n * transition; the controller applies them with `ctx.state.update`.\n */\nimport type { Cursor, ToolId } from './contract';\nimport type { HostTool } from './host-contract';\n\nexport interface InteractionState {\n readonly activeToolId: ToolId;\n /** Tools activated with `pushTool`, oldest first; `popTool` restores the last. */\n readonly toolStack: readonly ToolId[];\n readonly cursor: Cursor;\n}\n\n/** A document starts on `pointer`; the controller then activates the `defaultTool` setting. */\nexport const initialInteractionState = (): InteractionState => ({\n activeToolId: 'pointer',\n toolStack: [],\n cursor: 'default',\n});\n\n/** Activate a tool and forget every pushed one. */\nexport function activateTool(state: InteractionState, toolId: ToolId): InteractionState {\n if (state.activeToolId === toolId && state.toolStack.length === 0) return state;\n return { ...state, activeToolId: toolId, toolStack: [] };\n}\n\n/** Activate a tool for a moment, saving the active one for {@link popTool}. */\nexport function pushTool(state: InteractionState, toolId: ToolId): InteractionState {\n return { ...state, toolStack: [...state.toolStack, state.activeToolId], activeToolId: toolId };\n}\n\n/** Activate the tool saved last; an empty stack changes nothing. */\nexport function popTool(state: InteractionState): InteractionState {\n if (state.toolStack.length === 0) return state;\n return {\n ...state,\n toolStack: state.toolStack.slice(0, -1),\n activeToolId: state.toolStack[state.toolStack.length - 1],\n };\n}\n\nexport function setCursor(state: InteractionState, cursor: Cursor): InteractionState {\n return state.cursor === cursor ? state : { ...state, cursor };\n}\n\n/**\n * The two built-in tools. Features add tools via `registerTool`. `enables`\n * is the composition seam:\n * pointer → text selection + annotation editing + marquee selection\n * pan → scrolling (contributed by Stage) + annotation editing, no text select\n * Both carry `form-fill` and `link-nav`: filling forms and following links is\n * the resting state of a viewer (Acrobat's hand tool does both).\n */\nexport const builtinTools = (): HostTool[] => [\n {\n id: 'pointer',\n cursor: 'default',\n enables: new Set([\n 'text-select',\n 'annotation-edit',\n 'annotation-marquee',\n 'form-fill',\n 'link-nav',\n ]),\n },\n {\n id: 'pan',\n cursor: 'grab',\n gapCursor: 'grab',\n enables: new Set(['scroll', 'annotation-edit', 'form-fill', 'link-nav']),\n },\n];\n","import {\n memo,\n PluginError,\n type PageRef,\n type PluginContext,\n type Unsubscribe,\n} from '@embedpdf/core';\n\nimport type {\n Cursor,\n GestureCancelledEvent,\n GestureEndedEvent,\n GestureStartedEvent,\n InteractionSettings,\n ToolChangedEvent,\n ToolCursors,\n ToolId,\n ToolPointerEvent,\n} from './contract';\nimport {\n samplePointOn,\n type HostTool,\n type HostToolInput,\n type InteractionHandler,\n type InteractionHostCapability,\n type PointerSample,\n} from './host-contract';\nimport { activateTool, popTool, pushTool, setCursor, type InteractionState } from './model';\n\ninterface Claim {\n cursor: Cursor;\n priority: number;\n}\n\n/** A tool's own pointer methods take a press before every handler a plugin registers. */\nconst OWN_POINTER_PRIORITY = 1000;\n\nconst NO_TAGS: ReadonlySet<string> = new Set();\n\n/**\n * The interaction hub. Tools, handlers, the captured-gesture owner and cursor\n * claims are registries in this closure; the state holds only the active\n * tool, the tool stack and the resolved cursor, so UI can react. Registering\n * or removing a tool wakes readers with `ctx.notify()`.\n *\n * Cursor arbitration, top to bottom: the highest-priority claim (hover\n * feedback) → over a page, the tool's declared cursor → over a gap, the\n * tool's `gapCursor`. The winning keyword is replaced through the active\n * tool's cursors (`setToolCursor`) when they name that keyword.\n */\nexport function createInteractionController(\n ctx: PluginContext<InteractionState, InteractionSettings>,\n builtinTools: readonly HostTool[],\n) {\n const settings = ctx.settings();\n const tools = new Map<ToolId, HostTool>();\n /** The handler each tool with its own pointer methods brings, registered beside the plugins' handlers. */\n const ownHandlers = new Map<HostTool, InteractionHandler>();\n /** Bumped on every tool registry change: the input of the tool list. */\n let toolsVersion = 0;\n const handlers: InteractionHandler[] = [];\n const handlerSources = new Map<InteractionHandler, string>();\n const claims = new Map<string, Claim>();\n const toolCursors = new Map<ToolId, ToolCursors>();\n let owner: InteractionHandler | null = null;\n /** The gesture `owner` took, as its events report it. */\n let gesture: GestureStartedEvent | null = null;\n /** Whether the last dispatched sample hit a page: gaps fall back to `gapCursor`. */\n let overPage = false;\n\n const toolChanged = ctx.events.source<ToolChangedEvent>();\n const gestureStarted = ctx.events.source<GestureStartedEvent>();\n const gestureEnded = ctx.events.source<GestureEndedEvent>();\n const gestureCancelled = ctx.events.source<GestureCancelledEvent>();\n const cursorChanged = ctx.events.source<{ readonly cursor: Cursor }>();\n\n ctx.state.onChange(({ previous, next }) => {\n if (previous.cursor !== next.cursor) cursorChanged.emit({ cursor: next.cursor });\n });\n\n const state = () => ctx.state.get();\n const toolOf = (id: ToolId): HostTool =>\n tools.get(id) ?? { id, cursor: 'default', enables: NO_TAGS };\n const active = (): HostTool => toolOf(state().activeToolId);\n const listTools = memo(\n () => [toolsVersion],\n (_version) => [...tools.values()],\n );\n\n const resolveCursor = (): Cursor => {\n let top: Claim | null = null;\n for (const claim of claims.values()) if (!top || claim.priority > top.priority) top = claim;\n const tool = active();\n const cursors = toolCursors.get(tool.id);\n if (top) return cursors?.[top.cursor] ?? top.cursor;\n if (!overPage) return tool.gapCursor ?? 'default';\n return cursors?.[tool.cursor] ?? tool.cursor;\n };\n const syncCursor = (): void => ctx.state.update(setCursor, resolveCursor());\n\n // ── tools ─────────────────────────────────────────────────────────────\n\n const removeHandler = (handler: InteractionHandler): void => {\n const index = handlers.indexOf(handler);\n if (index >= 0) handlers.splice(index, 1);\n handlerSources.delete(handler);\n if (owner === handler) {\n owner = null;\n gesture = null;\n }\n };\n const addHandler = (handler: InteractionHandler, source?: string): Unsubscribe => {\n handlers.push(handler);\n if (source !== undefined) handlerSources.set(handler, source);\n return () => removeHandler(handler);\n };\n\n /** Take a tool out of the registry, with the handler its pointer methods brought. */\n const removeTool = (tool: HostTool): void => {\n tools.delete(tool.id);\n const handler = ownHandlers.get(tool);\n ownHandlers.delete(tool);\n if (handler) removeHandler(handler);\n };\n\n /**\n * Put a tool in the registry without waking readers, and return the stored\n * tool (tags filled in). A taken id is refused unless `replace`.\n */\n const addTool = (input: HostToolInput, replace: boolean): HostTool => {\n const taken = tools.get(input.id);\n if (taken && !replace) {\n throw new PluginError(\n 'conflict',\n 'interaction',\n `tool '${input.id}' is already registered; pass { replace: true } to replace it`,\n );\n }\n if (taken) removeTool(taken);\n const tool: HostTool = input.enables ? (input as HostTool) : { ...input, enables: NO_TAGS };\n tools.set(tool.id, tool);\n const handler = ownPointerHandler(tool);\n if (handler) {\n ownHandlers.set(tool, handler);\n addHandler(handler);\n }\n return tool;\n };\n\n const toolsChanged = (): void => {\n toolsVersion += 1;\n ctx.notify();\n };\n\n /** The registration's remover: it removes this registration only, never a later one with its id. */\n const removerOf = (tool: HostTool) => (): void => {\n if (tools.get(tool.id) !== tool) return;\n removeTool(tool);\n toolCursors.delete(tool.id);\n toolsChanged();\n };\n\n for (const tool of builtinTools) addTool(tool, true);\n // The `tools` setting's tools, registered over the built-in ones; a change of\n // the setting swaps them for the new list.\n let removeSettingTools: Array<() => void> = settings\n .get()\n .tools.map((tool) => removerOf(addTool(tool, true)));\n settings.api.onSettingsChanged(({ changed }) => {\n if (!changed.includes('tools')) return;\n for (const remove of removeSettingTools) remove();\n removeSettingTools = settings.get().tools.map((tool) => removerOf(addTool(tool, true)));\n toolsChanged();\n });\n ctx.state.update(activateTool, settings.get().defaultTool);\n syncCursor();\n\n // ── routing ───────────────────────────────────────────────────────────\n\n /** Lens scoping: a handler registered with a `source` only sees samples stamped with it. */\n const eligible = (source?: string): InteractionHandler[] => {\n const tool = active();\n return handlers\n .filter((handler) => {\n const handlerSource = handlerSources.get(handler);\n return handlerSource === undefined || source === undefined || handlerSource === source;\n })\n .filter((handler) => handler.enabledFor(tool))\n .sort((left, right) => right.priority - left.priority);\n };\n\n /**\n * Activate a tool through `transition`. Every activation is announced,\n * including activating the active tool again.\n */\n function arm(id: ToolId, transition: (current: InteractionState) => InteractionState): void {\n if (!tools.has(id)) throw new PluginError('not-found', 'interaction', `unknown tool '${id}'`);\n const previousToolId = state().activeToolId;\n owner = null;\n gesture = null;\n claims.clear(); // the previous tool's hover claims go with it; handlers claim again on hover\n ctx.state.update(transition);\n syncCursor();\n toolChanged.emit({ toolId: id, previousToolId });\n }\n\n const api: InteractionHostCapability = {\n ...settings.api,\n\n // ── public ────────────────────────────────────────────────────────────\n getActiveTool: active,\n getActiveToolId: () => state().activeToolId,\n getDefaultToolId: () => settings.get().defaultTool,\n listTools,\n getTool: (id) => tools.get(id) ?? null,\n hasTool: (id) => tools.has(id),\n activateTool: (id) => arm(id, (current) => activateTool(current, id)),\n activateDefaultTool: () => {\n const id = settings.get().defaultTool;\n arm(id, (current) => activateTool(current, id));\n },\n pushTool: (id) => arm(id, (current) => pushTool(current, id)),\n popTool: () => {\n const stack = state().toolStack;\n if (stack.length === 0) return;\n arm(stack[stack.length - 1], popTool);\n },\n setToolCursor: (id, cursors) => {\n if (cursors === null) toolCursors.delete(id);\n else toolCursors.set(id, cursors);\n syncCursor();\n },\n registerTool: (tool, options) => {\n const stored = addTool(tool, options?.replace ?? false);\n toolsChanged();\n return removerOf(stored);\n },\n onToolChanged: toolChanged.on,\n onGestureStarted: gestureStarted.on,\n onGestureEnded: gestureEnded.on,\n onGestureCancelled: gestureCancelled.on,\n\n // ── host ──────────────────────────────────────────────────────────────\n activeToolEnables: (behavior) => active().enables.has(behavior),\n registerHandler: (handler, options) => addHandler(handler, options?.source),\n claimCursor: (token, cursor, priority = 0) => {\n if (cursor === null) claims.delete(token);\n else claims.set(token, { cursor, priority });\n syncCursor();\n },\n getCursor: () => state().cursor,\n hasCursorClaim: () => claims.size > 0,\n onCursorChanged: cursorChanged.on,\n wouldClaimTouch: (sample) => {\n for (const handler of eligible(sample.source)) if (handler.claimsTouch?.(sample)) return true;\n return false;\n },\n dispatchPointer: (sample) => {\n const nowOverPage = sample.page != null;\n if (nowOverPage !== overPage) {\n overPage = nowOverPage;\n syncCursor(); // crossing a page edge resolves the base cursor again\n }\n if (sample.phase === 'down') {\n owner = null;\n gesture = null;\n for (const handler of eligible(sample.source)) {\n if (handler.onDown(sample)) {\n owner = handler;\n gesture = {\n toolId: state().activeToolId,\n page: sample.page?.ref ?? null,\n pointerType: sample.pointerType ?? 'mouse',\n };\n gestureStarted.emit(gesture);\n break;\n }\n }\n } else if (sample.phase === 'move') {\n if (owner) owner.onMove?.(sample);\n else for (const handler of eligible(sample.source)) handler.onHover?.(sample);\n } else {\n const captured = owner;\n const taken = gesture;\n owner = null;\n gesture = null;\n if (!captured || !taken) return;\n if (sample.phase === 'cancel') {\n // Abort, don't commit: navigation took the pointer or the system cancelled it.\n (captured.onCancel ?? captured.onUp)?.call(captured, sample);\n gestureCancelled.emit(taken);\n } else {\n captured.onUp?.(sample);\n gestureEnded.emit(taken);\n }\n }\n },\n };\n\n return { api };\n}\n\n/**\n * The handler a tool's own pointer methods bring, or null for a tool without\n * them: it is live only while that tool is active, and turns samples into\n * {@link ToolPointerEvent}s in page coordinates. A gesture keeps the page it\n * started on, so a drag that leaves the page goes on in that page's\n * coordinates.\n */\nfunction ownPointerHandler(tool: HostTool): InteractionHandler | null {\n if (!tool.onPointerDown && !tool.onHover) return null;\n let home: PageRef | null = null;\n let last: ToolPointerEvent | null = null;\n\n const eventOf = (page: PageRef, point: ToolPointerEvent['point'], sample: PointerSample) => ({\n page,\n point,\n modifiers: sample.modifiers,\n pointerType: sample.pointerType ?? 'mouse',\n });\n /** The sample on the gesture's page, or the last event when the source can't project it there. */\n const follow = (sample: PointerSample): ToolPointerEvent | null => {\n if (!home) return null;\n const point = samplePointOn(sample, home);\n if (point) last = eventOf(home, point, sample);\n return last;\n };\n const end = (sample: PointerSample): void => {\n const event = follow(sample);\n home = null;\n last = null;\n if (event) tool.onPointerUp?.(event);\n };\n\n return {\n id: `tool:${tool.id}`,\n priority: OWN_POINTER_PRIORITY,\n enabledFor: (active) => active === tool,\n onDown: (sample) => {\n if (!sample.page || !tool.onPointerDown) return false;\n const event = eventOf(sample.page.ref, sample.page.point, sample);\n if (tool.onPointerDown(event) !== true) return false;\n home = sample.page.ref;\n last = event;\n return true;\n },\n onMove: (sample) => {\n const event = follow(sample);\n if (event) tool.onPointerMove?.(event);\n },\n onUp: end,\n onCancel: end,\n onHover: (sample) => {\n if (sample.page) tool.onHover?.(eventOf(sample.page.ref, sample.page.point, sample));\n },\n };\n}\n","import { definePlugin } from '@embedpdf/core';\n\nimport { INTERACTION_DEFAULTS, type InteractionConfig } from './contract';\nimport { createInteractionController } from './controller';\nimport { InteractionToken } from './host-contract';\nimport { builtinTools, initialInteractionState } from './model';\n\n/**\n * The interaction hub: document-scoped, depends on nothing. Feature plugins\n * `require` this token; the Stage `optional`-ly contributes a scroll handler.\n * `config` is the settings the app registers, over {@link INTERACTION_DEFAULTS}.\n */\nexport const interactionPlugin = (config?: InteractionConfig) =>\n definePlugin({\n id: 'interaction',\n token: InteractionToken,\n scope: 'document',\n state: initialInteractionState,\n settings: { defaults: INTERACTION_DEFAULTS, registered: config },\n create: (ctx) => createInteractionController(ctx, builtinTools()),\n });\n","/**\n * The tools page's State table as code: what `useInteractionState()` returns,\n * and the same fields in every other framework. With no document there is no\n * active tool and no tool to switch to.\n */\nimport { defineState } from '@embedpdf/core';\n\nimport { InteractionToken, type ToolId } from './contract';\n\nexport const interactionState = defineState(InteractionToken, {\n read: (interaction) => ({\n // Widened: the empty state has no active tool.\n activeToolId: interaction.getActiveToolId() as ToolId | null,\n tools: interaction.listTools(),\n }),\n empty: { activeToolId: null, tools: [] },\n});\n","/**\n * Platform feedback (haptics) — the output half of the interaction seam:\n * pointers come in, feedback goes back out to the device.\n *\n * One workspace-scoped provider, many consumers: any plugin lists\n * {@link FeedbackToken} as an optional dependency and calls the capability at\n * its semantic moments (the selection handler on a long-press word-select).\n * The plugin itself stays DOM-free — the provider is injected from outside\n * (`@embedpdf/web` ships `vibrationFeedback` and a WKWebView bridge; native\n * shells bring their own), as the host's clock enters the kernel\n * (`createKernel({ clock })`): host dependencies enter through explicit\n * injection, never a hidden global.\n *\n * The vocabulary is deliberately the platform's haptic taxonomy — iOS's three\n * generator families, which Android mirrors in `HapticFeedbackConstants` and\n * the web approximates with vibration patterns — not app vocabulary. A verb\n * belongs here only if a provider would map it to a physically different\n * output; call-site semantics (\"word selected\", \"annotation picked up\") pick a\n * family, they never add one.\n */\nimport { definePlugin } from '@embedpdf/core';\n\nimport { FeedbackToken } from './feedback.types';\nimport type { FeedbackPluginOptions, PlatformFeedback } from './feedback.types';\n\n/** No provider → every call is a cheap no-op; consumers never branch. */\nconst NOOP: PlatformFeedback = { selection() {}, impact() {}, notify() {} };\n\n/**\n * Register the workspace's one feedback provider. Stateless: the capability\n * is the provider (or the no-op).\n */\nexport const feedbackPlugin = (options: FeedbackPluginOptions = {}) =>\n definePlugin({\n id: 'feedback',\n scope: 'workspace',\n token: FeedbackToken,\n create: () => ({ api: options.provider ?? NOOP }),\n });\n"],"mappings":"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"}
|
package/dist/index.d.cts
CHANGED
|
@@ -1,62 +1,33 @@
|
|
|
1
|
-
import { _ as
|
|
2
|
-
import {
|
|
3
|
-
//#region src/
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
* Both carry `form-fill`: filling a form is the RESTING state of a viewer
|
|
11
|
-
* (Acrobat's hand tool fills forms too), so widgets are fill-controls under the
|
|
12
|
-
* default tools and only become geometry-editable under a form-design tool.
|
|
13
|
-
* Both carry `link-nav` for the same reason: links navigate under the default
|
|
14
|
-
* tools (Acrobat behaviour) and only become geometry-editable under the link
|
|
15
|
-
* tool. Tags are opaque to the hub — without the form/link plugins they're inert.
|
|
16
|
-
*/
|
|
17
|
-
declare const builtinTools: () => Tool[];
|
|
18
|
-
/**
|
|
19
|
-
* The interaction hub plugin — document-scoped, depends on nothing. Every feature
|
|
20
|
-
* plugin (selection, annotation, forms, redaction) `requires` this token; Stage
|
|
21
|
-
* `optional`-ly contributes a scroll handler. Works with `<Stage>` or a standalone
|
|
22
|
-
* `<PageView>` — it only routes page-space pointer samples.
|
|
23
|
-
*/
|
|
24
|
-
declare const interactionPlugin: (config?: InteractionConfig) => import("@embedpdf/core").PluginDef<InteractionState, InteractionAction, InteractionCapability>;
|
|
1
|
+
import { _ as ToolTouch, a as INTERACTION_DEFAULTS, b as FeedbackToken, c as InteractionSettings, d as RegisterToolOptions, f as Tool, g as ToolPointerEvent, h as ToolId, i as GestureStartedEvent, l as Modifiers, m as ToolCursors, n as GestureCancelledEvent, o as InteractionCapability, p as ToolChangedEvent, r as GestureEndedEvent, s as InteractionConfig, t as Cursor, u as PointerKind, v as InteractionToken, x as PlatformFeedback, y as FeedbackPluginOptions } from "./contract-C9IFDdDd.cjs";
|
|
2
|
+
import { i as InteractionHostCapability } from "./host-contract-DCb-QVfd.cjs";
|
|
3
|
+
//#region src/model.d.ts
|
|
4
|
+
interface InteractionState {
|
|
5
|
+
readonly activeToolId: ToolId;
|
|
6
|
+
/** Tools activated with `pushTool`, oldest first; `popTool` restores the last. */
|
|
7
|
+
readonly toolStack: readonly ToolId[];
|
|
8
|
+
readonly cursor: Cursor;
|
|
9
|
+
}
|
|
25
10
|
//#endregion
|
|
26
|
-
//#region src/
|
|
11
|
+
//#region src/interaction.plugin.d.ts
|
|
27
12
|
/**
|
|
28
|
-
* The interaction hub
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
* can react to them. The router (`dispatch`) is the heart: on down, the first
|
|
32
|
-
* eligible handler (by priority) to return true OWNS the gesture; move/up route
|
|
33
|
-
* to it; with no owner, move is hover (cursor feedback).
|
|
34
|
-
*
|
|
35
|
-
* Cursor arbitration, top to bottom: the highest-priority CLAIM (hover feedback
|
|
36
|
-
* — 'move' over an annotation, 'text' over text) → over a page, the tool's
|
|
37
|
-
* declared cursor → over a gap, the tool's `gapCursor` (default the neutral
|
|
38
|
-
* arrow). The winning KEYWORD (claim or base, never gap) is then restyled
|
|
39
|
-
* through the armed tool's skin when it maps that keyword. One resolver, so an
|
|
40
|
-
* image cursor can never shadow hover feedback or outlive the page under the
|
|
41
|
-
* pointer.
|
|
13
|
+
* The interaction hub: document-scoped, depends on nothing. Feature plugins
|
|
14
|
+
* `require` this token; the Stage `optional`-ly contributes a scroll handler.
|
|
15
|
+
* `config` is the settings the app registers, over {@link INTERACTION_DEFAULTS}.
|
|
42
16
|
*/
|
|
43
|
-
declare
|
|
17
|
+
declare const interactionPlugin: (config?: InteractionConfig) => import("@embedpdf/core").PluginDef<InteractionState, InteractionHostCapability, InteractionSettings>;
|
|
44
18
|
//#endregion
|
|
45
|
-
//#region src/
|
|
46
|
-
declare const
|
|
47
|
-
|
|
19
|
+
//#region src/state.d.ts
|
|
20
|
+
declare const interactionState: import("@embedpdf/core").StateDeclaration<InteractionCapability, {
|
|
21
|
+
activeToolId: ToolId | null;
|
|
22
|
+
tools: readonly Tool[];
|
|
23
|
+
}>;
|
|
48
24
|
//#endregion
|
|
49
25
|
//#region src/feedback.d.ts
|
|
50
|
-
/** The plugin is stateless; the action type exists only to satisfy the
|
|
51
|
-
* reducer contract (nothing ever dispatches it). */
|
|
52
|
-
type FeedbackAction = {
|
|
53
|
-
type: 'feedback/noop';
|
|
54
|
-
};
|
|
55
26
|
/**
|
|
56
|
-
* Register the workspace's
|
|
57
|
-
* the provider (or the no-op).
|
|
27
|
+
* Register the workspace's one feedback provider. Stateless: the capability
|
|
28
|
+
* is the provider (or the no-op).
|
|
58
29
|
*/
|
|
59
|
-
declare const feedbackPlugin: (options?: FeedbackPluginOptions) => import("@embedpdf/core").PluginDef<
|
|
30
|
+
declare const feedbackPlugin: (options?: FeedbackPluginOptions) => import("@embedpdf/core").PluginDef<void, PlatformFeedback, import("@embedpdf/core").NoSettings>;
|
|
60
31
|
//#endregion
|
|
61
|
-
export {
|
|
32
|
+
export { Cursor, type FeedbackPluginOptions, FeedbackToken, GestureCancelledEvent, GestureEndedEvent, GestureStartedEvent, INTERACTION_DEFAULTS, InteractionCapability, InteractionConfig, InteractionSettings, InteractionToken, Modifiers, type PlatformFeedback, PointerKind, RegisterToolOptions, Tool, ToolChangedEvent, ToolCursors, ToolId, ToolPointerEvent, ToolTouch, feedbackPlugin, interactionPlugin, interactionState };
|
|
62
33
|
//# sourceMappingURL=index.d.cts.map
|
package/dist/index.d.ts
CHANGED
|
@@ -1,62 +1,33 @@
|
|
|
1
|
-
import { _ as
|
|
2
|
-
import {
|
|
3
|
-
//#region src/
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
* Both carry `form-fill`: filling a form is the RESTING state of a viewer
|
|
11
|
-
* (Acrobat's hand tool fills forms too), so widgets are fill-controls under the
|
|
12
|
-
* default tools and only become geometry-editable under a form-design tool.
|
|
13
|
-
* Both carry `link-nav` for the same reason: links navigate under the default
|
|
14
|
-
* tools (Acrobat behaviour) and only become geometry-editable under the link
|
|
15
|
-
* tool. Tags are opaque to the hub — without the form/link plugins they're inert.
|
|
16
|
-
*/
|
|
17
|
-
declare const builtinTools: () => Tool[];
|
|
18
|
-
/**
|
|
19
|
-
* The interaction hub plugin — document-scoped, depends on nothing. Every feature
|
|
20
|
-
* plugin (selection, annotation, forms, redaction) `requires` this token; Stage
|
|
21
|
-
* `optional`-ly contributes a scroll handler. Works with `<Stage>` or a standalone
|
|
22
|
-
* `<PageView>` — it only routes page-space pointer samples.
|
|
23
|
-
*/
|
|
24
|
-
declare const interactionPlugin: (config?: InteractionConfig) => import("@embedpdf/core").PluginDef<InteractionState, InteractionAction, InteractionCapability>;
|
|
1
|
+
import { _ as ToolTouch, a as INTERACTION_DEFAULTS, b as FeedbackToken, c as InteractionSettings, d as RegisterToolOptions, f as Tool, g as ToolPointerEvent, h as ToolId, i as GestureStartedEvent, l as Modifiers, m as ToolCursors, n as GestureCancelledEvent, o as InteractionCapability, p as ToolChangedEvent, r as GestureEndedEvent, s as InteractionConfig, t as Cursor, u as PointerKind, v as InteractionToken, x as PlatformFeedback, y as FeedbackPluginOptions } from "./contract-C9IFDdDd.js";
|
|
2
|
+
import { i as InteractionHostCapability } from "./host-contract-Crcu-9DW.js";
|
|
3
|
+
//#region src/model.d.ts
|
|
4
|
+
interface InteractionState {
|
|
5
|
+
readonly activeToolId: ToolId;
|
|
6
|
+
/** Tools activated with `pushTool`, oldest first; `popTool` restores the last. */
|
|
7
|
+
readonly toolStack: readonly ToolId[];
|
|
8
|
+
readonly cursor: Cursor;
|
|
9
|
+
}
|
|
25
10
|
//#endregion
|
|
26
|
-
//#region src/
|
|
11
|
+
//#region src/interaction.plugin.d.ts
|
|
27
12
|
/**
|
|
28
|
-
* The interaction hub
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
* can react to them. The router (`dispatch`) is the heart: on down, the first
|
|
32
|
-
* eligible handler (by priority) to return true OWNS the gesture; move/up route
|
|
33
|
-
* to it; with no owner, move is hover (cursor feedback).
|
|
34
|
-
*
|
|
35
|
-
* Cursor arbitration, top to bottom: the highest-priority CLAIM (hover feedback
|
|
36
|
-
* — 'move' over an annotation, 'text' over text) → over a page, the tool's
|
|
37
|
-
* declared cursor → over a gap, the tool's `gapCursor` (default the neutral
|
|
38
|
-
* arrow). The winning KEYWORD (claim or base, never gap) is then restyled
|
|
39
|
-
* through the armed tool's skin when it maps that keyword. One resolver, so an
|
|
40
|
-
* image cursor can never shadow hover feedback or outlive the page under the
|
|
41
|
-
* pointer.
|
|
13
|
+
* The interaction hub: document-scoped, depends on nothing. Feature plugins
|
|
14
|
+
* `require` this token; the Stage `optional`-ly contributes a scroll handler.
|
|
15
|
+
* `config` is the settings the app registers, over {@link INTERACTION_DEFAULTS}.
|
|
42
16
|
*/
|
|
43
|
-
declare
|
|
17
|
+
declare const interactionPlugin: (config?: InteractionConfig) => import("@embedpdf/core").PluginDef<InteractionState, InteractionHostCapability, InteractionSettings>;
|
|
44
18
|
//#endregion
|
|
45
|
-
//#region src/
|
|
46
|
-
declare const
|
|
47
|
-
|
|
19
|
+
//#region src/state.d.ts
|
|
20
|
+
declare const interactionState: import("@embedpdf/core").StateDeclaration<InteractionCapability, {
|
|
21
|
+
activeToolId: ToolId | null;
|
|
22
|
+
tools: readonly Tool[];
|
|
23
|
+
}>;
|
|
48
24
|
//#endregion
|
|
49
25
|
//#region src/feedback.d.ts
|
|
50
|
-
/** The plugin is stateless; the action type exists only to satisfy the
|
|
51
|
-
* reducer contract (nothing ever dispatches it). */
|
|
52
|
-
type FeedbackAction = {
|
|
53
|
-
type: 'feedback/noop';
|
|
54
|
-
};
|
|
55
26
|
/**
|
|
56
|
-
* Register the workspace's
|
|
57
|
-
* the provider (or the no-op).
|
|
27
|
+
* Register the workspace's one feedback provider. Stateless: the capability
|
|
28
|
+
* is the provider (or the no-op).
|
|
58
29
|
*/
|
|
59
|
-
declare const feedbackPlugin: (options?: FeedbackPluginOptions) => import("@embedpdf/core").PluginDef<
|
|
30
|
+
declare const feedbackPlugin: (options?: FeedbackPluginOptions) => import("@embedpdf/core").PluginDef<void, PlatformFeedback, import("@embedpdf/core").NoSettings>;
|
|
60
31
|
//#endregion
|
|
61
|
-
export {
|
|
32
|
+
export { Cursor, type FeedbackPluginOptions, FeedbackToken, GestureCancelledEvent, GestureEndedEvent, GestureStartedEvent, INTERACTION_DEFAULTS, InteractionCapability, InteractionConfig, InteractionSettings, InteractionToken, Modifiers, type PlatformFeedback, PointerKind, RegisterToolOptions, Tool, ToolChangedEvent, ToolCursors, ToolId, ToolPointerEvent, ToolTouch, feedbackPlugin, interactionPlugin, interactionState };
|
|
62
33
|
//# sourceMappingURL=index.d.ts.map
|