@lofcz/embedpdf-plugin-commands 2.15.0 → 3.0.0-next.7
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/LICENSE +192 -21
- package/dist/index.cjs +196 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +150 -0
- package/dist/index.d.ts +150 -1
- package/dist/index.js +189 -379
- package/dist/index.js.map +1 -1
- package/package.json +21 -40
- package/dist/lib/actions.d.ts +0 -8
- package/dist/lib/commands-plugin.d.ts +0 -40
- package/dist/lib/index.d.ts +0 -8
- package/dist/lib/manifest.d.ts +0 -4
- package/dist/lib/reducer.d.ts +0 -5
- package/dist/lib/types.d.ts +0 -116
- package/dist/react/adapter.d.ts +0 -2
- package/dist/react/core.d.ts +0 -1
- package/dist/react/index.cjs +0 -2
- package/dist/react/index.cjs.map +0 -1
- package/dist/react/index.d.ts +0 -1
- package/dist/react/index.js +0 -90
- package/dist/react/index.js.map +0 -1
- package/dist/shared/components/index.d.ts +0 -1
- package/dist/shared/components/keyboard-shortcuts.d.ts +0 -6
- package/dist/shared/hooks/index.d.ts +0 -1
- package/dist/shared/hooks/use-commands.d.ts +0 -23
- package/dist/shared/index.d.ts +0 -4
- package/dist/shared/utils/index.d.ts +0 -1
- package/dist/shared/utils/keyboard-handler.d.ts +0 -10
- package/dist/shared-react/components/index.d.ts +0 -1
- package/dist/shared-react/components/keyboard-shortcuts.d.ts +0 -6
- package/dist/shared-react/hooks/index.d.ts +0 -1
- package/dist/shared-react/hooks/use-commands.d.ts +0 -23
- package/dist/shared-react/index.d.ts +0 -4
- package/dist/shared-react/utils/index.d.ts +0 -1
- package/dist/shared-react/utils/keyboard-handler.d.ts +0 -10
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../src/lib/manifest.ts","../src/lib/actions.ts","../src/lib/commands-plugin.ts","../src/lib/reducer.ts","../src/lib/index.ts"],"sourcesContent":["import { PluginManifest } from '@embedpdf/core';\nimport { CommandsPluginConfig } from './types';\n\nexport const COMMANDS_PLUGIN_ID = 'commands';\n\nexport const manifest: PluginManifest<CommandsPluginConfig> = {\n id: COMMANDS_PLUGIN_ID,\n name: 'Commands Plugin',\n version: '1.0.0',\n provides: ['commands'],\n requires: [],\n optional: ['i18n', 'ui'],\n defaultConfig: {\n commands: {},\n },\n};\n","import { Action } from '@embedpdf/core';\n\nexport const SET_DISABLED_CATEGORIES = 'COMMANDS/SET_DISABLED_CATEGORIES';\n\nexport interface SetDisabledCategoriesAction extends Action {\n type: typeof SET_DISABLED_CATEGORIES;\n payload: string[];\n}\n\nexport type CommandsAction = SetDisabledCategoriesAction;\n\nexport const setDisabledCategories = (categories: string[]): SetDisabledCategoriesAction => ({\n type: SET_DISABLED_CATEGORIES,\n payload: categories,\n});\n","import {\n BasePlugin,\n PluginRegistry,\n StoreState,\n createEmitter,\n createBehaviorEmitter,\n Listener,\n arePropsEqual,\n} from '@embedpdf/core';\nimport { Logger } from '@embedpdf/models';\nimport { I18nCapability, I18nPlugin } from '@embedpdf/plugin-i18n';\nimport {\n CommandsCapability,\n CommandsPluginConfig,\n CommandsState,\n Command,\n ResolvedCommand,\n CommandExecutedEvent,\n CommandStateChangedEvent,\n ShortcutExecutedEvent,\n CategoryChangedEvent,\n CommandScope,\n Dynamic,\n} from './types';\nimport { CommandsAction, setDisabledCategories } from './actions';\n\nexport class CommandsPlugin extends BasePlugin<\n CommandsPluginConfig,\n CommandsCapability,\n CommandsState,\n CommandsAction\n> {\n static readonly id = 'commands' as const;\n\n private commands = new Map<string, Command>();\n private i18n: I18nCapability | null = null;\n private shortcutMap = new Map<string, string>(); // shortcut -> commandId\n\n private readonly commandExecuted$ = createEmitter<CommandExecutedEvent>();\n private readonly commandStateChanged$ = createEmitter<CommandStateChangedEvent>();\n private readonly shortcutExecuted$ = createEmitter<ShortcutExecutedEvent>();\n private readonly categoryChanged$ = createBehaviorEmitter<CategoryChangedEvent>();\n\n // Cache previous resolved states per document to detect changes\n private previousStates = new Map<string, Map<string, ResolvedCommand>>();\n\n constructor(id: string, registry: PluginRegistry, config: CommandsPluginConfig) {\n super(id, registry);\n\n // Check if i18n plugin is available (optional dependency)\n const i18nPlugin = registry.getPlugin<I18nPlugin>('i18n');\n this.i18n = i18nPlugin?.provides() ?? null;\n\n // Initialize disabled categories from config\n if (config.disabledCategories?.length) {\n this.dispatch(setDisabledCategories(config.disabledCategories));\n }\n\n // Register all commands from config\n Object.values(config.commands).forEach((command) => {\n this.registerCommand(command);\n });\n\n // Subscribe to global store changes\n this.registry.getStore().subscribe((_action, newState) => {\n this.onGlobalStoreChange(newState);\n });\n }\n\n protected override onDocumentClosed(documentId: string): void {\n // Cleanup previous states cache\n this.previousStates.delete(documentId);\n\n this.logger.debug(\n 'CommandsPlugin',\n 'DocumentClosed',\n `Cleaned up command state cache for document: ${documentId}`,\n );\n }\n\n async initialize(): Promise<void> {\n this.logger.info('CommandsPlugin', 'Initialize', 'Commands plugin initialized');\n }\n\n async destroy(): Promise<void> {\n this.commandExecuted$.clear();\n this.commandStateChanged$.clear();\n this.shortcutExecuted$.clear();\n this.categoryChanged$.clear();\n this.commands.clear();\n this.shortcutMap.clear();\n this.previousStates.clear();\n super.destroy();\n }\n\n // ─────────────────────────────────────────────────────────\n // Category Management\n // ─────────────────────────────────────────────────────────\n\n private disableCategoryImpl(category: string): void {\n const current = new Set(this.state.disabledCategories);\n if (!current.has(category)) {\n current.add(category);\n this.dispatch(setDisabledCategories(Array.from(current)));\n this.categoryChanged$.emit({ disabledCategories: Array.from(current) });\n }\n }\n\n private enableCategoryImpl(category: string): void {\n const current = new Set(this.state.disabledCategories);\n if (current.has(category)) {\n current.delete(category);\n this.dispatch(setDisabledCategories(Array.from(current)));\n this.categoryChanged$.emit({ disabledCategories: Array.from(current) });\n }\n }\n\n private toggleCategoryImpl(category: string): void {\n if (this.state.disabledCategories.includes(category)) {\n this.enableCategoryImpl(category);\n } else {\n this.disableCategoryImpl(category);\n }\n }\n\n private setDisabledCategoriesImpl(categories: string[]): void {\n this.dispatch(setDisabledCategories(categories));\n this.categoryChanged$.emit({ disabledCategories: categories });\n }\n\n /**\n * Check if command has any disabled category\n */\n private isCommandCategoryDisabled(command: Command): boolean {\n if (!command.categories?.length) return false;\n return command.categories.some((cat) => this.state.disabledCategories.includes(cat));\n }\n\n // ─────────────────────────────────────────────────────────\n // Capability\n // ─────────────────────────────────────────────────────────\n\n protected buildCapability(): CommandsCapability {\n return {\n resolve: (commandId, documentId) => this.resolve(commandId, documentId),\n execute: (commandId, documentId, source = 'ui') =>\n this.execute(commandId, documentId, source),\n getAllCommands: (documentId) => this.getAllCommands(documentId),\n getCommandsByCategory: (category, documentId) =>\n this.getCommandsByCategory(category, documentId),\n getCommandByShortcut: (shortcut) => this.getCommandByShortcut(shortcut),\n getAllShortcuts: () => new Map(this.shortcutMap),\n forDocument: (documentId) => this.createCommandScope(documentId),\n registerCommand: (command) => this.registerCommand(command),\n unregisterCommand: (commandId) => this.unregisterCommand(commandId),\n\n // Category management\n disableCategory: (category) => this.disableCategoryImpl(category),\n enableCategory: (category) => this.enableCategoryImpl(category),\n toggleCategory: (category) => this.toggleCategoryImpl(category),\n setDisabledCategories: (categories) => this.setDisabledCategoriesImpl(categories),\n getDisabledCategories: () => this.state.disabledCategories,\n isCategoryDisabled: (category) => this.state.disabledCategories.includes(category),\n\n // Events\n onCommandExecuted: this.commandExecuted$.on,\n onCommandStateChanged: this.commandStateChanged$.on,\n onShortcutExecuted: this.shortcutExecuted$.on,\n onCategoryChanged: this.categoryChanged$.on,\n };\n }\n\n // ─────────────────────────────────────────────────────────\n // Document Scoping\n // ─────────────────────────────────────────────────────────\n\n private createCommandScope(documentId: string): CommandScope {\n return {\n resolve: (commandId) => this.resolve(commandId, documentId),\n execute: (commandId, source = 'ui') => this.execute(commandId, documentId, source),\n getAllCommands: () => this.getAllCommands(documentId),\n getCommandsByCategory: (category) => this.getCommandsByCategory(category, documentId),\n onCommandStateChanged: (listener: Listener<Omit<CommandStateChangedEvent, 'documentId'>>) =>\n this.commandStateChanged$.on((event) => {\n if (event.documentId === documentId) {\n const { documentId: _, ...rest } = event;\n listener(rest);\n }\n }),\n };\n }\n\n // ─────────────────────────────────────────────────────────\n // Command Resolution\n // ─────────────────────────────────────────────────────────\n\n private resolve(commandId: string, documentId?: string): ResolvedCommand {\n const resolvedDocId = documentId ?? this.getActiveDocumentId();\n\n const command = this.commands.get(commandId);\n if (!command) {\n throw new Error(`Command not found: ${commandId}`);\n }\n\n const state = this.registry.getStore().getState();\n\n // Resolve label with i18n if available\n const label = this.resolveLabel(command, state, resolvedDocId);\n\n // Resolve shortcuts\n const shortcuts = command.shortcuts\n ? Array.isArray(command.shortcuts)\n ? command.shortcuts\n : [command.shortcuts]\n : undefined;\n\n // Check if disabled via categories OR explicit disabled predicate\n const explicitDisabled = this.resolveDynamic(command.disabled, state, resolvedDocId) ?? false;\n const categoryDisabled = this.isCommandCategoryDisabled(command);\n const isDisabled = explicitDisabled || categoryDisabled;\n\n return {\n id: command.id,\n label,\n icon: this.resolveDynamic(command.icon, state, resolvedDocId),\n iconProps: this.resolveDynamic(command.iconProps, state, resolvedDocId),\n active: this.resolveDynamic(command.active, state, resolvedDocId) ?? false,\n disabled: isDisabled,\n visible: this.resolveDynamic(command.visible, state, resolvedDocId) ?? true,\n shortcuts,\n shortcutLabel: command.shortcutLabel,\n categories: command.categories,\n description: command.description,\n execute: () =>\n command.action({\n registry: this.registry,\n state,\n documentId: resolvedDocId,\n logger: this.logger,\n }),\n };\n }\n\n private resolveLabel(command: Command, state: StoreState<any>, documentId: string): string {\n // Priority: labelKey (with i18n) > label (plain string) > id (fallback)\n const labelKey = this.resolveDynamic(command.labelKey, state, documentId);\n if (labelKey && this.i18n) {\n const params = this.resolveDynamic(command.labelParams, state, documentId);\n return this.i18n.t(labelKey, { params, documentId });\n }\n\n if (command.label) {\n return command.label;\n }\n\n return command.id; // Fallback to ID\n }\n\n private resolveDynamic<T>(\n value: Dynamic<any, T> | undefined,\n state: StoreState<any>,\n documentId: string,\n ): T | undefined {\n if (value === undefined) return undefined;\n\n // Check if it's a function (the dynamic evaluator)\n if (typeof value === 'function') {\n return (\n value as (context: {\n registry: PluginRegistry;\n state: StoreState<any>;\n documentId: string;\n logger: Logger;\n }) => T\n )({\n registry: this.registry,\n state,\n documentId,\n logger: this.logger,\n });\n }\n\n // Otherwise it's the static value\n return value as T;\n }\n\n // ─────────────────────────────────────────────────────────\n // Command Execution\n // ─────────────────────────────────────────────────────────\n\n private execute(\n commandId: string,\n documentId?: string,\n source: 'keyboard' | 'ui' | 'api' = 'ui',\n ): void {\n const resolvedDocId = documentId ?? this.getActiveDocumentId();\n const resolved = this.resolve(commandId, resolvedDocId);\n\n if (resolved.disabled) {\n this.logger.warn(\n 'CommandsPlugin',\n 'ExecutionBlocked',\n `Command '${commandId}' is disabled for document '${resolvedDocId}'`,\n );\n return;\n }\n\n if (!resolved.visible) {\n this.logger.warn(\n 'CommandsPlugin',\n 'ExecutionBlocked',\n `Command '${commandId}' is not visible for document '${resolvedDocId}'`,\n );\n return;\n }\n\n resolved.execute();\n\n this.commandExecuted$.emit({\n commandId,\n documentId: resolvedDocId,\n source,\n });\n\n this.logger.debug(\n 'CommandsPlugin',\n 'CommandExecuted',\n `Command '${commandId}' executed for document '${resolvedDocId}' (source: ${source})`,\n );\n }\n\n // ─────────────────────────────────────────────────────────\n // Command Registration\n // ─────────────────────────────────────────────────────────\n\n private registerCommand(command: Command): void {\n if (this.commands.has(command.id)) {\n this.logger.warn(\n 'CommandsPlugin',\n 'CommandOverwrite',\n `Command '${command.id}' already exists and will be overwritten`,\n );\n }\n\n this.commands.set(command.id, command);\n\n // Register shortcuts\n if (command.shortcuts) {\n const shortcuts = Array.isArray(command.shortcuts) ? command.shortcuts : [command.shortcuts];\n\n shortcuts.forEach((shortcut) => {\n const normalized = this.normalizeShortcut(shortcut);\n this.shortcutMap.set(normalized, command.id);\n });\n }\n\n this.logger.debug('CommandsPlugin', 'CommandRegistered', `Command '${command.id}' registered`);\n }\n\n private unregisterCommand(commandId: string): void {\n const command = this.commands.get(commandId);\n if (!command) return;\n\n // Remove shortcuts\n if (command.shortcuts) {\n const shortcuts = Array.isArray(command.shortcuts) ? command.shortcuts : [command.shortcuts];\n\n shortcuts.forEach((shortcut) => {\n const normalized = this.normalizeShortcut(shortcut);\n this.shortcutMap.delete(normalized);\n });\n }\n\n this.commands.delete(commandId);\n this.logger.debug(\n 'CommandsPlugin',\n 'CommandUnregistered',\n `Command '${commandId}' unregistered`,\n );\n }\n\n // ─────────────────────────────────────────────────────────\n // Shortcuts\n // ─────────────────────────────────────────────────────────\n\n private getCommandByShortcut(shortcut: string): Command | null {\n const normalized = this.normalizeShortcut(shortcut);\n const commandId = this.shortcutMap.get(normalized);\n return commandId ? (this.commands.get(commandId) ?? null) : null;\n }\n\n private normalizeShortcut(shortcut: string): string {\n // Normalize: \"Ctrl+Shift+A\" -> \"ctrl+shift+a\"\n return shortcut.toLowerCase().split('+').sort().join('+');\n }\n\n // ─────────────────────────────────────────────────────────\n // Query Methods\n // ─────────────────────────────────────────────────────────\n\n private getAllCommands(documentId?: string): ResolvedCommand[] {\n const resolvedDocId = documentId ?? this.getActiveDocumentId();\n return Array.from(this.commands.keys()).map((id) => this.resolve(id, resolvedDocId));\n }\n\n private getCommandsByCategory(category: string, documentId?: string): ResolvedCommand[] {\n const resolvedDocId = documentId ?? this.getActiveDocumentId();\n return Array.from(this.commands.values())\n .filter((cmd) => cmd.categories?.includes(category))\n .map((cmd) => this.resolve(cmd.id, resolvedDocId));\n }\n\n // ─────────────────────────────────────────────────────────\n // State Change Detection\n // ─────────────────────────────────────────────────────────\n\n private onGlobalStoreChange(newState: StoreState<any>): void {\n // Get all documents from core state\n const documentIds = Object.keys(newState.core.documents);\n\n // Check each document for command state changes\n documentIds.forEach((documentId) => {\n this.detectCommandChanges(documentId, newState);\n });\n }\n\n private detectCommandChanges(documentId: string, newState: StoreState<any>): void {\n // Skip if document isn't fully loaded yet\n const coreDoc = newState.core.documents[documentId];\n if (!coreDoc || coreDoc.status !== 'loaded') return;\n\n const previousCache = this.previousStates.get(documentId) ?? new Map();\n const changedCommandIds: string[] = [];\n\n this.commands.forEach((command, commandId) => {\n const newResolved = this.resolve(commandId, documentId);\n const prevResolved = previousCache.get(commandId);\n\n if (!prevResolved) {\n // First time resolving for this document\n previousCache.set(commandId, newResolved);\n return;\n }\n\n // Check for changes\n const changes: CommandStateChangedEvent['changes'] = {};\n\n if (prevResolved.active !== newResolved.active) {\n changes.active = newResolved.active;\n }\n if (prevResolved.disabled !== newResolved.disabled) {\n changes.disabled = newResolved.disabled;\n }\n if (prevResolved.visible !== newResolved.visible) {\n changes.visible = newResolved.visible;\n }\n if (prevResolved.label !== newResolved.label) {\n changes.label = newResolved.label;\n }\n if (prevResolved.icon !== newResolved.icon) {\n changes.icon = newResolved.icon;\n }\n if (!arePropsEqual(prevResolved.iconProps, newResolved.iconProps)) {\n changes.iconProps = newResolved.iconProps;\n }\n\n if (Object.keys(changes).length > 0) {\n changedCommandIds.push(commandId);\n previousCache.set(commandId, newResolved);\n\n this.commandStateChanged$.emit({\n commandId,\n documentId,\n changes,\n });\n }\n });\n\n this.previousStates.set(documentId, previousCache);\n }\n}\n","import { Reducer } from '@embedpdf/core';\nimport { CommandsState } from './types';\nimport { CommandsAction, SET_DISABLED_CATEGORIES } from './actions';\n\nexport const initialState: CommandsState = {\n disabledCategories: [],\n};\n\nexport const commandsReducer: Reducer<CommandsState, CommandsAction> = (\n state = initialState,\n action,\n) => {\n switch (action.type) {\n case SET_DISABLED_CATEGORIES:\n return {\n ...state,\n disabledCategories: action.payload,\n };\n\n default:\n return state;\n }\n};\n","import { PluginPackage } from '@embedpdf/core';\nimport { manifest, COMMANDS_PLUGIN_ID } from './manifest';\nimport { CommandsPluginConfig, CommandsState } from './types';\nimport { CommandsPlugin } from './commands-plugin';\nimport { CommandsAction } from './actions';\nimport { commandsReducer, initialState } from './reducer';\n\nexport const CommandsPluginPackage: PluginPackage<\n CommandsPlugin,\n CommandsPluginConfig,\n CommandsState,\n CommandsAction\n> = {\n manifest,\n create: (registry, config) => new CommandsPlugin(COMMANDS_PLUGIN_ID, registry, config),\n reducer: commandsReducer,\n initialState,\n};\n\nexport * from './commands-plugin';\nexport * from './types';\nexport * from './manifest';\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/capability.ts","../src/reducer.ts","../src/types.ts","../src/commands.plugin.ts"],"sourcesContent":["import type { CapabilityToken, PluginContext } from '@embedpdf/core';\nimport { I18nToken } from '@embedpdf/plugin-i18n';\nimport { ShellToken } from '@embedpdf/plugin-shell';\nimport { matchShortcut, parseShortcut } from '@embedpdf/core-ui';\nimport type { KeyStroke, ParsedShortcut } from '@embedpdf/core-ui';\nimport type {\n CommandCtx,\n CommandDef,\n CommandsAction,\n CommandsCapability,\n CommandsState,\n IconAccent,\n ResolvedCommand,\n} from './types';\n\n/** A registered command: the definition plus its pre-parsed shortcuts. */\nexport interface RegisteredCommand {\n readonly def: CommandDef;\n readonly shortcuts: readonly string[];\n readonly parsed: readonly ParsedShortcut[];\n}\n\nexport type CommandRegistry = Map<string, RegisteredCommand>;\n\nexport function registerCommand(registry: CommandRegistry, def: CommandDef): void {\n if (registry.has(def.id)) throw new Error(`[commands] duplicate command: ${def.id}`);\n const shortcuts = def.shortcut === undefined ? [] : ([] as string[]).concat(def.shortcut);\n registry.set(def.id, { def, shortcuts, parsed: shortcuts.map(parseShortcut) });\n}\n\nconst panelTarget = (def: CommandDef): { id: string; exclusive?: string } | null =>\n def.panel === undefined ? null : typeof def.panel === 'string' ? { id: def.panel } : def.panel;\n\nexport function createCommandsCapability(\n ctx: PluginContext<CommandsState, CommandsAction>,\n registry: CommandRegistry,\n): CommandsCapability {\n /** Bind capability resolution to the command's target document. The kernel\n * resolves workspace tokens regardless of the document argument, so one\n * code path serves both scopes. */\n const commandCtx = (documentId?: string): CommandCtx => {\n const target = documentId ?? ctx.core().activeId;\n const get = <T>(token: CapabilityToken<T>): T =>\n target ? ctx.forDocument(token, target) : ctx.get(token);\n return {\n documentId: target,\n core: ctx.core,\n get,\n tryGet: <T>(token: CapabilityToken<T>): T | null => {\n try {\n return get(token);\n } catch {\n return null;\n }\n },\n };\n };\n\n /** Derivations run against live state; a derivation that throws (e.g. it\n * needs a document and none is open) falls back to the safe default —\n * the button renders, disabled, exactly like v2's empty state. */\n const derive = (\n fn: ((c: CommandCtx) => boolean) | undefined,\n c: CommandCtx,\n fallback: boolean,\n ): boolean => {\n if (!fn) return fallback;\n try {\n return fn(c);\n } catch {\n return fallback;\n }\n };\n\n /** Same guard as `derive`, for the icon-accent derivation: a throw (no\n * document, provider missing) means \"no accent\" — the icon renders plain. */\n const deriveAccent = (\n fn: ((c: CommandCtx) => IconAccent | null) | undefined,\n c: CommandCtx,\n ): IconAccent | undefined => {\n if (!fn) return undefined;\n try {\n return fn(c) ?? undefined;\n } catch {\n return undefined;\n }\n };\n\n const resolve = (id: string, documentId?: string): ResolvedCommand | null => {\n const entry = registry.get(id);\n if (!entry) return null;\n const { def } = entry;\n const c = commandCtx(documentId);\n\n const disabled = ctx.getState().disabledCategories;\n const categoryHidden = (def.categories ?? []).some((cat) => disabled.includes(cat));\n\n const i18n = c.tryGet(I18nToken);\n const label = i18n ? i18n.t(def.labelKey) : def.labelKey;\n\n // Surface-target commands derive `active` from the surface's open state\n // unless the definition overrides it.\n let active: boolean;\n if (def.active) {\n active = derive(def.active, c, false);\n } else {\n const shell = c.tryGet(ShellToken);\n const panel = panelTarget(def);\n active = shell\n ? def.menu\n ? shell.isMenuOpen(def.menu)\n : panel\n ? shell.isOpen(panel.id)\n : def.modal\n ? shell.isOpen(def.modal)\n : false\n : false;\n }\n\n return {\n id: def.id,\n label,\n icon: def.icon,\n iconAccent: deriveAccent(def.iconAccent, c),\n shortcuts: entry.shortcuts,\n menu: def.menu,\n enabled: derive(def.enabled, c, true) && !categoryHidden,\n active,\n visible: derive(def.visible, c, true) && !categoryHidden,\n categories: def.categories ?? [],\n };\n };\n\n const execute = (id: string, documentId?: string): void => {\n const entry = registry.get(id);\n if (!entry) return;\n const resolved = resolve(id, documentId);\n if (!resolved || !resolved.enabled || !resolved.visible) return;\n const c = commandCtx(documentId);\n\n if (entry.def.run) {\n entry.def.run(c);\n return;\n }\n // Default routing for declarative surface targets.\n const shell = c.tryGet(ShellToken);\n if (!shell) return;\n const panel = panelTarget(entry.def);\n if (entry.def.menu) shell.toggleMenu(entry.def.menu);\n else if (panel) shell.toggle(panel.id, { exclusive: panel.exclusive });\n else if (entry.def.modal) shell.toggle(entry.def.modal, { exclusive: 'modal' });\n };\n\n return {\n register: (def) => registerCommand(registry, def),\n unregister: (id) => void registry.delete(id),\n has: (id) => registry.has(id),\n ids: () => [...registry.keys()],\n\n resolve,\n search: (query, documentId) => {\n const q = query.trim().toLowerCase();\n const hits: ResolvedCommand[] = [];\n for (const id of registry.keys()) {\n const r = resolve(id, documentId);\n if (!r || !r.visible) continue;\n if (q === '' || r.label.toLowerCase().includes(q) || r.id.includes(q)) hits.push(r);\n }\n return hits;\n },\n menuTarget: (id) => {\n const entry = registry.get(id);\n return entry ? { menu: entry.def.menu } : null;\n },\n\n execute,\n matchStroke: (stroke: KeyStroke, opts) => {\n for (const [id, entry] of registry) {\n if (entry.parsed.some((p) => matchShortcut(p, stroke, opts))) return id;\n }\n return null;\n },\n\n disabledCategories: () => ctx.getState().disabledCategories,\n isCategoryDisabled: (category) => ctx.getState().disabledCategories.includes(category),\n disableCategory: (category) => ctx.dispatch({ type: 'COMMANDS/DISABLE_CATEGORY', category }),\n enableCategory: (category) => ctx.dispatch({ type: 'COMMANDS/ENABLE_CATEGORY', category }),\n setDisabledCategories: (categories) =>\n ctx.dispatch({ type: 'COMMANDS/SET_DISABLED_CATEGORIES', categories }),\n };\n}\n","import type { CommandsAction, CommandsState } from './types';\n\nexport const initialCommandsState: CommandsState = {\n disabledCategories: [],\n};\n\nexport function commandsReducer(state: CommandsState, action: CommandsAction): CommandsState {\n switch (action.type) {\n case 'COMMANDS/DISABLE_CATEGORY':\n return state.disabledCategories.includes(action.category)\n ? state\n : { disabledCategories: [...state.disabledCategories, action.category] };\n case 'COMMANDS/ENABLE_CATEGORY':\n return state.disabledCategories.includes(action.category)\n ? { disabledCategories: state.disabledCategories.filter((c) => c !== action.category) }\n : state;\n case 'COMMANDS/SET_DISABLED_CATEGORIES':\n return { disabledCategories: [...action.categories] };\n default:\n return state;\n }\n}\n","import { createCapabilityToken } from '@embedpdf/core';\nimport type { CapabilityToken, CoreState } from '@embedpdf/core';\nimport type { KeyStroke } from '@embedpdf/core-ui';\n\n/**\n * @embedpdf/plugin-commands — the contract.\n *\n * Commands are the single vocabulary of verbs: toolbars, menus, contextual\n * strips, shortcuts, and the palette are all projections of this registry.\n * The plugin ships ZERO commands (mechanism here, definitions in the product\n * — same split as plugin-i18n's locale packs).\n *\n * Command state is a pure DERIVATION over the store: `resolve()` reads other\n * capabilities' selectors at call time, so any store change is reflected on\n * the next read and the framework binding's one change stream makes every\n * consumer reactive. There is no CommandStateChangedEvent, no diffing, no\n * cache — v2's entire notification apparatus has no v3 equivalent because\n * the reactive store subsumes it.\n *\n * Definitions hold functions, so they live in the plugin's registry (config\n * + `register()`), never in the store; store state is only the serializable\n * `disabledCategories`.\n */\n\n/** What a derivation or `run` sees: capability resolution bound to the\n * command's target document (explicit, else the active one). */\nexport interface CommandCtx {\n /** The target document, or null when no document is open. */\n readonly documentId: string | null;\n core(): CoreState;\n /** Resolve a capability; document-scoped tokens bind to the target document. */\n get<T>(token: CapabilityToken<T>): T;\n /** Like `get`, but null when unavailable (no provider / no document). */\n tryGet<T>(token: CapabilityToken<T>): T | null;\n}\n\n/**\n * Up to two theme colors accompanying a command's icon — typed FACTS any\n * renderer can interpret (tint a glyph's slots, show a swatch), never\n * renderer props. The registry carries it the way it carries `icon`: as\n * data it doesn't interpret. Deliberately NOT v2's `iconProps` bag —\n * renderer-specific needs live app-side, joined by command id.\n */\nexport interface IconAccent {\n /** The mark: stroke / markup / font color. */\n readonly primary?: string;\n /** The fill, when there is one. */\n readonly secondary?: string;\n}\n\nexport interface CommandDef {\n /** Convention: 'domain:verb' — 'zoom:in', 'mode:annotate', 'panel:search'. */\n readonly id: string;\n /** i18n key, resolved through I18nToken when present (else shown verbatim). */\n readonly labelKey: string;\n readonly icon?: string;\n /** Live color accent for the icon (a tool previewing its drawing defaults).\n * A pure derivation over the store, exactly like `active`/`enabled`. */\n readonly iconAccent?: (ctx: CommandCtx) => IconAccent | null;\n /** 'Mod+K' style (ui-core grammar). Multiple bindings allowed. */\n readonly shortcut?: string | readonly string[];\n /** Feature-gating tags: a disabled category hides its commands everywhere. */\n readonly categories?: readonly string[];\n\n // ── declarative surface targets ──────────────────────────────────────────\n // A command that opens chrome DECLARES what it opens instead of doing it\n // imperatively. This is load-bearing: buttons render carets/aria-haspopup,\n // `active` derives automatically from the surface's open state, and the\n // overflow projection renders `menu` targets as nested submenus.\n /** Toggles a named dropdown menu (a MenuSchema id in the app's chrome). */\n readonly menu?: string;\n /** Toggles a named shell surface, optionally exclusive within a tag ('left'…). */\n readonly panel?: string | { readonly id: string; readonly exclusive?: string };\n /** Toggles a modal surface (exclusive within the built-in 'modal' tag). */\n readonly modal?: string;\n\n // ── pure derivations over the store ──────────────────────────────────────\n readonly enabled?: (ctx: CommandCtx) => boolean;\n readonly active?: (ctx: CommandCtx) => boolean;\n readonly visible?: (ctx: CommandCtx) => boolean;\n\n /** The verb. Optional for pure surface-target commands. Runs before the\n * default target routing when both are present. */\n readonly run?: (ctx: CommandCtx) => void;\n}\n\n/** A command as a renderer sees it — everything resolved for the target document. */\nexport interface ResolvedCommand {\n readonly id: string;\n readonly label: string;\n readonly icon?: string;\n readonly iconAccent?: IconAccent;\n readonly shortcuts: readonly string[];\n readonly menu?: string;\n readonly enabled: boolean;\n readonly active: boolean;\n readonly visible: boolean;\n readonly categories: readonly string[];\n}\n\n/** Value equality over resolved commands — `resolve()` mints a fresh object\n * per read, so reactive bindings memo by value to re-render on real change. */\nexport const resolvedCommandsEqual = (\n a: ResolvedCommand | null,\n b: ResolvedCommand | null,\n): boolean => {\n if (a === b) return true;\n if (!a || !b) return false;\n return (\n a.id === b.id &&\n a.label === b.label &&\n a.icon === b.icon &&\n // by value: resolve() mints a fresh accent object each read\n a.iconAccent?.primary === b.iconAccent?.primary &&\n a.iconAccent?.secondary === b.iconAccent?.secondary &&\n a.menu === b.menu &&\n a.enabled === b.enabled &&\n a.active === b.active &&\n a.visible === b.visible &&\n a.shortcuts.length === b.shortcuts.length &&\n a.shortcuts.every((s, i) => s === b.shortcuts[i])\n );\n};\n\nexport interface CommandsState {\n readonly disabledCategories: readonly string[];\n}\n\nexport type CommandsAction =\n | { type: 'COMMANDS/DISABLE_CATEGORY'; category: string }\n | { type: 'COMMANDS/ENABLE_CATEGORY'; category: string }\n | { type: 'COMMANDS/SET_DISABLED_CATEGORIES'; categories: readonly string[] };\n\nexport interface CommandsConfig {\n /** The app's command definitions (content — the plugin ships none). */\n commands?: readonly CommandDef[];\n /** Categories disabled at startup (host feature-gating). */\n disabledCategories?: readonly string[];\n}\n\nexport interface CommandsCapability {\n // ── registry ──\n register(def: CommandDef): void;\n unregister(id: string): void;\n has(id: string): boolean;\n ids(): string[];\n\n // ── resolution (pure reads; reactive through the store) ──\n resolve(id: string, documentId?: string): ResolvedCommand | null;\n /** Palette query: visible commands whose resolved label matches. */\n search(query: string, documentId?: string): ResolvedCommand[];\n /** The one fact the overflow projection needs (ResolveMenuTarget-shaped). */\n menuTarget(id: string): { menu?: string } | null;\n\n // ── execution (the ONLY path; guarded by enabled/visible) ──\n execute(id: string, documentId?: string): void;\n /** Match a keystroke against every registered shortcut → command id or null. */\n matchStroke(stroke: KeyStroke, opts: { isMac: boolean }): string | null;\n\n // ── category gating ──\n disabledCategories(): readonly string[];\n isCategoryDisabled(category: string): boolean;\n disableCategory(category: string): void;\n enableCategory(category: string): void;\n setDisabledCategories(categories: readonly string[]): void;\n}\n\nexport const CommandsToken = createCapabilityToken<CommandsCapability>('commands');\n","import { definePlugin } from '@embedpdf/core';\nimport { createCommandsCapability, registerCommand } from './capability';\nimport type { CommandRegistry } from './capability';\nimport { commandsReducer, initialCommandsState } from './reducer';\nimport { CommandsToken } from './types';\nimport type { CommandsAction, CommandsCapability, CommandsConfig, CommandsState } from './types';\n\n/**\n * The commands plugin: workspace-scoped (one vocabulary for the whole\n * workspace; resolution/execution bind to a target document per call).\n * Definitions live in this closure — never in the store (they hold\n * functions); the store slice holds only `disabledCategories`.\n */\nexport const commandsPlugin = (config?: CommandsConfig) => {\n const registry: CommandRegistry = new Map();\n for (const def of config?.commands ?? []) registerCommand(registry, def);\n\n return definePlugin<CommandsState, CommandsAction, CommandsCapability>({\n id: 'commands',\n scope: 'workspace',\n token: CommandsToken,\n initialState: {\n ...initialCommandsState,\n disabledCategories: [...(config?.disabledCategories ?? [])],\n },\n reduce: commandsReducer,\n capability: (ctx) => createCommandsCapability(ctx, registry),\n });\n};\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,63 +1,44 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lofcz/embedpdf-plugin-commands",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.0-next.7",
|
|
4
|
+
"private": false,
|
|
5
|
+
"description": "The command registry: the single vocabulary of verbs. Definitions are app content (mechanism/content split); state (label · icon · enabled · active · visible) derives purely from other capabilities, so every consumer is reactive through the kernel's one change stream — no events, no diffing.",
|
|
4
6
|
"type": "module",
|
|
5
|
-
"
|
|
6
|
-
"main": "./dist/index.cjs",
|
|
7
|
+
"main": "./dist/index.js",
|
|
7
8
|
"module": "./dist/index.js",
|
|
8
9
|
"types": "./dist/index.d.ts",
|
|
9
10
|
"exports": {
|
|
10
11
|
".": {
|
|
11
|
-
"types": "./dist/index.d.ts",
|
|
12
12
|
"import": "./dist/index.js",
|
|
13
13
|
"require": "./dist/index.cjs"
|
|
14
14
|
},
|
|
15
|
-
"./
|
|
16
|
-
"types": "./dist/react/index.d.ts",
|
|
17
|
-
"import": "./dist/react/index.js",
|
|
18
|
-
"require": "./dist/react/index.cjs"
|
|
19
|
-
}
|
|
15
|
+
"./package.json": "./package.json"
|
|
20
16
|
},
|
|
17
|
+
"files": [
|
|
18
|
+
"dist"
|
|
19
|
+
],
|
|
21
20
|
"dependencies": {
|
|
22
|
-
"@embedpdf/
|
|
21
|
+
"@embedpdf/core": "npm:@lofcz/embedpdf-core@3.0.0-next.7",
|
|
22
|
+
"@embedpdf/plugin-i18n": "npm:@lofcz/embedpdf-plugin-i18n@3.0.0-next.7",
|
|
23
|
+
"@embedpdf/plugin-shell": "npm:@lofcz/embedpdf-plugin-shell@3.0.0-next.7",
|
|
24
|
+
"@embedpdf/core-ui": "npm:@lofcz/embedpdf-core-ui@3.0.0-next.7"
|
|
23
25
|
},
|
|
24
26
|
"devDependencies": {
|
|
25
|
-
"@types/react": "^18.2.0",
|
|
26
27
|
"typescript": "^5.0.0",
|
|
27
|
-
"
|
|
28
|
-
"@embedpdf/
|
|
29
|
-
"@embedpdf/plugin-i18n": "npm:@lofcz/embedpdf-plugin-i18n@2.15.0"
|
|
28
|
+
"vitest": "^2.1.9",
|
|
29
|
+
"@embedpdf/tooling-build": "npm:@lofcz/embedpdf-tooling-build@0.0.0"
|
|
30
30
|
},
|
|
31
|
-
"
|
|
32
|
-
|
|
33
|
-
"react-dom": ">=16.8.0",
|
|
34
|
-
"@embedpdf/core": "2.15.0@lofcz/embedpdf-core@*"
|
|
35
|
-
},
|
|
36
|
-
"files": [
|
|
37
|
-
"dist",
|
|
38
|
-
"README.md"
|
|
39
|
-
],
|
|
31
|
+
"license": "Apache-2.0",
|
|
32
|
+
"sideEffects": false,
|
|
40
33
|
"repository": {
|
|
41
34
|
"type": "git",
|
|
42
|
-
"url": "https://github.com/lofcz/embed-pdf-viewer",
|
|
43
|
-
"directory": "packages/plugin
|
|
44
|
-
},
|
|
45
|
-
"homepage": "https://www.embedpdf.com/docs",
|
|
46
|
-
"bugs": {
|
|
47
|
-
"url": "https://github.com/embedpdf/embed-pdf-viewer/issues"
|
|
48
|
-
},
|
|
49
|
-
"publishConfig": {
|
|
50
|
-
"access": "public"
|
|
35
|
+
"url": "git+https://github.com/lofcz/embed-pdf-viewer.git",
|
|
36
|
+
"directory": "packages/plugin/commands"
|
|
51
37
|
},
|
|
52
38
|
"scripts": {
|
|
53
|
-
"build
|
|
54
|
-
"build:react": "vite build --mode react",
|
|
55
|
-
"build:preact": "vite build --mode preact",
|
|
56
|
-
"build:vue": "vite build --mode vue",
|
|
57
|
-
"build:svelte": "vite build --mode svelte",
|
|
58
|
-
"build": "pnpm run clean && concurrently -c auto -n base,react \"vite build --mode base\" \"vite build --mode react\"",
|
|
39
|
+
"build": "epdf-build",
|
|
59
40
|
"clean": "rimraf dist",
|
|
60
|
-
"
|
|
61
|
-
"
|
|
41
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
42
|
+
"test": "vitest run"
|
|
62
43
|
}
|
|
63
44
|
}
|
package/dist/lib/actions.d.ts
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import { Action } from '@embedpdf/core';
|
|
2
|
-
export declare const SET_DISABLED_CATEGORIES = "COMMANDS/SET_DISABLED_CATEGORIES";
|
|
3
|
-
export interface SetDisabledCategoriesAction extends Action {
|
|
4
|
-
type: typeof SET_DISABLED_CATEGORIES;
|
|
5
|
-
payload: string[];
|
|
6
|
-
}
|
|
7
|
-
export type CommandsAction = SetDisabledCategoriesAction;
|
|
8
|
-
export declare const setDisabledCategories: (categories: string[]) => SetDisabledCategoriesAction;
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import { BasePlugin, PluginRegistry } from '@embedpdf/core';
|
|
2
|
-
import { CommandsCapability, CommandsPluginConfig, CommandsState } from './types';
|
|
3
|
-
import { CommandsAction } from './actions';
|
|
4
|
-
export declare class CommandsPlugin extends BasePlugin<CommandsPluginConfig, CommandsCapability, CommandsState, CommandsAction> {
|
|
5
|
-
static readonly id: "commands";
|
|
6
|
-
private commands;
|
|
7
|
-
private i18n;
|
|
8
|
-
private shortcutMap;
|
|
9
|
-
private readonly commandExecuted$;
|
|
10
|
-
private readonly commandStateChanged$;
|
|
11
|
-
private readonly shortcutExecuted$;
|
|
12
|
-
private readonly categoryChanged$;
|
|
13
|
-
private previousStates;
|
|
14
|
-
constructor(id: string, registry: PluginRegistry, config: CommandsPluginConfig);
|
|
15
|
-
protected onDocumentClosed(documentId: string): void;
|
|
16
|
-
initialize(): Promise<void>;
|
|
17
|
-
destroy(): Promise<void>;
|
|
18
|
-
private disableCategoryImpl;
|
|
19
|
-
private enableCategoryImpl;
|
|
20
|
-
private toggleCategoryImpl;
|
|
21
|
-
private setDisabledCategoriesImpl;
|
|
22
|
-
/**
|
|
23
|
-
* Check if command has any disabled category
|
|
24
|
-
*/
|
|
25
|
-
private isCommandCategoryDisabled;
|
|
26
|
-
protected buildCapability(): CommandsCapability;
|
|
27
|
-
private createCommandScope;
|
|
28
|
-
private resolve;
|
|
29
|
-
private resolveLabel;
|
|
30
|
-
private resolveDynamic;
|
|
31
|
-
private execute;
|
|
32
|
-
private registerCommand;
|
|
33
|
-
private unregisterCommand;
|
|
34
|
-
private getCommandByShortcut;
|
|
35
|
-
private normalizeShortcut;
|
|
36
|
-
private getAllCommands;
|
|
37
|
-
private getCommandsByCategory;
|
|
38
|
-
private onGlobalStoreChange;
|
|
39
|
-
private detectCommandChanges;
|
|
40
|
-
}
|
package/dist/lib/index.d.ts
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import { PluginPackage } from '@embedpdf/core';
|
|
2
|
-
import { CommandsPluginConfig, CommandsState } from './types';
|
|
3
|
-
import { CommandsPlugin } from './commands-plugin';
|
|
4
|
-
import { CommandsAction } from './actions';
|
|
5
|
-
export declare const CommandsPluginPackage: PluginPackage<CommandsPlugin, CommandsPluginConfig, CommandsState, CommandsAction>;
|
|
6
|
-
export * from './commands-plugin';
|
|
7
|
-
export * from './types';
|
|
8
|
-
export * from './manifest';
|
package/dist/lib/manifest.d.ts
DELETED
package/dist/lib/reducer.d.ts
DELETED
package/dist/lib/types.d.ts
DELETED
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
import { BasePluginConfig, CoreState, EventHook, PluginRegistry } from '@embedpdf/core';
|
|
2
|
-
import { Logger } from '@embedpdf/models';
|
|
3
|
-
import { TranslationKey } from '@embedpdf/plugin-i18n';
|
|
4
|
-
export type Dynamic<TStore, T> = T | ((context: {
|
|
5
|
-
registry: PluginRegistry;
|
|
6
|
-
state: TStore;
|
|
7
|
-
documentId: string;
|
|
8
|
-
logger: Logger;
|
|
9
|
-
}) => T);
|
|
10
|
-
export interface IconProps {
|
|
11
|
-
primaryColor?: string;
|
|
12
|
-
secondaryColor?: string;
|
|
13
|
-
className?: string;
|
|
14
|
-
title?: string;
|
|
15
|
-
}
|
|
16
|
-
export interface Command<TStore = any> {
|
|
17
|
-
id: string;
|
|
18
|
-
label?: string;
|
|
19
|
-
labelKey?: Dynamic<TStore, TranslationKey>;
|
|
20
|
-
labelParams?: Dynamic<TStore, Record<string, string | number>>;
|
|
21
|
-
icon?: Dynamic<TStore, string>;
|
|
22
|
-
iconProps?: Dynamic<TStore, IconProps>;
|
|
23
|
-
action: (context: {
|
|
24
|
-
registry: PluginRegistry;
|
|
25
|
-
state: TStore;
|
|
26
|
-
documentId: string;
|
|
27
|
-
logger: Logger;
|
|
28
|
-
}) => void;
|
|
29
|
-
active?: Dynamic<TStore, boolean>;
|
|
30
|
-
disabled?: Dynamic<TStore, boolean>;
|
|
31
|
-
visible?: Dynamic<TStore, boolean>;
|
|
32
|
-
shortcuts?: string | string[];
|
|
33
|
-
shortcutLabel?: string;
|
|
34
|
-
categories?: string[];
|
|
35
|
-
description?: string;
|
|
36
|
-
}
|
|
37
|
-
export interface ResolvedCommand {
|
|
38
|
-
id: string;
|
|
39
|
-
label: string;
|
|
40
|
-
icon?: string;
|
|
41
|
-
iconProps?: IconProps;
|
|
42
|
-
active: boolean;
|
|
43
|
-
disabled: boolean;
|
|
44
|
-
visible: boolean;
|
|
45
|
-
shortcuts?: string[];
|
|
46
|
-
shortcutLabel?: string;
|
|
47
|
-
categories?: string[];
|
|
48
|
-
description?: string;
|
|
49
|
-
execute: () => void;
|
|
50
|
-
}
|
|
51
|
-
export interface GlobalStoreState<TPlugins extends Record<string, any> = {}> {
|
|
52
|
-
core: CoreState;
|
|
53
|
-
plugins: TPlugins;
|
|
54
|
-
}
|
|
55
|
-
export interface CommandsPluginConfig extends BasePluginConfig {
|
|
56
|
-
commands: Record<string, Command>;
|
|
57
|
-
/** Categories to disable at initialization */
|
|
58
|
-
disabledCategories?: string[];
|
|
59
|
-
}
|
|
60
|
-
export interface CommandsState {
|
|
61
|
-
/** Globally disabled command categories */
|
|
62
|
-
disabledCategories: string[];
|
|
63
|
-
}
|
|
64
|
-
export interface CommandExecutedEvent {
|
|
65
|
-
commandId: string;
|
|
66
|
-
documentId: string;
|
|
67
|
-
source: 'keyboard' | 'ui' | 'api';
|
|
68
|
-
}
|
|
69
|
-
export interface CommandStateChangedEvent {
|
|
70
|
-
commandId: string;
|
|
71
|
-
documentId: string;
|
|
72
|
-
changes: {
|
|
73
|
-
active?: boolean;
|
|
74
|
-
disabled?: boolean;
|
|
75
|
-
visible?: boolean;
|
|
76
|
-
label?: string;
|
|
77
|
-
icon?: string;
|
|
78
|
-
iconProps?: IconProps;
|
|
79
|
-
};
|
|
80
|
-
}
|
|
81
|
-
export interface ShortcutExecutedEvent {
|
|
82
|
-
shortcut: string;
|
|
83
|
-
commandId: string;
|
|
84
|
-
documentId: string;
|
|
85
|
-
}
|
|
86
|
-
export interface CategoryChangedEvent {
|
|
87
|
-
disabledCategories: string[];
|
|
88
|
-
}
|
|
89
|
-
export interface CommandScope {
|
|
90
|
-
resolve(commandId: string): ResolvedCommand;
|
|
91
|
-
execute(commandId: string, source?: 'keyboard' | 'ui' | 'api'): void;
|
|
92
|
-
getAllCommands(): ResolvedCommand[];
|
|
93
|
-
getCommandsByCategory(category: string): ResolvedCommand[];
|
|
94
|
-
onCommandStateChanged: EventHook<Omit<CommandStateChangedEvent, 'documentId'>>;
|
|
95
|
-
}
|
|
96
|
-
export interface CommandsCapability {
|
|
97
|
-
resolve(commandId: string, documentId?: string): ResolvedCommand;
|
|
98
|
-
execute(commandId: string, documentId?: string, source?: 'keyboard' | 'ui' | 'api'): void;
|
|
99
|
-
getAllCommands(documentId?: string): ResolvedCommand[];
|
|
100
|
-
getCommandsByCategory(category: string, documentId?: string): ResolvedCommand[];
|
|
101
|
-
getCommandByShortcut(shortcut: string): Command | null;
|
|
102
|
-
getAllShortcuts(): Map<string, string>;
|
|
103
|
-
forDocument(documentId: string): CommandScope;
|
|
104
|
-
registerCommand(command: Command): void;
|
|
105
|
-
unregisterCommand(commandId: string): void;
|
|
106
|
-
disableCategory(category: string): void;
|
|
107
|
-
enableCategory(category: string): void;
|
|
108
|
-
toggleCategory(category: string): void;
|
|
109
|
-
setDisabledCategories(categories: string[]): void;
|
|
110
|
-
getDisabledCategories(): string[];
|
|
111
|
-
isCategoryDisabled(category: string): boolean;
|
|
112
|
-
onCommandExecuted: EventHook<CommandExecutedEvent>;
|
|
113
|
-
onCommandStateChanged: EventHook<CommandStateChangedEvent>;
|
|
114
|
-
onShortcutExecuted: EventHook<ShortcutExecutedEvent>;
|
|
115
|
-
onCategoryChanged: EventHook<CategoryChangedEvent>;
|
|
116
|
-
}
|
package/dist/react/adapter.d.ts
DELETED
package/dist/react/core.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from '@embedpdf/core/react';
|
package/dist/react/index.cjs
DELETED
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("@embedpdf/core"),t=require("@embedpdf/plugin-commands"),r=require("react"),o=require("@embedpdf/core/react"),n=()=>o.useCapability(t.CommandsPlugin.id);function s(){const{provides:e}=n();return r.useEffect(()=>{if(!e)return;const t=function(e){return t=>{const r=t.composedPath()[0]||t.target;if(("INPUT"===r.tagName||"TEXTAREA"===r.tagName||r.isContentEditable)&&"Tab"!==t.key)return;const o=function(e){const t=[];e.ctrlKey&&t.push("ctrl"),e.shiftKey&&t.push("shift"),e.altKey&&t.push("alt"),e.metaKey&&t.push("meta");let r=e.key.toLowerCase();return" "===r&&(r="space"),["control","shift","alt","meta"].includes(r)?null:[...t,r].sort().join("+")}(t);if(!o)return;const n=e.getCommandByShortcut(o);if(!n)return;const s=e.resolve(n.id);!s.disabled&&s.visible&&(t.preventDefault(),t.stopPropagation(),e.execute(n.id,void 0,"keyboard"))}}(e);return document.addEventListener("keydown",t),()=>document.removeEventListener("keydown",t)},[e]),null}const u=e.createPluginPackage(t.CommandsPluginPackage).addUtility(s).build();exports.CommandsPluginPackage=u,exports.KeyboardShortcuts=s,exports.useCommand=(e,t)=>{const{provides:o}=n(),[s,u]=r.useState(()=>o?o.resolve(e,t):null);return r.useEffect(()=>{if(!o)return void u(null);u(o.resolve(e,t));return o.onCommandStateChanged(r=>{r.commandId===e&&r.documentId===t&&u(o.resolve(e,t))})},[o,e,t]),s},exports.useCommandExecutor=e=>{const{provides:t}=n();return r=>{t&&t.execute(r,e,"ui")}},exports.useCommandsCapability=n,exports.useCommandsPlugin=()=>o.usePlugin(t.CommandsPlugin.id),Object.keys(t).forEach(e=>{"default"===e||Object.prototype.hasOwnProperty.call(exports,e)||Object.defineProperty(exports,e,{enumerable:!0,get:()=>t[e]})});
|
|
2
|
-
//# sourceMappingURL=index.cjs.map
|
package/dist/react/index.cjs.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","sources":["../../src/shared/hooks/use-commands.ts","../../src/shared/components/keyboard-shortcuts.tsx","../../src/shared/utils/keyboard-handler.ts","../../src/shared/index.ts"],"sourcesContent":["import { useCapability, usePlugin } from '@embedpdf/core/@framework';\nimport { CommandsPlugin, ResolvedCommand } from '@embedpdf/plugin-commands';\nimport { useState, useEffect } from '@framework';\n\nexport const useCommandsCapability = () => useCapability<CommandsPlugin>(CommandsPlugin.id);\nexport const useCommandsPlugin = () => usePlugin<CommandsPlugin>(CommandsPlugin.id);\n\n/**\n * Hook to get a reactive command for a specific document\n * Automatically updates when command state changes\n * @param commandId Command ID\n * @param documentId Document ID\n * @returns ResolvedCommand or null if not available\n */\nexport const useCommand = (commandId: string, documentId: string): ResolvedCommand | null => {\n const { provides } = useCommandsCapability();\n const [command, setCommand] = useState<ResolvedCommand | null>(() =>\n provides ? provides.resolve(commandId, documentId) : null,\n );\n\n useEffect(() => {\n if (!provides) {\n setCommand(null);\n return;\n }\n\n // Initial resolve\n setCommand(provides.resolve(commandId, documentId));\n\n // Subscribe to state changes for this command + document\n const unsubscribe = provides.onCommandStateChanged((event) => {\n if (event.commandId === commandId && event.documentId === documentId) {\n setCommand(provides.resolve(commandId, documentId));\n }\n });\n\n return unsubscribe;\n }, [provides, commandId, documentId]);\n\n return command;\n};\n\n/**\n * Hook to execute a command\n */\nexport const useCommandExecutor = (documentId: string) => {\n const { provides } = useCommandsCapability();\n\n return (commandId: string) => {\n if (provides) {\n provides.execute(commandId, documentId, 'ui');\n }\n };\n};\n","import { useEffect } from '@framework';\nimport { useCommandsCapability } from '../hooks';\nimport { createKeyDownHandler } from '../utils';\n\n/**\n * Utility component that listens to keyboard events\n * and executes commands based on shortcuts.\n * This component doesn't render anything, it just sets up keyboard shortcuts.\n */\nexport function KeyboardShortcuts() {\n const { provides: commands } = useCommandsCapability();\n\n useEffect(() => {\n if (!commands) return;\n\n const handleKeyDown = createKeyDownHandler(commands);\n\n document.addEventListener('keydown', handleKeyDown);\n return () => document.removeEventListener('keydown', handleKeyDown);\n }, [commands]);\n\n // This component is only used to set up keyboard shortcuts when the plugin is initialized.\n return null;\n}\n","import { CommandsCapability } from '../../lib/types';\n\n/**\n * Build a shortcut string from a keyboard event\n * @example Ctrl+Shift+A -> \"ctrl+shift+a\"\n */\nexport function buildShortcutString(event: KeyboardEvent): string | null {\n const modifiers: string[] = [];\n\n if (event.ctrlKey) modifiers.push('ctrl');\n if (event.shiftKey) modifiers.push('shift');\n if (event.altKey) modifiers.push('alt');\n if (event.metaKey) modifiers.push('meta');\n\n // Only add non-modifier keys\n let key = event.key.toLowerCase();\n if (key === ' ') key = 'space';\n const isModifier = ['control', 'shift', 'alt', 'meta'].includes(key);\n\n if (isModifier) {\n return null; // Just a modifier, no command\n }\n\n const parts = [...modifiers, key];\n return parts.sort().join('+');\n}\n\n/**\n * Handle keyboard events and execute commands based on shortcuts\n */\nexport function createKeyDownHandler(commands: CommandsCapability) {\n return (event: KeyboardEvent) => {\n // Use composedPath to get the actual target element, even inside Shadow DOM\n const composedPath = event.composedPath();\n const target = (composedPath[0] || event.target) as HTMLElement;\n\n // Don't handle shortcuts if target is an input, textarea, or contentEditable\n // Exception: allow Tab/Shift+Tab through for form field navigation\n if (target.tagName === 'INPUT' || target.tagName === 'TEXTAREA' || target.isContentEditable) {\n if (event.key !== 'Tab') return;\n }\n\n const shortcut = buildShortcutString(event);\n if (!shortcut) return;\n\n const command = commands.getCommandByShortcut(shortcut);\n if (!command) return;\n\n // Resolve without document ID - will use active document\n const resolved = commands.resolve(command.id);\n\n if (resolved.disabled || !resolved.visible) {\n return;\n }\n\n // Execute and prevent default (documentId is optional now)\n event.preventDefault();\n event.stopPropagation();\n commands.execute(command.id, undefined, 'keyboard');\n };\n}\n","import { createPluginPackage } from '@embedpdf/core';\nimport { CommandsPluginPackage as BaseCommandsPackage } from '@embedpdf/plugin-commands';\n\nimport { KeyboardShortcuts } from './components';\n\nexport * from './hooks';\nexport * from './components';\nexport * from '@embedpdf/plugin-commands';\n\nexport const CommandsPluginPackage = createPluginPackage(BaseCommandsPackage)\n .addUtility(KeyboardShortcuts)\n .build();\n"],"names":["useCommandsCapability","useCapability","CommandsPlugin","id","KeyboardShortcuts","provides","commands","useEffect","handleKeyDown","event","target","composedPath","tagName","isContentEditable","key","shortcut","modifiers","ctrlKey","push","shiftKey","altKey","metaKey","toLowerCase","includes","sort","join","buildShortcutString","command","getCommandByShortcut","resolved","resolve","disabled","visible","preventDefault","stopPropagation","execute","createKeyDownHandler","document","addEventListener","removeEventListener","CommandsPluginPackage","createPluginPackage","BaseCommandsPackage","addUtility","build","commandId","documentId","setCommand","useState","onCommandStateChanged","usePlugin"],"mappings":"8MAIaA,EAAwB,IAAMC,gBAA8BC,EAAAA,eAAeC,ICKjF,SAASC,IACd,MAAQC,SAAUC,GAAaN,IAY/B,OAVAO,EAAAA,UAAU,KACR,IAAKD,EAAU,OAEf,MAAME,ECeH,SAA8BF,GACnC,OAAQG,IAEN,MACMC,EADeD,EAAME,eACE,IAAMF,EAAMC,OAIzC,IAAuB,UAAnBA,EAAOE,SAA0C,aAAnBF,EAAOE,SAA0BF,EAAOG,oBACtD,QAAdJ,EAAMK,IAAe,OAG3B,MAAMC,EApCH,SAA6BN,GAClC,MAAMO,EAAsB,GAExBP,EAAMQ,SAASD,EAAUE,KAAK,QAC9BT,EAAMU,UAAUH,EAAUE,KAAK,SAC/BT,EAAMW,QAAQJ,EAAUE,KAAK,OAC7BT,EAAMY,SAASL,EAAUE,KAAK,QAGlC,IAAIJ,EAAML,EAAMK,IAAIQ,cAIpB,MAHY,MAARR,IAAaA,EAAM,SACJ,CAAC,UAAW,QAAS,MAAO,QAAQS,SAAST,GAGvD,KAGK,IAAIE,EAAWF,GAChBU,OAAOC,KAAK,IAC3B,CAiBqBC,CAAoBjB,GACrC,IAAKM,EAAU,OAEf,MAAMY,EAAUrB,EAASsB,qBAAqBb,GAC9C,IAAKY,EAAS,OAGd,MAAME,EAAWvB,EAASwB,QAAQH,EAAQxB,KAEtC0B,EAASE,UAAaF,EAASG,UAKnCvB,EAAMwB,iBACNxB,EAAMyB,kBACN5B,EAAS6B,QAAQR,EAAQxB,QAAI,EAAW,aAE5C,CD7C0BiC,CAAqB9B,GAG3C,OADA+B,SAASC,iBAAiB,UAAW9B,GAC9B,IAAM6B,SAASE,oBAAoB,UAAW/B,IACpD,CAACF,IAGG,IACT,CEdO,MAAMkC,EAAwBC,EAAAA,oBAAoBC,EAAAA,uBACtDC,WAAWvC,GACXwC,uFHGuB,CAACC,EAAmBC,KAC5C,MAAMzC,SAAEA,GAAaL,KACd2B,EAASoB,GAAcC,EAAAA,SAAiC,IAC7D3C,EAAWA,EAASyB,QAAQe,EAAWC,GAAc,MAsBvD,OAnBAvC,EAAAA,UAAU,KACR,IAAKF,EAEH,YADA0C,EAAW,MAKbA,EAAW1C,EAASyB,QAAQe,EAAWC,IASvC,OANoBzC,EAAS4C,sBAAuBxC,IAC9CA,EAAMoC,YAAcA,GAAapC,EAAMqC,aAAeA,GACxDC,EAAW1C,EAASyB,QAAQe,EAAWC,OAK1C,CAACzC,EAAUwC,EAAWC,IAElBnB,8BAM0BmB,IACjC,MAAMzC,SAAEA,GAAaL,IAErB,OAAQ6C,IACFxC,GACFA,EAAS8B,QAAQU,EAAWC,EAAY,kEA7Cb,IAAMI,YAA0BhD,EAAAA,eAAeC"}
|
package/dist/react/index.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from '../shared-react';
|
package/dist/react/index.js
DELETED
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
import { createPluginPackage } from "@embedpdf/core";
|
|
2
|
-
import { CommandsPlugin, CommandsPluginPackage as CommandsPluginPackage$1 } from "@embedpdf/plugin-commands";
|
|
3
|
-
export * from "@embedpdf/plugin-commands";
|
|
4
|
-
import { useState, useEffect } from "react";
|
|
5
|
-
import { useCapability, usePlugin } from "@embedpdf/core/react";
|
|
6
|
-
const useCommandsCapability = () => useCapability(CommandsPlugin.id);
|
|
7
|
-
const useCommandsPlugin = () => usePlugin(CommandsPlugin.id);
|
|
8
|
-
const useCommand = (commandId, documentId) => {
|
|
9
|
-
const { provides } = useCommandsCapability();
|
|
10
|
-
const [command, setCommand] = useState(
|
|
11
|
-
() => provides ? provides.resolve(commandId, documentId) : null
|
|
12
|
-
);
|
|
13
|
-
useEffect(() => {
|
|
14
|
-
if (!provides) {
|
|
15
|
-
setCommand(null);
|
|
16
|
-
return;
|
|
17
|
-
}
|
|
18
|
-
setCommand(provides.resolve(commandId, documentId));
|
|
19
|
-
const unsubscribe = provides.onCommandStateChanged((event) => {
|
|
20
|
-
if (event.commandId === commandId && event.documentId === documentId) {
|
|
21
|
-
setCommand(provides.resolve(commandId, documentId));
|
|
22
|
-
}
|
|
23
|
-
});
|
|
24
|
-
return unsubscribe;
|
|
25
|
-
}, [provides, commandId, documentId]);
|
|
26
|
-
return command;
|
|
27
|
-
};
|
|
28
|
-
const useCommandExecutor = (documentId) => {
|
|
29
|
-
const { provides } = useCommandsCapability();
|
|
30
|
-
return (commandId) => {
|
|
31
|
-
if (provides) {
|
|
32
|
-
provides.execute(commandId, documentId, "ui");
|
|
33
|
-
}
|
|
34
|
-
};
|
|
35
|
-
};
|
|
36
|
-
function buildShortcutString(event) {
|
|
37
|
-
const modifiers = [];
|
|
38
|
-
if (event.ctrlKey) modifiers.push("ctrl");
|
|
39
|
-
if (event.shiftKey) modifiers.push("shift");
|
|
40
|
-
if (event.altKey) modifiers.push("alt");
|
|
41
|
-
if (event.metaKey) modifiers.push("meta");
|
|
42
|
-
let key = event.key.toLowerCase();
|
|
43
|
-
if (key === " ") key = "space";
|
|
44
|
-
const isModifier = ["control", "shift", "alt", "meta"].includes(key);
|
|
45
|
-
if (isModifier) {
|
|
46
|
-
return null;
|
|
47
|
-
}
|
|
48
|
-
const parts = [...modifiers, key];
|
|
49
|
-
return parts.sort().join("+");
|
|
50
|
-
}
|
|
51
|
-
function createKeyDownHandler(commands) {
|
|
52
|
-
return (event) => {
|
|
53
|
-
const composedPath = event.composedPath();
|
|
54
|
-
const target = composedPath[0] || event.target;
|
|
55
|
-
if (target.tagName === "INPUT" || target.tagName === "TEXTAREA" || target.isContentEditable) {
|
|
56
|
-
if (event.key !== "Tab") return;
|
|
57
|
-
}
|
|
58
|
-
const shortcut = buildShortcutString(event);
|
|
59
|
-
if (!shortcut) return;
|
|
60
|
-
const command = commands.getCommandByShortcut(shortcut);
|
|
61
|
-
if (!command) return;
|
|
62
|
-
const resolved = commands.resolve(command.id);
|
|
63
|
-
if (resolved.disabled || !resolved.visible) {
|
|
64
|
-
return;
|
|
65
|
-
}
|
|
66
|
-
event.preventDefault();
|
|
67
|
-
event.stopPropagation();
|
|
68
|
-
commands.execute(command.id, void 0, "keyboard");
|
|
69
|
-
};
|
|
70
|
-
}
|
|
71
|
-
function KeyboardShortcuts() {
|
|
72
|
-
const { provides: commands } = useCommandsCapability();
|
|
73
|
-
useEffect(() => {
|
|
74
|
-
if (!commands) return;
|
|
75
|
-
const handleKeyDown = createKeyDownHandler(commands);
|
|
76
|
-
document.addEventListener("keydown", handleKeyDown);
|
|
77
|
-
return () => document.removeEventListener("keydown", handleKeyDown);
|
|
78
|
-
}, [commands]);
|
|
79
|
-
return null;
|
|
80
|
-
}
|
|
81
|
-
const CommandsPluginPackage = createPluginPackage(CommandsPluginPackage$1).addUtility(KeyboardShortcuts).build();
|
|
82
|
-
export {
|
|
83
|
-
CommandsPluginPackage,
|
|
84
|
-
KeyboardShortcuts,
|
|
85
|
-
useCommand,
|
|
86
|
-
useCommandExecutor,
|
|
87
|
-
useCommandsCapability,
|
|
88
|
-
useCommandsPlugin
|
|
89
|
-
};
|
|
90
|
-
//# sourceMappingURL=index.js.map
|
package/dist/react/index.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../../src/shared/hooks/use-commands.ts","../../src/shared/utils/keyboard-handler.ts","../../src/shared/components/keyboard-shortcuts.tsx","../../src/shared/index.ts"],"sourcesContent":["import { useCapability, usePlugin } from '@embedpdf/core/@framework';\nimport { CommandsPlugin, ResolvedCommand } from '@embedpdf/plugin-commands';\nimport { useState, useEffect } from '@framework';\n\nexport const useCommandsCapability = () => useCapability<CommandsPlugin>(CommandsPlugin.id);\nexport const useCommandsPlugin = () => usePlugin<CommandsPlugin>(CommandsPlugin.id);\n\n/**\n * Hook to get a reactive command for a specific document\n * Automatically updates when command state changes\n * @param commandId Command ID\n * @param documentId Document ID\n * @returns ResolvedCommand or null if not available\n */\nexport const useCommand = (commandId: string, documentId: string): ResolvedCommand | null => {\n const { provides } = useCommandsCapability();\n const [command, setCommand] = useState<ResolvedCommand | null>(() =>\n provides ? provides.resolve(commandId, documentId) : null,\n );\n\n useEffect(() => {\n if (!provides) {\n setCommand(null);\n return;\n }\n\n // Initial resolve\n setCommand(provides.resolve(commandId, documentId));\n\n // Subscribe to state changes for this command + document\n const unsubscribe = provides.onCommandStateChanged((event) => {\n if (event.commandId === commandId && event.documentId === documentId) {\n setCommand(provides.resolve(commandId, documentId));\n }\n });\n\n return unsubscribe;\n }, [provides, commandId, documentId]);\n\n return command;\n};\n\n/**\n * Hook to execute a command\n */\nexport const useCommandExecutor = (documentId: string) => {\n const { provides } = useCommandsCapability();\n\n return (commandId: string) => {\n if (provides) {\n provides.execute(commandId, documentId, 'ui');\n }\n };\n};\n","import { CommandsCapability } from '../../lib/types';\n\n/**\n * Build a shortcut string from a keyboard event\n * @example Ctrl+Shift+A -> \"ctrl+shift+a\"\n */\nexport function buildShortcutString(event: KeyboardEvent): string | null {\n const modifiers: string[] = [];\n\n if (event.ctrlKey) modifiers.push('ctrl');\n if (event.shiftKey) modifiers.push('shift');\n if (event.altKey) modifiers.push('alt');\n if (event.metaKey) modifiers.push('meta');\n\n // Only add non-modifier keys\n let key = event.key.toLowerCase();\n if (key === ' ') key = 'space';\n const isModifier = ['control', 'shift', 'alt', 'meta'].includes(key);\n\n if (isModifier) {\n return null; // Just a modifier, no command\n }\n\n const parts = [...modifiers, key];\n return parts.sort().join('+');\n}\n\n/**\n * Handle keyboard events and execute commands based on shortcuts\n */\nexport function createKeyDownHandler(commands: CommandsCapability) {\n return (event: KeyboardEvent) => {\n // Use composedPath to get the actual target element, even inside Shadow DOM\n const composedPath = event.composedPath();\n const target = (composedPath[0] || event.target) as HTMLElement;\n\n // Don't handle shortcuts if target is an input, textarea, or contentEditable\n // Exception: allow Tab/Shift+Tab through for form field navigation\n if (target.tagName === 'INPUT' || target.tagName === 'TEXTAREA' || target.isContentEditable) {\n if (event.key !== 'Tab') return;\n }\n\n const shortcut = buildShortcutString(event);\n if (!shortcut) return;\n\n const command = commands.getCommandByShortcut(shortcut);\n if (!command) return;\n\n // Resolve without document ID - will use active document\n const resolved = commands.resolve(command.id);\n\n if (resolved.disabled || !resolved.visible) {\n return;\n }\n\n // Execute and prevent default (documentId is optional now)\n event.preventDefault();\n event.stopPropagation();\n commands.execute(command.id, undefined, 'keyboard');\n };\n}\n","import { useEffect } from '@framework';\nimport { useCommandsCapability } from '../hooks';\nimport { createKeyDownHandler } from '../utils';\n\n/**\n * Utility component that listens to keyboard events\n * and executes commands based on shortcuts.\n * This component doesn't render anything, it just sets up keyboard shortcuts.\n */\nexport function KeyboardShortcuts() {\n const { provides: commands } = useCommandsCapability();\n\n useEffect(() => {\n if (!commands) return;\n\n const handleKeyDown = createKeyDownHandler(commands);\n\n document.addEventListener('keydown', handleKeyDown);\n return () => document.removeEventListener('keydown', handleKeyDown);\n }, [commands]);\n\n // This component is only used to set up keyboard shortcuts when the plugin is initialized.\n return null;\n}\n","import { createPluginPackage } from '@embedpdf/core';\nimport { CommandsPluginPackage as BaseCommandsPackage } from '@embedpdf/plugin-commands';\n\nimport { KeyboardShortcuts } from './components';\n\nexport * from './hooks';\nexport * from './components';\nexport * from '@embedpdf/plugin-commands';\n\nexport const CommandsPluginPackage = createPluginPackage(BaseCommandsPackage)\n .addUtility(KeyboardShortcuts)\n .build();\n"],"names":["BaseCommandsPackage"],"mappings":";;;;;AAIO,MAAM,wBAAwB,MAAM,cAA8B,eAAe,EAAE;AACnF,MAAM,oBAAoB,MAAM,UAA0B,eAAe,EAAE;AAS3E,MAAM,aAAa,CAAC,WAAmB,eAA+C;AAC3F,QAAM,EAAE,SAAA,IAAa,sBAAA;AACrB,QAAM,CAAC,SAAS,UAAU,IAAI;AAAA,IAAiC,MAC7D,WAAW,SAAS,QAAQ,WAAW,UAAU,IAAI;AAAA,EAAA;AAGvD,YAAU,MAAM;AACd,QAAI,CAAC,UAAU;AACb,iBAAW,IAAI;AACf;AAAA,IACF;AAGA,eAAW,SAAS,QAAQ,WAAW,UAAU,CAAC;AAGlD,UAAM,cAAc,SAAS,sBAAsB,CAAC,UAAU;AAC5D,UAAI,MAAM,cAAc,aAAa,MAAM,eAAe,YAAY;AACpE,mBAAW,SAAS,QAAQ,WAAW,UAAU,CAAC;AAAA,MACpD;AAAA,IACF,CAAC;AAED,WAAO;AAAA,EACT,GAAG,CAAC,UAAU,WAAW,UAAU,CAAC;AAEpC,SAAO;AACT;AAKO,MAAM,qBAAqB,CAAC,eAAuB;AACxD,QAAM,EAAE,SAAA,IAAa,sBAAA;AAErB,SAAO,CAAC,cAAsB;AAC5B,QAAI,UAAU;AACZ,eAAS,QAAQ,WAAW,YAAY,IAAI;AAAA,IAC9C;AAAA,EACF;AACF;AC/CO,SAAS,oBAAoB,OAAqC;AACvE,QAAM,YAAsB,CAAA;AAE5B,MAAI,MAAM,QAAS,WAAU,KAAK,MAAM;AACxC,MAAI,MAAM,SAAU,WAAU,KAAK,OAAO;AAC1C,MAAI,MAAM,OAAQ,WAAU,KAAK,KAAK;AACtC,MAAI,MAAM,QAAS,WAAU,KAAK,MAAM;AAGxC,MAAI,MAAM,MAAM,IAAI,YAAA;AACpB,MAAI,QAAQ,IAAK,OAAM;AACvB,QAAM,aAAa,CAAC,WAAW,SAAS,OAAO,MAAM,EAAE,SAAS,GAAG;AAEnE,MAAI,YAAY;AACd,WAAO;AAAA,EACT;AAEA,QAAM,QAAQ,CAAC,GAAG,WAAW,GAAG;AAChC,SAAO,MAAM,OAAO,KAAK,GAAG;AAC9B;AAKO,SAAS,qBAAqB,UAA8B;AACjE,SAAO,CAAC,UAAyB;AAE/B,UAAM,eAAe,MAAM,aAAA;AAC3B,UAAM,SAAU,aAAa,CAAC,KAAK,MAAM;AAIzC,QAAI,OAAO,YAAY,WAAW,OAAO,YAAY,cAAc,OAAO,mBAAmB;AAC3F,UAAI,MAAM,QAAQ,MAAO;AAAA,IAC3B;AAEA,UAAM,WAAW,oBAAoB,KAAK;AAC1C,QAAI,CAAC,SAAU;AAEf,UAAM,UAAU,SAAS,qBAAqB,QAAQ;AACtD,QAAI,CAAC,QAAS;AAGd,UAAM,WAAW,SAAS,QAAQ,QAAQ,EAAE;AAE5C,QAAI,SAAS,YAAY,CAAC,SAAS,SAAS;AAC1C;AAAA,IACF;AAGA,UAAM,eAAA;AACN,UAAM,gBAAA;AACN,aAAS,QAAQ,QAAQ,IAAI,QAAW,UAAU;AAAA,EACpD;AACF;ACnDO,SAAS,oBAAoB;AAClC,QAAM,EAAE,UAAU,SAAA,IAAa,sBAAA;AAE/B,YAAU,MAAM;AACd,QAAI,CAAC,SAAU;AAEf,UAAM,gBAAgB,qBAAqB,QAAQ;AAEnD,aAAS,iBAAiB,WAAW,aAAa;AAClD,WAAO,MAAM,SAAS,oBAAoB,WAAW,aAAa;AAAA,EACpE,GAAG,CAAC,QAAQ,CAAC;AAGb,SAAO;AACT;ACdO,MAAM,wBAAwB,oBAAoBA,uBAAmB,EACzE,WAAW,iBAAiB,EAC5B,MAAA;"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { KeyboardShortcuts } from './keyboard-shortcuts';
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from './use-commands';
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import { CommandsPlugin, ResolvedCommand } from '../../index.ts';
|
|
2
|
-
export declare const useCommandsCapability: () => {
|
|
3
|
-
provides: Readonly<import('../../index.ts').CommandsCapability> | null;
|
|
4
|
-
isLoading: boolean;
|
|
5
|
-
ready: Promise<void>;
|
|
6
|
-
};
|
|
7
|
-
export declare const useCommandsPlugin: () => {
|
|
8
|
-
plugin: CommandsPlugin | null;
|
|
9
|
-
isLoading: boolean;
|
|
10
|
-
ready: Promise<void>;
|
|
11
|
-
};
|
|
12
|
-
/**
|
|
13
|
-
* Hook to get a reactive command for a specific document
|
|
14
|
-
* Automatically updates when command state changes
|
|
15
|
-
* @param commandId Command ID
|
|
16
|
-
* @param documentId Document ID
|
|
17
|
-
* @returns ResolvedCommand or null if not available
|
|
18
|
-
*/
|
|
19
|
-
export declare const useCommand: (commandId: string, documentId: string) => ResolvedCommand | null;
|
|
20
|
-
/**
|
|
21
|
-
* Hook to execute a command
|
|
22
|
-
*/
|
|
23
|
-
export declare const useCommandExecutor: (documentId: string) => (commandId: string) => void;
|
package/dist/shared/index.d.ts
DELETED
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
export * from './hooks';
|
|
2
|
-
export * from './components';
|
|
3
|
-
export * from '../index.ts';
|
|
4
|
-
export declare const CommandsPluginPackage: import('@embedpdf/core').WithAutoMount<import('@embedpdf/core').PluginPackage<import('../index.ts').CommandsPlugin, import('../index.ts').CommandsPluginConfig, import('../index.ts').CommandsState, import('../lib/actions').SetDisabledCategoriesAction>>;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from './keyboard-handler';
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { CommandsCapability } from '../../lib/types';
|
|
2
|
-
/**
|
|
3
|
-
* Build a shortcut string from a keyboard event
|
|
4
|
-
* @example Ctrl+Shift+A -> "ctrl+shift+a"
|
|
5
|
-
*/
|
|
6
|
-
export declare function buildShortcutString(event: KeyboardEvent): string | null;
|
|
7
|
-
/**
|
|
8
|
-
* Handle keyboard events and execute commands based on shortcuts
|
|
9
|
-
*/
|
|
10
|
-
export declare function createKeyDownHandler(commands: CommandsCapability): (event: KeyboardEvent) => void;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { KeyboardShortcuts } from './keyboard-shortcuts';
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from './use-commands';
|