Collapse the duplicated Digit/Numpad opacity bindings into a single
OPACITY_CODES array with one shared run handler. Digit value is
extracted via code.slice(-1), which works for both Digit0-9 and
Numpad0-9.
Figma treats pressing 0 alone as 100% opacity, not 0%. The single-digit
decena rule (n * 10) would produce 0 * 10 = 0% for 0, which is wrong.
Add an explicit check: buffer '0' applies 100%, all other single
digits apply n * 10, and multi-digit buffers apply the literal value.
Replace the 400ms apply-timeout with immediate application on every
digit press. A separate 800ms reset timer clears the buffer between
sequences so digits typed in quick succession still compose (28→28%,
00→0%, 100→100%) but single digits no longer have a perceptible delay.
- Move setOpacity to core editor action (packages/core/src/editor/nodes.ts)
so it is testable directly without duplicating logic in tests
- Replace external window listener with tinykeys bindings in
registerKeyboardShortcuts, reusing shared input/focus guards
- Add 20 bindings for Digit0-9 and Numpad0-9 with modifier guards
- Implement Figma-style multi-digit buffer: 2→20%, 28→28%, 00→0%, 100→100%
with 400ms timeout and auto-apply at 3 digits
- Simplify selection.setOpacity command to delegate to editor.setOpacity
- Rewrite unit tests to call editor.setOpacity directly
- Add ofetch to dependencies (was imported but missing from package.json)
- Update CHANGELOG
Add opacity keyboard shortcuts matching Figma: pressing Digit1-Digit9
sets the selected layers to 10%-90% opacity, and Digit0 sets 100%.
- Add 'selection.setOpacity' EditorCommandId with registry metadata
(shortcut display '1-9, 0') so it appears in the command cheatsheet
- Implement the command in selection.ts using editor.undo.runBatch +
updateNodeWithUndo, following the toggleVisibility/setBlendMode pattern
- Add setOpacityTarget closure in useEditorCommands so the external
keyboard handler can inject the digit value before runCommand
- Add canSetOpacity capability (enabled when there is a selection)
- Add bindOpacityKeys handler in src/app/shell/keyboard/opacity.ts,
following the nudging.ts external-handler pattern since the opacity
value comes from event.code and cannot be expressed as a fixed
keybinding in the registry
- Guard against text editing, number field focus, and modifier keys
- Add i18n label 'setOpacity' across all 8 locales
- Add unit tests for single/multi selection, undo batch, and no-op
- Share compact item-row and paint-field anatomy across Fill, Stroke, Effects, and Export
- Add COLOR binding transactions with picker rollback and one-step undo
- Replace bespoke panel test hooks with semantic selectors and visual coverage
- Remove ScrubInput aliases, app wrappers, test hooks, and translated legacy references
- Share a Tailwind NumberField demo between Storybook and VitePress
- Generate component API tables from Vue metadata and type-check examples with Twoslash
- Upgrade the docs stack to VitePress 2
- Add selection mask commands, shortcuts, context-menu access, and mask type controls
- Add mixed-selection blend mode editing with single-step undo
- Document the headless mask composable and localize the new controls
* i18n: more menu translations
* chore: code formatting
* chore: code formatting
* chore: code formatting
* i18n: more menu translations
* chore: code formatting
* i18n: added new menu translations and japanese language
* i18n: added theme-auto string
* i18n: translated rest of the menu text
* fix(i18n): polish PR 273 locale updates
- Fix obvious translation regressions in existing locales
- Preserve update size interpolation across localized update messages
- Use standard UI terms for theme and boolean operation labels
* fix(i18n): tune localized toggle labels
- Use paired action wording for toggle commands
- Clarify Japanese frame-selection command label
---------
Co-authored-by: Santos Alarcón <santosalarcon86@gmail.com>
- Add shared DOM event helpers to Vue SDK (inputValue, inputNumberValue,
blurTarget, selectTarget) in packages/vue/src/shared/dom-events.ts
- Export from @open-pencil/vue for app consumption
- Replace all template `as HTMLInputElement` casts with helper calls:
ColorPickerPanel fields, PickerSlider, GradientEditor, ColorInput,
CollabPanel, AppMenu, ScrubInputRoot
- Fix useInlineRename to use instanceof guards instead of casts
- Fix document name rename to accept Event directly
- Rename window-api to browser-bridge to describe the app-to-browser test hook boundary
- Rename the chat transport hook to exposeChatTransportOverride and window.openPencil.setChatTransport
- Route browser globals through src/app/window-api.ts instead of private __OPEN_PENCIL fields
- Move E2E tests to the public window.openPencil test API
- Add an oxlint rule banning direct window.__OPEN_PENCIL* access
- Configure oxfmt custom import groups for workspace, app, package, and test aliases
- Keep type imports grouped with their matching source category instead of one global tail group
- Expand the format script to cover formatter config, Vite files, and scripts
- Add a nullable active editor store accessor for optional app-bound updates
- Skip canvas theme repainting until an editor store exists
- Guard FontPicker focus handling against non-element component refs
- Replace one-off class props with typed ui objects
- Group renderless slot actions and remove DOM ref callback plumbing
- Make local font access and typography font loading explicit capability objects
- Deduplicate plugin data and relaunch data during .fig parsing/export
- Unify shared plugin data into the pluginData storage path
- Refactor shadow rendering and harden CanvasKit font provider teardown
- Expand engine and renderer coverage